<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Bootlabs Blog]]></title><description><![CDATA[Bootlabs Blog]]></description><link>https://blog.bootlabstech.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1651058646644/UN_vTOAbg.png</url><title>Bootlabs Blog</title><link>https://blog.bootlabstech.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 13:24:12 GMT</lastBuildDate><atom:link href="https://blog.bootlabstech.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Security Driven Development(SDD)]]></title><description><![CDATA[Introduction
In today's rapidly evolving digital landscape, cybersecurity threats pose a constant challenge to organizations. As the complexity of software applications increases, so does the need for robust security measures. Security-Driven Develop...]]></description><link>https://blog.bootlabstech.com/security-driven-developmentsdd</link><guid isPermaLink="true">https://blog.bootlabstech.com/security-driven-developmentsdd</guid><category><![CDATA[Security Driven Development]]></category><category><![CDATA[SDD]]></category><category><![CDATA[Shift Left Approach]]></category><category><![CDATA[unmasking vulnerabilities]]></category><category><![CDATA[SDLC]]></category><category><![CDATA[DevSecOps]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Wed, 29 Nov 2023 10:29:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cckf4TsHAuw/upload/ee9fcf034a0c5a8ab70772bef66814a8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction"><strong>Introduction</strong></h3>
<p>In today's rapidly evolving digital landscape, cybersecurity threats pose a constant challenge to organizations. As the complexity of software applications increases, so does the need for robust security measures. Security-Driven Development (SDD), often associated with the concept of the "Shift Left Approach," emerges as a proactive approach to embed security throughout the software development lifecycle (SDLC). This paradigm shift not only strengthens the resilience of applications but also fosters a security-aware culture within development teams.</p>
<p><img src="https://lh7-us.googleusercontent.com/fsn6vhC9ynR33N0okUX503eKsDmZ3x9GH7vHyhsVhcIQI8AH-fy5EQbkx5DYCiTC0TtMcBu2Ku46kIcMX-qYEvPG_vzhzQG9Tb4V2C98T8wIciOZFTzXWfNrb0VVL-s7hAO7on8TsaZdfP6YWBh2tlM" alt class="image--center mx-auto" /></p>
<h3 id="heading-what-is-devsecops-and-why-does-it-matter"><strong>What is DevSecOps? And Why Does It Matter?</strong></h3>
<p>DevSecOps is an extension of DevOps that integrates security seamlessly into the development and operations processes. It emphasizes collaboration, automation, and shared responsibility. In DevSecOps, security is not a standalone phase but a continuous and integrated part of the software delivery pipeline as shown below,</p>
<p><img src="https://lh7-us.googleusercontent.com/hVLzvHYNRlGZIGxaifNZKO7L8dJWtVlNFC2bx9l3K3NkGrF5PEBmepbBkBybtdmvgSeAm_CmHkiKRR_b7fCFaabHMqDnwUq6XmT-8UdkI8IowTEH_XGIcR0iDltkIyish880o6891nDy6qB0odb2H6U" alt /></p>
<h3 id="heading-the-cultural-shift-of-devsecops"><strong>The Cultural Shift of DevSecOps</strong></h3>
<p>Implementing DevSecOps requires a cultural shift within organizations. Developers, operations, and security teams must collaborate closely, breaking down traditional silos. This collaborative culture ensures that security is not an afterthought but an integral consideration from the project's inception.</p>
<p><img src="https://lh7-us.googleusercontent.com/HQILp0N0aOVBLAWTdbCJN8l73xMFoG3fyZFIXVeGcIiw1D8PwsK6ULssEcAAdrK0J_5h1k9lm2aLlNi3qAPsNSGGXh5yukZj6jtjiKFcDh9O3rMs8CGRhsoW4dk-vuo3uHSWCvCC7sZOUeIlxsxCFkA" alt /></p>
<p><strong>Automation and Continuous Monitoring</strong></p>
<p>Automation is a cornerstone of DevSecOps. Automated security checks are integrated into the Continuous Integration (CI) and Continuous Deployment (CD) pipelines, allowing for real-time identification of vulnerabilities. Continuous monitoring ensures that security is an ongoing concern, not just a point-in-time activity.</p>
<p><strong>Shift Left (Moving Security to the Left): The Earlier, the Better</strong></p>
<p>The concept of moving security to the left involves addressing security concerns as early as possible in the SDLC. By doing so, organizations can identify and remediate security issues before they become costly and time-consuming problems later in the development process.</p>
<p><strong>Collaboration Across Teams: A Shared Responsibility</strong></p>
<p>DevSecOps encourages collaboration between traditionally separate teams. Security champions within development teams help bridge the gap, ensuring that security considerations are understood and implemented by everyone involved.</p>
<p><strong>Threat Modeling: Anticipating the Threats</strong></p>
<p>Threat modeling, conducted early in the development process, helps identify potential security threats and vulnerabilities. By understanding the system's architecture and potential attack vectors, development teams can make informed decisions about security controls.</p>
<h3 id="heading-advanced-scanning-techniques-unmasking-vulnerabilities"><strong>Advanced Scanning Techniques: Unmasking Vulnerabilities</strong></h3>
<p><strong>Static Application Security Testing (SAST):</strong> SAST analyzes the source code for potential vulnerabilities. By scanning the code early in development, SAST helps identify and fix security issues at the code level.</p>
<p><strong>Dynamic Application Security Testing (DAST):</strong> DAST complements SAST by testing running applications for vulnerabilities. It provides a more realistic view of potential threats in a live environment.</p>
<p><strong>Software Composition Analysis (SCA):</strong> SCA focuses on third-party dependencies, scanning for known vulnerabilities. As applications increasingly rely on external libraries, SCA helps mitigate risks associated with such dependencies.</p>
<p><strong>Container Security: Shielding the Containers</strong></p>
<p>Containerization offers flexibility and scalability, but it also introduces unique security challenges. Container scanning identifies vulnerabilities within container images, ensuring secure deployment.</p>
<p><strong>Infrastructure as Code (IaC) Scanning: Securing the Foundation</strong></p>
<p>IaC scanning assesses security configurations within infrastructure code templates. By identifying misconfigurations early, organizations can prevent security gaps in the deployed infrastructure.</p>
<h3 id="heading-elevating-sdlc-to-a-higher-level"><strong>Elevating SDLC to a Higher Level</strong></h3>
<p><strong>Proactive Issue Remediation: Preventing, Not Reacting</strong></p>
<p>Addressing security issues early in the SDLC allows for proactive remediation. This proactive approach not only reduces the risk of security incidents but also saves costs associated with fixing vulnerabilities later in the development process.</p>
<p><strong>Continuous Improvement: An Ongoing Journey</strong></p>
<p>DevSecOps is an iterative approach. Continuous learning from incidents and feedback loops enables teams to improve security processes continually. This emphasis on continuous improvement ensures that security remains effective in addressing emerging threats.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Security Driven Development(SDD), combined with the principles of DevSecOps and advanced scanning techniques, offers a holistic and proactive approach to software security. By integrating security practices early in the SDLC, organizations can build more resilient applications, reduce costs associated with post-deployment fixes, and foster a culture where security is a shared responsibility. Embracing DevSecOps and leveraging tools like SAST, DAST, SCA, container scanning, and IaC scanning can elevate the entire SDLC to a new level of security and reliability. As we navigate the ever-evolving threat landscape, the collaboration between development, operations, and security becomes not just a best practice but a necessity for building secure software in the digital age.</p>
<p><strong>Author:</strong> <a target="_blank" href="https://www.linkedin.com/in/vaibhav-rathod02?lipi=urn%3Ali%3Apage%3Ad_flagship3_profile_view_base_contact_details%3Bj%2BwKBNZ7RsWm593G9Pt8Vg%3D%3D"><strong>Vaibhav Rathod</strong></a></p>
<p><strong>Associate DevOps Engineer</strong></p>
<p><a target="_blank" href="https://www.bootlabstech.com/"><strong>BootLabs Technologies Pvt Ltd</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Gatekeeper vs Kyverno]]></title><description><![CDATA[Gatekeeper is a general-purpose policy engine based on Open Policy Agent (OPA). It allows you to define and enforce custom policies across various Kubernetes resources.
  Kyverno is specifically designed for Kubernetes policy management. It focuses o...]]></description><link>https://blog.bootlabstech.com/gatekeeper-vs-kyverno</link><guid isPermaLink="true">https://blog.bootlabstech.com/gatekeeper-vs-kyverno</guid><category><![CDATA[gatekeeper]]></category><category><![CDATA[kyverno]]></category><category><![CDATA[openpolicyagent]]></category><category><![CDATA[Security]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Fri, 21 Jul 2023 17:34:31 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689935553249/afaf2098-73b8-4cf0-b152-0bfbf00c5a8d.jpeg" alt class="image--center mx-auto" /></p>
<p>Gatekeeper is a <strong>general-purpose policy engine</strong> based on Open Policy Agent (OPA). It allows you to define and enforce custom policies across various Kubernetes resources.</p>
<p>  Kyverno is specifically <strong>designed for Kubernetes</strong> policy management. It focuses on <strong>validating and mutating resources</strong> in Kubernetes to enforce desired configurations and security policies.</p>
<p>Small Comparison of both tools that gives you a clear picture,</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Gatekeeper</td><td>Kyverno</td></tr>
</thead>
<tbody>
<tr>
<td>It acts as an admission controller which means it evaluates policies during the admission of resources</td><td>It is built using Custom Resource Definitions (CRDs) and operates as a validating and mutating webhook.</td></tr>
<tr>
<td>It uses the <strong>powerful Rego language</strong>, part of the OPA project, for defining policies</td><td>It uses a YAML or JSON-based <strong>declarative policy language</strong> that is easier to read and write for many users</td></tr>
<tr>
<td>Users should have <strong>prior knowledge</strong> to understand and write rego policies</td><td>Users are familiar with Kubernetes <strong>YAML or JSON</strong> which makes it easier to write policies</td></tr>
<tr>
<td>It does not provide built-in policies out of the box, but there are community-contributed policies available to get started</td><td>Kyverno comes with a set of <strong>built-in policies</strong> and examples that cover common use cases</td></tr>
<tr>
<td>It is more extensible and can be used to enforce policy beyond Kubernetes since it’s <strong>general-purpose</strong> nature</td><td>Its <strong>primary focus is Kubernetes</strong>, making it highly optimised for handling Kubernetes resources and scenarios</td></tr>
</tbody>
</table>
</div><p> <strong>Key Points that you don’t miss!!</strong></p>
<ul>
<li><p>Gatekeeper is a general-purpose open policy agent whereas Kyverno is specifically designed for Kubernetes</p>
</li>
<li><p>Gatekeeper supports only validating webhooks but Kyverno supports validating, mutating, generating and image verification webhooks</p>
</li>
<li><p>Gatekeeper makes you feel complex for writing a simple policy but Kyverno is easier to implement</p>
</li>
<li><p>Some complex requirement to write custom policies in Kyverno is difficult where rego plays in the background of the OPA gatekeeper makes possible to solve those requirements</p>
</li>
<li><p>Kyverno is a <strong>straightforward method</strong> for the implementation of policies but in the case of gatekeeper we need to create the CRDs like <strong>templates</strong> and <strong>constraints</strong></p>
</li>
</ul>
<p><strong>Implementation of Gatekeeper</strong></p>
<ol>
<li><p><strong>Installation:</strong> Gatekeeper supports three methods of installation.</p>
<ol>
<li><p>Install using releases to deploy:</p>
<pre><code class="lang-bash"> kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
</code></pre>
</li>
<li><p>Deploy using Helm:</p>
<pre><code class="lang-bash"> helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
 helm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace
</code></pre>
</li>
<li><p>Deploy HEAD using make:</p>
<pre><code class="lang-bash"> git <span class="hljs-built_in">clone</span> https://github.com/open-policy-agent/gatekeeper.git
 <span class="hljs-built_in">export</span> DESTINATION_GATEKEEPER_IMAGE=&lt;add registry like <span class="hljs-string">"myregistry.docker.io/gatekeeper"</span>&gt;
 make docker-buildx REPOSITORY=<span class="hljs-variable">$DESTINATION_GATEKEEPER_DOCKER_IMAGE</span> OUTPUT_TYPE=<span class="hljs-built_in">type</span>=registry
 make deploy REPOSITORY=<span class="hljs-variable">$DESTINATION_GATEKEEPER_DOCKER_IMAGE</span>
</code></pre>
</li>
</ol>
</li>
<li><p>After successful installation, you can create templates and constraints to implement the policies. Here we are trying to restrict Deployment if the deployment is not having the label <strong>“app”</strong>. If the specified <strong>label</strong> is not present it should not be allowed to deploy.</p>
<ol>
<li><p>Create a template.yaml file and paste this YAML and apply the file</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">templates.gatekeeper.sh/v1beta1</span>
 <span class="hljs-attr">kind:</span> <span class="hljs-string">ConstraintTemplate</span>
 <span class="hljs-attr">metadata:</span>
   <span class="hljs-attr">name:</span> <span class="hljs-string">kubernetesvalidatinglabel</span>
 <span class="hljs-attr">spec:</span>
   <span class="hljs-attr">crd:</span>
     <span class="hljs-attr">spec:</span>
       <span class="hljs-attr">names:</span>
         <span class="hljs-attr">kind:</span> <span class="hljs-string">KubernetesValidatingLabel</span>
   <span class="hljs-attr">targets:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-attr">target:</span> <span class="hljs-string">admission.k8s.gatekeeper.sh</span>
       <span class="hljs-attr">rego:</span> <span class="hljs-string">|
         package kubernetes.validating.labels
         import future.keywords.contains
         import future.keywords.if
         import future.keywords.in
         violation[{"msg": msg, "details": {"missing_labels": missing}}] {
           provided := {label | input.review.object.metadata.labels[label]}
           required := {label | label := input.parameters.labels[_]}
           missing := required - provided
           count(missing) &gt; 0
           msg := sprintf("you must provide labels: %v", [missing])
         }</span>
</code></pre>
</li>
<li><p>Create a constraint.yaml file and paste this YAML and apply the file</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">constraints.gatekeeper.sh/v1beta1</span>
 <span class="hljs-attr">kind:</span> <span class="hljs-string">KubernetesValidatingLabel</span>
 <span class="hljs-attr">metadata:</span>
   <span class="hljs-attr">name:</span> <span class="hljs-string">require-deployment-labels</span>
 <span class="hljs-attr">spec:</span>
   <span class="hljs-attr">match:</span>
     <span class="hljs-attr">kinds:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroups:</span>
         <span class="hljs-attr">kinds:</span>
           <span class="hljs-bullet">-</span> <span class="hljs-string">Deployment</span>
   <span class="hljs-attr">parameters:</span>
     <span class="hljs-attr">labels:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-string">app</span>
</code></pre>
<p> After successfully applying the template and constraints we will try to <strong>create a deployment without the label app</strong> such that we can see the error message.</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
 <span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
 <span class="hljs-attr">metadata:</span>
   <span class="hljs-attr">name:</span> <span class="hljs-string">nginx-deployment</span>
   <span class="hljs-attr">labels:</span>
     <span class="hljs-attr">test:</span> <span class="hljs-string">prod</span>
 <span class="hljs-attr">spec:</span>
   <span class="hljs-attr">replicas:</span> <span class="hljs-number">2</span>
   <span class="hljs-attr">selector:</span>
     <span class="hljs-attr">matchLabels:</span>
       <span class="hljs-attr">test:</span> <span class="hljs-string">prod</span>
   <span class="hljs-attr">template:</span>
     <span class="hljs-attr">metadata:</span>
       <span class="hljs-attr">labels:</span>
         <span class="hljs-attr">test:</span> <span class="hljs-string">prod</span>
     <span class="hljs-attr">spec:</span>
       <span class="hljs-attr">containers:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nginx-container</span>
         <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
         <span class="hljs-attr">ports:</span>
         <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
</code></pre>
<p> While applying this YAML you will get a message like this  </p>
<pre><code class="lang-yaml"> <span class="hljs-string">Error</span> <span class="hljs-string">from</span> <span class="hljs-string">server</span> <span class="hljs-string">(Forbidden):</span> <span class="hljs-string">error</span> <span class="hljs-string">when</span> <span class="hljs-string">creating</span> <span class="hljs-attr">"deploy.yaml":</span> <span class="hljs-string">admission</span> <span class="hljs-string">webhook</span> <span class="hljs-string">"validation.gatekeeper.sh"</span> <span class="hljs-attr">denied the request:</span> [<span class="hljs-string">require-deployment-labels</span>]
 [<span class="hljs-string">require-deployment-labels</span>] <span class="hljs-attr">you must provide labels:</span> {<span class="hljs-string">"app"</span>}
</code></pre>
</li>
</ol>
</li>
</ol>
<p><strong>Implementation of Kyverno</strong></p>
<ol>
<li><p>Installation:</p>
<ol>
<li><p>Installation using Helm:</p>
<pre><code class="lang-bash"> helm repo add kyverno https://kyverno.github.io/kyverno/
 helm install kyverno-policies kyverno/kyverno-policies -n kyverno -- create-namespace
</code></pre>
</li>
<li><p>Install using YAMLs:</p>
<pre><code class="lang-bash"> kubectl create -f https://github.com/kyverno/kyverno/releases/download/v1.10.0/install.yaml
</code></pre>
</li>
<li><p>Testing unreleased code:</p>
<pre><code class="lang-bash"> kubectl create -f https://github.com/kyverno/kyverno/raw/main/config/install-latest-testing.yaml
</code></pre>
</li>
</ol>
</li>
<li><p>Here, you can directly create policies using the YAML files. We will create a cluster policy using Kyverno’s CRDs</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">kyverno.io/v1</span>
 <span class="hljs-comment"># The `ClusterPolicy` kind applies to the entire cluster.</span>
 <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterPolicy</span>
 <span class="hljs-attr">metadata:</span>
   <span class="hljs-attr">name:</span> <span class="hljs-string">require-ns-purpose-label</span>
 <span class="hljs-comment"># The `spec` defines properties of the policy.</span>
 <span class="hljs-attr">spec:</span>
   <span class="hljs-comment"># The `validationFailureAction` tells Kyverno if the resource being validated should be allowed but reported (`Audit`) or blocked (`Enforce`).</span>
   <span class="hljs-attr">validationFailureAction:</span> <span class="hljs-string">Enforce</span>
   <span class="hljs-comment"># The `rules` is one or more rules which must be true.</span>
   <span class="hljs-attr">rules:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">require-ns-purpose-label</span>
     <span class="hljs-comment"># The `match` statement sets the scope of what will be checked. In this case, it is any `Namespace` resource.</span>
     <span class="hljs-attr">match:</span>
       <span class="hljs-attr">any:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">resources:</span>
           <span class="hljs-attr">kinds:</span>
           <span class="hljs-bullet">-</span> <span class="hljs-string">Namespace</span>
     <span class="hljs-comment"># The `validate` statement tries to positively check what is defined. If the statement, when compared with the requested resource, is true, it is allowed. If false, it is blocked.</span>
     <span class="hljs-attr">validate:</span>
       <span class="hljs-comment"># The `message` is what gets displayed to a user if this rule fails validation.</span>
       <span class="hljs-attr">message:</span> <span class="hljs-string">"You must have label `purpose` with value `production` set on all new namespaces."</span>
       <span class="hljs-comment"># The `pattern` object defines what pattern will be checked in the resource. In this case, it is looking for `metadata.labels` with `purpose=production`.</span>
       <span class="hljs-attr">pattern:</span>
         <span class="hljs-attr">metadata:</span>
           <span class="hljs-attr">labels:</span>
             <span class="hljs-attr">app:</span> <span class="hljs-string">test</span>
</code></pre>
<p> We are using only one file to apply the policies rather than creating templates and constraints in the gatekeeper.</p>
<p> After applying this policy try to create the same deployment using the deployment file and you will get the error message like this.</p>
<pre><code class="lang-bash"> Error from server: error when creating <span class="hljs-string">"deploy.yaml"</span>: admission webhook <span class="hljs-string">"validate.kyverno.svc-fail"</span> denied the request: 
 resource Deployment/default/nginx-deployment was blocked due to the following policies 
 require-ns-purpose-label:
   require-ns-purpose-label: <span class="hljs-string">'validation error: You must have label `purpose` with
     value `production` set on all new namespaces. rule require-ns-purpose-label failed
     at path /metadata/labels/jk/'</span>
</code></pre>
<p> If you want to pass the deployment then you should <strong>add the label “app”</strong> in the metadata of your deployment file.</p>
</li>
</ol>
<p><strong>Summary</strong></p>
<p>I hope now you have a good understanding of Gatekeeper and Kyverno and how it’s working. </p>
<p>As of my understanding for <strong>simple and general usage</strong> of policies we can go with Kyverno if you need <strong>some complex security policies</strong> to be implemented in your k8s then you can use Gatekeeper. Both Gatekeeper and Kyverno have their pros and cons, you can use any one of the tools based on your requirement.</p>
<p><strong>Author:</strong> <a target="_blank" href="https://www.linkedin.com/in/jayakumar-s-bba536194">Jayakumar S</a></p>
]]></content:encoded></item><item><title><![CDATA[Connecting to AWS private EC2 instance using open ssh without a Bastion Host]]></title><description><![CDATA[Connecting to an AWS private EC2 instance using OpenSSH requires a few prerequisites and steps.
Prerequisites:

Ensure you have OpenSSH installed.

Make sure you have AWS CLI version 2.12.6 or above installed.

Set up an EC2 instance connect endpoint...]]></description><link>https://blog.bootlabstech.com/connecting-to-aws-private-ec2-instance-using-open-ssh-without-a-bastion-host</link><guid isPermaLink="true">https://blog.bootlabstech.com/connecting-to-aws-private-ec2-instance-using-open-ssh-without-a-bastion-host</guid><category><![CDATA[AWS]]></category><category><![CDATA[EC2 instance]]></category><category><![CDATA[Devops]]></category><category><![CDATA[open ssh]]></category><category><![CDATA[Bastion host alternatives]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Mon, 10 Jul 2023 07:48:33 GMT</pubDate><content:encoded><![CDATA[<p>Connecting to an AWS private EC2 instance using OpenSSH requires a few prerequisites and steps.</p>
<p><strong>Prerequisites</strong>:</p>
<ol>
<li><p>Ensure you have OpenSSH installed.</p>
</li>
<li><p>Make sure you have AWS CLI version 2.12.6 or above installed.</p>
</li>
<li><p>Set up an EC2 instance connect endpoint (EICE) in your VPC for the subnet where your EC2 instance resides.</p>
</li>
</ol>
<h3 id="heading-steps-to-connect-your-private-instance-using-ssh">Steps to connect your private instance using ssh:</h3>
<ol>
<li><p>Create a private EC2 instance and an EC2 instance connect endpoint in your VPC for the relevant subnet.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688973185652/45b71b95-dd0b-4fc2-97f2-7a4882e0b24e.png" alt class="image--center mx-auto" /></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688973219439/4ba911a0-3bf5-4946-b93c-2fdd3360f30c.png" alt class="image--center mx-auto" /></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688973131979/e1d2ef0d-9716-45fe-8eb3-2213dd1f2d95.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Ensure that the proper security group is associated which allows ssh (port 22) for your IP addresses</p>
</li>
<li><p>On your local machine or the device, you will use to connect via SSH, verify that the correct version of AWS CLI is installed. You can check this by running the command aws ec2-instance-connect help and ensuring that the -o open-tunnel and -o ssh options are available. If not, update your AWS CLI version</p>
<h3 id="heading-for-a-single-connection-to-your-ec2-instance"><strong><em>For a single connection to your EC2 instance,</em></strong></h3>
<p> <em>use the following command</em></p>
<p> <code>ssh -i my-key-pair.pem ec2-user@i-0123456789example \ -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-0123456789example'</code></p>
</li>
</ol>
<p><img src="https://lh3.googleusercontent.com/KJ8H8NZZZKksjde3jdVL26IEi6wQZFw0FiDNKZB-4w51lCuCGDAJaoWAgCzY2pGZG-2GlfmEbzWfvEgptOn_dpnNVBE9Vzlh6M8eavkEW56w4BW7DtBWJ2kj_FyD2-FqZ3zFpj3BwfqJfKygjqvfZg" alt class="image--right mx-auto mr-0" /></p>
<ul>
<li><p>Ensure that you have properly configured your AWS cli</p>
</li>
<li><p>-i – Specify the key pair that was used to launch the instance.</p>
</li>
<li><p>ec2-user@i-0123456789example – Specify the username of the AMI that was used to launch the instance, and the instance ID.</p>
</li>
<li><p>--instance-id – Specify the ID of the instance to connect to. Alternatively, specify %h, which extracts the instance ID from the user as shown in the figure above.</p>
<h3 id="heading-to-enable-multiple-connections-to-your-ec2-instance"><em>To enable multiple connections to your EC2 instance,</em></h3>
<p>  <em>Follow these steps:</em></p>
</li>
<li><p>Start listening for new TCP connections by running the "open-tunnel" command using the AWS CLI. This command sets up a secure tunneling service.</p>
</li>
</ul>
<p><code>aws ec2-instance-connect open-tunnel \ --instance-id i-0123456789example \ --local-port 8888</code></p>
<p><img src="https://lh4.googleusercontent.com/0_MsFZPgttGZ1qeENepRvRVhcfMzf0ugwpNVAQfczqB05L1xtGNqbaO-aLe2_wWRfspfVzfL5IEjdtanORTcuoEe2wuwK-BB3dyUlxw_EVZ4tte0hAiuzA905wmHNmJf6kPnbdQNjZO4mlsuKbxARA" alt /></p>
<ul>
<li><p>Once the tunnel is established, you can create new TCP connections and private tunnels to your EC2 instance using the "ssh" command, allowing multiple users to connect simultaneously.</p>
</li>
<li><p>In a new terminal window, run the following SSH command to create a new TCP connection and a private tunnel to your instance:</p>
<p>  <code>ssh -i my-key-pair.pem ec2-user@localhost -p 8888</code></p>
</li>
</ul>
<p><img src="https://lh4.googleusercontent.com/104TJwcrUs3_mQ34KyYJ2vPFmkdw4IFm72aB3IbP63LR74J6XYawf6O4GMF9yOdKZAnoPTTUNnVZa1wyvZ4jNPYmIlSeQpp7cV9aJw6Dqec1ZxCv4t7rFRga6zsFZmLeG8wU6PXIMfx1ikpzc8F82Q" alt /></p>
<ul>
<li>By initiating the "open-tunnel" command and utilising SSH to create private tunnels, you can enable multiple connections to your EC2 instance, facilitating collaboration and remote access for multiple users.</li>
</ul>
<p><strong>Author:</strong> <a target="_blank" href="https://www.linkedin.com/in/sathiya-narayanan-s/">Sathiya Narayanan S</a></p>
<p><a target="_blank" href="https://www.bootlabstech.com/">BootLabs Technologies</a></p>
]]></content:encoded></item><item><title><![CDATA[AWS Secrets Manager in Kubernetes - Secret Rotation and Reloader]]></title><description><![CDATA[HANDLING SECRETS AND PARAMETERS ON AWS EKS
Security best practices need the protection of personal data (e.g. passwords, tokens, API Keys). These details are often saved in AWS Secrets Manager or AWS Systems Manager Parameter Store when utilising AWS...]]></description><link>https://blog.bootlabstech.com/aws-secrets-manager-in-kubernetes-secret-rotation-and-reloader</link><guid isPermaLink="true">https://blog.bootlabstech.com/aws-secrets-manager-in-kubernetes-secret-rotation-and-reloader</guid><category><![CDATA[awssecretsmanager]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Mon, 27 Mar 2023 07:06:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/lUaaKCUANVI/upload/4f72a4b3453cf053e40c52454f09bf9f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-handling-secrets-and-parameters-on-aws-eks">HANDLING SECRETS AND PARAMETERS ON AWS EKS</h3>
<p>Security best practices need the protection of personal data (e.g. passwords, tokens, API Keys). These details are often saved in <a target="_blank" href="https://aws.amazon.com/secrets-manager/">AWS Secrets Manager</a> or <a target="_blank" href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">AWS Systems Manager Parameter Store</a> when utilising AWS (SSM Parameter Store).</p>
<p>Secrets are straightforward to create and use in Amazon, but getting them from a Kubernetes cluster is not. We often need to obtain secrets from a pod to extract database credentials, API Keys, and so on.</p>
<p>In this article, we'll look at how to configure EKS to use secrets and parameters from Amazon Secrets Manager and AWS Systems Manager Parameter Store.</p>
<h3 id="heading-kubernetes-secrets-versus-aws-secrets">KUBERNETES SECRETS VERSUS AWS SECRETS</h3>
<p>In Kubernetes to store secrets we can use the kind <code>Secret</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Secret</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">test-secret</span>
<span class="hljs-attr">type:</span> <span class="hljs-string">Opaque</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">password:</span> <span class="hljs-string">JdkDSIGhhdZ</span>
</code></pre>
<p>However, using the kind <em>SecretProviderClass</em> offered multiple advantages in a Cloud context:</p>
<ul>
<li><p>Having a <strong>single file to configure the secrets</strong> allows one to manage them from a single place.</p>
</li>
<li><p>Having <strong>secrets outside of the cluster</strong> eases the possible integration with external tools. It is also very easy to share a secret across several Kubernetes namespaces and to keep it in sync.<br />  This externalisation allows also delegating the creation of the secret to an Infrastructure as Code (IaC) tool for example. This way there is no need to find a solution to access Kubernetes’ API (often in private subnets) to create secrets and keep them up to date.</p>
</li>
<li><p>Using AWS Secrets Manager or AWS SSM Parameter Store from an EKS cluster in correlation with Kubernetes service accounts allows <strong>fined grain control over who can access which secrets</strong>. It is also easily possible to define groups of secrets with a <strong>reusable list of secrets</strong>.</p>
</li>
</ul>
<h3 id="heading-prerequisites">PREREQUISITES</h3>
<p>To utilise Amazon Secrets Manager or AWS SSM Parameter Store from Kubernetes, additional configuration is necessary.</p>
<p>We must do the following:</p>
<ul>
<li><p>An IAM policy, with permissions to retrieve secrets from Secret Manager.</p>
</li>
<li><p>Create Secrets in Secretsmanager below are the supported <strong>Secret Types</strong></p>
<ul>
<li><p><strong>A Secrets Manager simple secret</strong> (a plain text secret)</p>
</li>
<li><p><strong>A Secrets Manager JSON formatted secret</strong> (the secret is the whole JSON).</p>
</li>
<li><p><strong>An SSM Parameter Store parameter</strong>.</p>
</li>
</ul>
</li>
<li><p>To connect IAM roles with Kubernetes ServiceAccounts, create an <a target="_blank" href="https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html">OpenID Connect identity provider</a></p>
</li>
<li><p>Install the <a target="_blank" href="https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation.html">Kubernetes Secrets Store CSI Driver</a></p>
</li>
<li><p>Install the <a target="_blank" href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_csi_driver.html">AWS Secrets and Configuration Provider (ASCP)</a></p>
</li>
<li><p>Install the <a target="_blank" href="https://github.com/stakater/Reloader">Reloader</a></p>
</li>
</ul>
<h3 id="heading-iam-role-and-policy">IAM role and policy</h3>
<p>Then we need to create an IAM policy and an IAM role to be used by a service account. Use IAM roles for service accounts (IRSA) to limit secret access to your pods. By setting this up, the provider will retrieve the pod identity and exchange this identity for an IAM role. AWS Secrets and Config provider (ASCP) will then assume the IAM role of the pod and only retrieve secrets from Secrets Manager that the pod is authorized to access. This prevents the container from accessing secrets that are intended for another container that belongs to another pod.</p>
<blockquote>
<p>A service account provides an identity for processes that run in a Pod. These processes will have the permissions of the AWS IAM role attached to the service account.</p>
</blockquote>
<p>The following policy allows:</p>
<ul>
<li><p>The retrieval of secrets from AWS Secrets Manager and AWS SSM Parameter Store</p>
</li>
<li><p>Allowing only the secrets that needed to be loaded ( For minimal access )</p>
</li>
<li><p>The use of a KMS key (required if the secrets are encrypted).</p>
</li>
</ul>
<p><strong>IAM Policy Document</strong></p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"secretsmanager:DescribeSecret"</span>,
                <span class="hljs-string">"secretsmanager:GetSecretValue"</span>
            ],
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Resource"</span>: [               
                <span class="hljs-string">"arn:aws:secretsmanager:&lt;AWS_REGION&gt;:&lt;AWS_ACCOUNT_ID&gt;:secret:mySimpleSecret-s8Yb7Y"</span>,        #Added Secret1 ARN
                <span class="hljs-string">"arn:aws:secretsmanager:&lt;AWS_REGION&gt;:&lt;AWS_ACCOUNT_ID&gt;:secret:myJSONSecret-Uaauu1"</span>,        #Added Secret2 ARN
                <span class="hljs-string">"arn:aws:secretsmanager:&lt;AWS_REGION&gt;:&lt;AWS_ACCOUNT_ID&gt;:secret:/dev/msk/password-iUq8oR"</span>         #Added Secret3 ARN              
                        ]

        }
}
</code></pre>
<p>To follow the principle of least privilege we create an IAM role with a trusted policy which restricts its usage to a specific EKS cluster, namespace and service account.</p>
<p><strong>IAM Role - Restrict with Specific Namespace and Service account</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>: [
    {
      <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"sts:AssumeRoleWithWebIdentity"</span>,
      <span class="hljs-attr">"Principal"</span>: {
        <span class="hljs-attr">"Federated"</span>: <span class="hljs-string">"arn:aws:iam::&lt;AWS_ACCOUNT_ID&gt;:oidc-provider/oidc.eks.&lt;AWS_REGION&gt;.amazonaws.com/id/&lt;OIDC_ID&gt;"</span>
      },
      <span class="hljs-attr">"Condition"</span>: {
        <span class="hljs-attr">"StringEquals"</span>: {
          <span class="hljs-attr">"oidc.eks.&lt;AWS_REGION&gt;.amazonaws.com/id/&lt;OIDC_ID&gt;:aud"</span>: <span class="hljs-string">"sts.amazonaws.com"</span>,
          <span class="hljs-attr">"oidc.eks.&lt;AWS_REGION&gt;.amazonaws.com/id/&lt;OIDC_ID&gt;:sub"</span>: <span class="hljs-string">"system:serviceaccount:&lt;K8S_NAMESPACE&gt;:&lt;SERVICE_ACCOUNT_NAME&gt;"</span>
        }
      }
    }
  ]
}
</code></pre>
<p>If Incase to allow all the Namespace and seviceaccounts in EKS cluster then follow the below IAM role with a trusted policy which restricts its usage to a specific EKS cluster.</p>
<p><strong>IAM Role - Allows all Namespace and Service accounts</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>: [
    {
      <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"sts:AssumeRoleWithWebIdentity"</span>,
      <span class="hljs-attr">"Principal"</span>: {
        <span class="hljs-attr">"Federated"</span>: <span class="hljs-string">"arn:aws:iam::&lt;AWS_ACCOUNT_ID&gt;:oidc-provider/oidc.eks.&lt;AWS_REGION&gt;.amazonaws.com/id/&lt;OIDC_ID&gt;"</span>
      },
      <span class="hljs-attr">"Condition"</span>: {
        <span class="hljs-attr">"StringLike"</span>: {
          <span class="hljs-attr">"oidc.eks.&lt;AWS_REGION&gt;.amazonaws.com/id/&lt;OIDC_ID&gt;:aud"</span>: <span class="hljs-string">"sts.amazonaws.com"</span>,
          <span class="hljs-attr">"oidc.eks.&lt;AWS_REGION&gt;.amazonaws.com/id/&lt;OIDC_ID&gt;:sub"</span>: <span class="hljs-string">"system:serviceaccount:*"</span>  #Allow all namespaces and serviceaccounts
        }
      }
    }
  ]
}
</code></pre>
<p>In this article, we will use <code>test-app-secrets</code> as the namespace and <code>app-admin-account</code> as the service account.</p>
<p>Let's look at how this can be implemented with terraform code</p>
<h3 id="heading-terraform-code-for-implementing-all-the-above-iam-related"><strong>Terraform Code for Implementing all the above IAM Related</strong></h3>
<pre><code class="lang-json">data <span class="hljs-string">"aws_eks_cluster"</span> <span class="hljs-string">"eks_cluster"</span> {
    name = <span class="hljs-attr">"${var.env}-${var.clustername}"</span>
}
data <span class="hljs-string">"aws_iam_openid_connect_provider"</span> <span class="hljs-string">"eks_oidc_provider"</span> {
  url = data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer
}

data <span class="hljs-string">"aws_secretsmanager_secrets"</span> <span class="hljs-string">"retrieve_secrets"</span> {
  filter {
    name   = <span class="hljs-attr">"tag-value"</span>
    values = [var.sub_system]
  }
}

data <span class="hljs-string">"aws_iam_policy_document"</span> <span class="hljs-string">"app_secret_eks_policy"</span> {
  statement {
    actions = [
      <span class="hljs-attr">"secretsmanager:GetSecretValue"</span>,
      <span class="hljs-attr">"secretsmanager:DescribeSecret"</span>
    ]
    resources = flatten([data.aws_secretsmanager_secrets.retrieve_secrets.arns])
    effect    = <span class="hljs-attr">"Allow"</span>
  }
  depends_on = [
    module.secret_manager_secret
  ]
}

resource <span class="hljs-string">"aws_iam_policy"</span> <span class="hljs-string">"app_secret_eks_policy"</span> {
  name        = <span class="hljs-attr">"${var.env}_${var.clustername}_api_readaccess_eks_Policy"</span>
  description = <span class="hljs-attr">"Custom policy for secret read access"</span>
  policy      = data.aws_iam_policy_document.app_secret_eks_policy.json
}

resource <span class="hljs-string">"aws_iam_role"</span> <span class="hljs-string">"app_secret_eks_role"</span> {
  name               = <span class="hljs-attr">"${var.env}-${var.clustername}-api-token-access"</span>
  path               = <span class="hljs-attr">"/"</span>
  assume_role_policy = &lt;&lt;POLICY
{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [{
        <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
        <span class="hljs-attr">"Principal"</span>: {
            <span class="hljs-attr">"Federated"</span>: <span class="hljs-string">"${data.aws_iam_openid_connect_provider.eks_oidc_provider.arn}"</span>
        },
        <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"sts:AssumeRoleWithWebIdentity"</span>,
        <span class="hljs-attr">"Condition"</span>: {
            <span class="hljs-attr">"StringLike"</span>: {
                <span class="hljs-attr">"${replace(data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer, "</span>https:<span class="hljs-comment">//", "")}:sub": "system:serviceaccount:*",</span>
                <span class="hljs-string">"${replace(data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer, "</span>https:<span class="hljs-comment">//", "")}:aud": "sts.amazonaws.com"</span>
            }
        }
    }]
}
POLICY
}

resource <span class="hljs-string">"aws_iam_policy_attachment"</span> <span class="hljs-string">"app_secret_eks_policy_role_attachment"</span> {
  name       = <span class="hljs-attr">"${var.env}-${var.clustername}-app-secret-policy-attach"</span>
  policy_arn = aws_iam_policy.app_secret_eks_policy.arn
  roles      = [aws_iam_role.app_secret_eks_role.name]
}
</code></pre>
<p>The last step is to attach the policy with the role.</p>
<h3 id="heading-serviceaccount-kind">ServiceAccount kind</h3>
<p>Now we can create a <em>ServiceAccount</em> to allow the pods to assume the IAM role.</p>
<p><strong>Serviceaccount.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceAccount</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">app-admin-account</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">test-app-secrets</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">eks.amazonaws.com/role-arn:</span> <span class="hljs-string">&lt;IAM_SERVICE_ACCOUNT_ROLE_ARN&gt;</span>
</code></pre>
<p>It is important to note that this service account is only available to the specified namespace.</p>
<h3 id="heading-secretproviderclass-kind">SecretProviderClass kind</h3>
<p>To use the Secrets Store CSI driver, you have to create a SecretProviderClass custom resource. This provides driver configurations and provider-specific parameters to the CSI driver itself. With the <em>SecretProviderClass</em> kind we can define to which secrets a pod has access.</p>
<p>But first, we need to create some secrets and parameters:</p>
<ul>
<li><p><strong>A Secrets Manager simple secret</strong> (a plain text secret). This secret will be identified by <em>mySimpleSecret</em> in the examples below.</p>
</li>
<li><p><strong>A Secrets Manager JSON formatted secret</strong> (the secret is the whole JSON). This secret will be identified by <em>myJSONSecret</em> in the examples below.</p>
</li>
<li><p><strong>An SSM Parameter Store parameter</strong>. This parameter will be identified by <em>/dev/msk/password</em> in the examples below.</p>
</li>
</ul>
<p>Then we can create a <code>SecretProviderClass</code> manifest.</p>
<p><strong>SecretProviderClass.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">secrets-store.csi.x-k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">SecretProviderClass</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">aws-secrets-providerclass</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">test-app-secrets</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">provider:</span> <span class="hljs-string">aws</span>
  <span class="hljs-attr">parameters:</span>
    <span class="hljs-attr">objects:</span> <span class="hljs-string">|
        - objectName: "mySimpleSecret"
          objectType: "secretsmanager"
        - objectName: "myJSONSecret"
          objectType: "secretsmanager"
        - objectName: "/dev/msk/password"
          objectType: "secretsmanager"</span>
</code></pre>
<p>In this example, we have two secrets from AWS Secrets Manager (using the secret name) and one from SSM Parameter Store (using the parameter key).</p>
<p>Again, these secrets are only available inside the specified namespace.</p>
<h2 id="heading-deploying-the-solution">DEPLOYING THE SOLUTION</h2>
<h3 id="heading-architecture">Architecture</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679846004687/b2b785cf-f91d-4a75-b706-69687ec2c2f5.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-retrieving-secrets-and-parameters">Retrieving Secrets and Parameters</h3>
<p>Update your deployment YAML to use the <a target="_blank" href="http://secrets-store.csi.k8s.io">secrets-store.csi.k8s.io</a> driver, and reference the SecretProviderClass resource created previously. The following is an example of how to configure a pod to mount a volume based on the SecretProviderClass to retrieve secrets from Secrets Manager.</p>
<p>Finally, we are going to check that a Kubernetes pod can use the secrets and parameters we have previously defined. To do so we create a simple Kubernetes <em>Deployment</em>.</p>
<p><strong>Deployment.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">test-hello-world</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">test-app-secrets</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">nginx</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">nginx</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">serviceAccountName:</span> <span class="hljs-string">app-admin-account</span>
      <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mount-secrets-access</span>
        <span class="hljs-attr">csi:</span>
          <span class="hljs-attr">driver:</span> <span class="hljs-string">secrets-store.csi.k8s.io</span>
          <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">volumeAttributes:</span>
            <span class="hljs-attr">secretProviderClass:</span> <span class="hljs-string">"aws-secrets-providerclass"</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">demo-deployment</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
        <span class="hljs-attr">volumeMounts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mount-secrets-access</span>
          <span class="hljs-attr">mountPath:</span> <span class="hljs-string">"/mnt/aws-secrets"</span>
          <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>We mount a volume in the pod using the <a target="_blank" href="http://secrets-store.csi.k8s.io"><code>secrets-store.csi.k8s.io</code></a> driver and the <code>SecretProviderClass</code> we created earlier.</p>
<p><img src="https://d2908q01vomqb2.cloudfront.net/22d200f8670dbdb3e253a90eee5098477c95c23d/2021/04/22/AWS-Secrets-Configuration-Provider-2021-1.png" alt /></p>
<p><strong>Important notes</strong>:</p>
<ul>
<li><p><em>namespace</em> must be the same for the <code>SecretProviderClass</code>, <code>ServiceAccount</code> and <code>Deployment</code>.</p>
</li>
<li><p><em>serviceAccountName</em> must have the same name as the <code>ServiceAccount</code> created previously.</p>
</li>
<li><p><em>secretProviderClass</em> must have the same name as the <code>SecretProviderClass</code> created previously.</p>
</li>
<li><p><em>mountPath</em> is the directory path in the pod file system where we will be able to read all the secrets and parameters included in the specified secret class.</p>
</li>
<li><p><a target="_blank" href="http://volumes.name"><em>volumes.name</em></a> and <a target="_blank" href="http://volumeMounts.name"><em>volumeMounts.name</em></a> can have any value but must be the same.</p>
</li>
</ul>
<p>On pod start and restart, the CSI driver will call the provider binary to retrieve the secret and configurations from Secrets Manager and Parameter Store, respectively. After successfully retrieving this information, the CSI driver will mount them to the container’s file system.After the deployment we can connect to the pod and execute the following commands to check that our secrets are now accessible from our Kubernetes pod:</p>
<p><strong>Validate Secret Mounts</strong></p>
<pre><code class="lang-bash">$ ls /mnt/aws-secrets/
-rw-r--r-- 1 root root 11 Jan 31 23:10 mySQLsecret
-rw-r--r-- 1 root root 74 Jan 31 23:10 mySimpleSecret
-rw-r--r-- 1 root root 72 Jan 31 23:10 myJSONSecret

$ cat /mnt/aws-secrets/mySQLsecret
My parameter

$ cat /mnt/aws-secrets/mySimpleSecret
this !s N0t P@ssw0rd

$ cat /mnt/aws-secrets/myJSONSecret
{ <span class="hljs-string">"username"</span>: <span class="hljs-string">"usernameSecretValue"</span>,<span class="hljs-string">"password"</span>: <span class="hljs-string">"passwordSecretValue"</span> }
</code></pre>
<p>For the JSON secret, to only display a property we need an extra tool like <em>jq</em>:</p>
<pre><code class="lang-bash">
$ cat /mnt/aws-secrets/myJSONSecret | jq -r .username
usernameSecretValue

$ cat /mnt/aws-secrets/myJSONSecret | jq -r .password
passwordSecretValue
</code></pre>
<p>We will see later a better way to retrieve these values.</p>
<h3 id="heading-secrets-and-environment-variables">Secrets and environment variables</h3>
<p>Of course, retrieving secrets from a file comes with its limitation and we usually expect to have them in environment variables.</p>
<p>To do so we need to update the <em>SecretProviderClass</em> manifest:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">secrets-store.csi.x-k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">SecretProviderClass</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">aws-secrets-providerclass</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">test-app-secrets</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">provider:</span> <span class="hljs-string">aws</span>

<span class="hljs-comment">### Start update</span>
  <span class="hljs-attr">secretObjects:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">secretName:</span> <span class="hljs-string">eks-local-secrets</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">Opaque</span>
      <span class="hljs-attr">data:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">objectName:</span> <span class="hljs-string">mySimpleSecret</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">simpleSecret</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">objectName:</span> <span class="hljs-string">myJSONSecret</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">jsonSecret</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">objectName:</span> <span class="hljs-string">parameterAlias</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">myParameter</span>
<span class="hljs-comment">### End update</span>

  <span class="hljs-attr">parameters:</span>
    <span class="hljs-attr">objects:</span> <span class="hljs-string">|
###############
        - objectName: "mySimpleSecret"
          objectType: "secretsmanager"
        - objectName: "myJSONSecret"
          objectType: "secretsmanager"
### Start update
        - objectName: "/dev/msk/password"
          objectType: "secretsmanager"
          objectAlias: parameterAlias
### End update</span>
</code></pre>
<p>We have added a new <em>secretObjects</em> section to create a Kubernetes secret named <em>eks-local-secrets</em> containing the three keys: <em>simpleSecret</em>, <em>jsonSecret</em> and <em>parameterAlias</em>.</p>
<p><strong>Notes</strong>:</p>
<ul>
<li><p>For AWS Secret Manager secrets, <em>objectName</em>s must have the same values in the <em>secretObjects</em> and <em>parameters</em> sections</p>
</li>
<li><p>For SSM Parameter Store secrets, we need to use an <em>ObjectAlias</em> where <code>parameters.objects.objectName = "/dev/msk/password"</code> but <a target="_blank" href="http://secretObjects.data"><code>secretObjects.data</code></a><code>.objectName = parameters.objects.objectAlias</code>.</p>
</li>
</ul>
<p>Next, we need to update the <em>Deployment</em> manifest.</p>
<pre><code class="lang-yaml">[<span class="hljs-string">...</span>]
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">secrets-store-inline</span>
        <span class="hljs-attr">csi:</span>
          <span class="hljs-attr">driver:</span> <span class="hljs-string">secrets-store.csi.k8s.io</span>
          <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">volumeAttributes:</span>
            <span class="hljs-attr">secretProviderClass:</span> <span class="hljs-string">"aws-secrets-providerclass"</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">demo-deployment</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span>
<span class="hljs-comment">###Option1: Method for mapping each env variable with each secretkey reference</span>
<span class="hljs-comment">### Start update</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SIMPLE_SECRET_ENV_VAR</span>
            <span class="hljs-attr">valueFrom:</span>
              <span class="hljs-attr">secretKeyRef:</span>
                <span class="hljs-attr">name:</span> <span class="hljs-string">eks-local-secrets</span>
                <span class="hljs-attr">key:</span> <span class="hljs-string">simpleSecret</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">JSON_SECRET_ENV_VAR</span>
            <span class="hljs-attr">valueFrom:</span>
              <span class="hljs-attr">secretKeyRef:</span>
                <span class="hljs-attr">name:</span> <span class="hljs-string">eks-local-secrets</span>
                <span class="hljs-attr">key:</span> <span class="hljs-string">jsonSecret</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MY_PARAMETER</span>
            <span class="hljs-attr">valueFrom:</span>
              <span class="hljs-attr">secretKeyRef:</span>
                <span class="hljs-attr">name:</span> <span class="hljs-string">eks-local-secrets</span>
                <span class="hljs-attr">key:</span> <span class="hljs-string">myParameter</span>
<span class="hljs-comment">### End update</span>
[<span class="hljs-string">...</span>]
<span class="hljs-comment">#Option2: Method for referencing the list of all secrets using envFrom</span>
<span class="hljs-comment">### Start update</span>
        <span class="hljs-attr">envFrom:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">secretRef:</span>
              <span class="hljs-attr">name:</span> <span class="hljs-string">eks-local-secrets</span>
<span class="hljs-comment">### End update</span>
[<span class="hljs-string">...</span>]
</code></pre>
<p>Set the <code>envFrom</code> key in each container to an object containing the list of secrets that need to be included. In this method, we do not need to map each env referencing the secret key. We can use either of these methods based on the use case.</p>
<p>We have added a new <em>env</em> section to have three environment variables: <code>SIMPLE_SECRET_ENV_VAR</code>, <code>JSON_SECRET_ENV_VAR</code> and <code>JSON_SECRET_ENV_VAR</code> where <em>key</em> references <a target="_blank" href="http://secretObjects.data"><em>secretObjects.data</em></a><em>.key</em> from the <em>SecretProviderClass</em> configuration.</p>
<p>After re-deploying both manifests we can connect to the pod and execute the following commands to check that our secrets are now accessible from a Kubernetes pod:</p>
<p><strong>Validate Secret Mounts</strong></p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">echo</span> <span class="hljs-variable">$SIMPLE_SECRET_ENV_VAR</span>
this !s N0t P@ssw0rd

$ <span class="hljs-built_in">echo</span> <span class="hljs-variable">$JSON_SECRET_ENV_VAR</span>
{<span class="hljs-string">"username"</span>: <span class="hljs-string">"usernameSecretValue"</span>,<span class="hljs-string">"password"</span>: <span class="hljs-string">"passwordSecretValue"</span>}

$ <span class="hljs-built_in">echo</span> <span class="hljs-variable">$MY_PARAMETER</span>
My parameter
</code></pre>
<h3 id="heading-handling-json-secrets">HANDLING JSON SECRETS</h3>
<p>Storing JSON in an environment variable is rarely a good idea. But when we have no choice, we need to make sure that the value in AWS Secrets Manager is not stored in pretty printed format (with newlines, carriage returns, tabs, etc.) like in this example:</p>
<p>To solve this issue all formatting characters must be removed, otherwise we may end up with the environment variable <code>JSON_SECRET_ENV_VAR</code> containing only part of the JSON.</p>
<p>But we usually want to have the <code>username</code> and the <code>password</code> values from the JSON secret into two different environment variables. Fortunately, the <em>jmesPath</em> field allows us to do exactly that (<a target="_blank" href="https://jmespath.org/">JMESPath</a> stands for <em>JSON Matching Expression paths</em> and is a query language for JSON).</p>
<p>Once again we need to update the <em>SecretProviderClass</em> manifest:</p>
<p><strong>SecretProviderClass.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">secrets-store.csi.x-k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">SecretProviderClass</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">aws-secrets-providerclass</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">test-app-secrets</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">provider:</span> <span class="hljs-string">aws</span>

  <span class="hljs-attr">secretObjects:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">secretName:</span> <span class="hljs-string">eks-local-secrets</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">Opaque</span>
      <span class="hljs-attr">data:</span>
<span class="hljs-comment">### Start update</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">objectName:</span> <span class="hljs-string">usernameAlias</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">username</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">objectName:</span> <span class="hljs-string">passwordAlias</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">password</span>
<span class="hljs-comment">### End Update</span>

  <span class="hljs-attr">parameters:</span>
    <span class="hljs-attr">objects:</span> <span class="hljs-string">|
      - objectName: "myJSONSecret"
        objectType: "secretsmanager"
</span><span class="hljs-comment">### Start update</span>
        <span class="hljs-attr">jmesPath:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">username</span>
            <span class="hljs-attr">objectAlias:</span> <span class="hljs-string">usernameAlias</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">password</span>
            <span class="hljs-attr">objectAlias:</span> <span class="hljs-string">passwordAlias</span>
<span class="hljs-comment">### End Update</span>
</code></pre>
<p>In the <em>secretObjects</em> section we added two dedicated variables (<code>username</code> and <code>password</code>) where <em>key</em>s are used as references in the <em>Deployment</em> manifest and <em>object name</em>s are used as references in the <em>parameters</em> section. And like for AWS SSM Parameter Store secrets <a target="_blank" href="http://secretObjects.data"><code>secret objects.data</code></a><code>.objectName = parameters.objects.objectAlias</code>.</p>
<p>We update the <em>Deployment</em> manifest one last time to have two environment variables (<code>USERNAME</code> and <code>PASSWORD</code>) as we previously did.</p>
<p><strong>Deployment. yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
[<span class="hljs-string">...</span>]
        <span class="hljs-attr">env:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">USERNAME</span>
          <span class="hljs-attr">valueFrom:</span>
            <span class="hljs-attr">secretKeyRef:</span>
              <span class="hljs-attr">name:</span> <span class="hljs-string">eks-local-secrets</span>
              <span class="hljs-attr">key:</span> <span class="hljs-string">username</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">PASSWORD</span>
          <span class="hljs-attr">valueFrom:</span>
            <span class="hljs-attr">secretKeyRef:</span>
              <span class="hljs-attr">name:</span> <span class="hljs-string">eks-local-secrets</span>
              <span class="hljs-attr">key:</span> <span class="hljs-string">password</span>
[<span class="hljs-string">...</span>]
</code></pre>
<p>If we connect to the pod:</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">echo</span> <span class="hljs-variable">$USERNAME</span>
usernameSecretValue

$ <span class="hljs-built_in">echo</span> <span class="hljs-variable">$PASSWORD</span>
passwordSecretValue
</code></pre>
<p><strong>Note</strong>: Now in the pod file system we have two new files named <em>username</em> and <em>password</em> containing the individual secret values.</p>
<h2 id="heading-secret-rotation-and-versioning">SECRET ROTATION AND VERSIONING</h2>
<p>The ability to rotate secrets is one of the most important security requirements, especially when data has been <a target="_blank" href="http://exposed.it">exposed. it</a> is worth mentioning that the real added value of using the <em>AWS Secrets and Configuration Provider</em> is the ability to keep Kubernetes secrets synchronised with the AWS secrets. Without this feature, if an AWS secret is changed, the pod must be recreated to get the new secret value.</p>
<p>To enable this feature, we need to add two properties to the <em>Secrets Store CSI Driver</em> installation:</p>
<ul>
<li>Rotation can be enabled in the CSI driver by setting the -enable-secret-rotation flag in the secret store container. In addition, This can also be added to the helm chart using the below methods.</li>
</ul>
<blockquote>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Helm Parameter</td></tr>
</thead>
<tbody>
<tr>
<td><a target="_blank" href="https://secrets-store-csi-driver.sigs.k8s.io/topics/sync-as-kubernetes-secret.html">Sync as Kubernetes secret</a></td><td><code>syncSecret.enabled=true</code></td></tr>
<tr>
<td><a target="_blank" href="https://secrets-store-csi-driver.sigs.k8s.io/topics/secret-auto-rotation.html">Secret Auto rotation</a></td><td><code>enableSecretRotation=true</code></td></tr>
</tbody>
</table>
</div></blockquote>
<ul>
<li><p>The polling interval can be set to control how often the mounted contents for all pods and Kubernetes secrets need to be updated to the newest version. The <code>rotation-poll-interval</code> is set to 2 minutes by default, however, it can be changed by setting up the property <code>rotationPollInterval</code></p>
</li>
<li><p><code>SecretProviderClassPodStatus</code> is a custom resource created by the driver to track the binding between a pod and <code>SecretProviderClass</code>. This <code>SecretProviderClassPodStatus</code> status also shows the secrets and versions that are currently loaded in the pod mount.</p>
</li>
</ul>
<p>View the secret versions that are currently loaded in the pod mount.</p>
<pre><code class="lang-bash">kubectl get secretproviderclasspodstatus &lt;pod_name&gt;
</code></pre>
<p>Note: It is very important to keep in mind that this synchronization doesn’t update the environment variables. It only refreshes the secrets contained in the mounted secret volume. In our examples, it corresponds to the files in <code>/mnt/secrets-store/</code>. To update the environment variables linked to the secrets we still must restart the pod or use an extra tool like <a target="_blank" href="https://github.com/stakater/Reloader">Reloader</a>.</p>
<h2 id="heading-secrets-reloader">SECRETS RELOADER</h2>
<p>Reloader watches for <code>ConfigMap</code> and <code>Secret</code> and detects if there are changes in the data of these objects. After change detection reloader performs a rolling upgrade on relevant Pods via associated <code>Deployment</code>, <code>Daemonset</code> and <code>Statefulset</code>.</p>
<h3 id="heading-annotation-for-secret">Annotation for Secret</h3>
<p>For a <code>Deployment</code> called <code>foo</code> have a <code>Secret</code> called <code>foo</code>. Then add this annotation* to your <code>Deployment</code></p>
<p><strong>Deployment.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
   <span class="hljs-attr">name:</span> <span class="hljs-string">test-hello-world</span>
   <span class="hljs-attr">namespace:</span> <span class="hljs-string">test-app-secrets</span>
   <span class="hljs-attr">labels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">nginx</span>
   <span class="hljs-attr">annotations:</span>
     <span class="hljs-attr">reloader.stakater.com/auto:</span> <span class="hljs-string">"true"</span>
     <span class="hljs-attr">secret.reloader.stakater.com/reload:</span> <span class="hljs-string">eks-local-secrets</span>       <span class="hljs-comment">#Add the secret name that used in eks and to monitor by reloader</span>
</code></pre>
<p>*The default annotation can be changed with the <code>--secret-annotation</code> flag</p>
<p>Above mentioned annotation also works for <code>Daemonsets</code> <code>Statefulsets</code> and <code>Rollouts</code></p>
<h3 id="heading-verify-reloaders-working">Verify Reloader's Working</h3>
<p>Reloader's working can be verified by three ways.</p>
<h3 id="heading-verify-from-logs">Verify from logs</h3>
<p>Check the logs of the reloader and verify that you can see the logs looks like below, if you can find these logs then it means the reloader is working.</p>
<pre><code class="lang-powershell">Changes Detected <span class="hljs-keyword">in</span> <span class="hljs-built_in">test-object</span> of <span class="hljs-built_in">type</span> <span class="hljs-string">'SECRET'</span> <span class="hljs-keyword">in</span> namespace: <span class="hljs-built_in">test-reloader</span>

Updated <span class="hljs-built_in">test-resource</span> of <span class="hljs-built_in">type</span> Deployment <span class="hljs-keyword">in</span> namespace: <span class="hljs-built_in">test-reloader</span>
</code></pre>
<p>Below are the details that explain these logs:</p>
<h3 id="heading-verify-by-checking-the-age-of-the-pod">Verify by checking the age of the Pod</h3>
<p>A pod's age can tell whether the reloader is working correctly or not. If you know that a change in a <code>secret</code> or <code>configmap</code> has occurred, then check the relevant Pod's age immediately. It should be newly created a few moments ago.</p>
<h3 id="heading-verify-from-kubernetes-dashboard">Verify from Kubernetes Dashboard</h3>
<p><code>kubernetes dashboard</code> can be used to verify the working of Reloader. After a change in <code>secret</code> or <code>configmap</code>, check the relevant Pod's age from the dashboard. It should be newly created a few moments ago.</p>
<h3 id="heading-verify-from-the-command-line">Verify from the command line</h3>
<p>After a change in <code>secret</code> or <code>configmap</code>. Run the below-mentioned command and verify that the pod is newly created.</p>
<pre><code class="lang-bash">kubectl get pods &lt;pod name&gt; -n &lt;namespace name&gt;
</code></pre>
<p><strong>Author</strong> : <a target="_blank" href="https://www.linkedin.com/in/chokkalingam-k-ck-b7388890/">Chokkalingam</a></p>
<p><a target="_blank" href="https://www.bootlabstech.com/">BootLabs Technologies</a></p>
]]></content:encoded></item><item><title><![CDATA[Velero Backup Tool]]></title><description><![CDATA[Velero
Velero is an open-source tool for Kubernetes, to safely backup and restores the data in a cluster and disaster recovery of the cluster. With the help of velero, we can store the backup of EKS and volumes will store in a bucket and disk (in AWS...]]></description><link>https://blog.bootlabstech.com/velero-backup-tool</link><guid isPermaLink="true">https://blog.bootlabstech.com/velero-backup-tool</guid><category><![CDATA[velero]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[Devops]]></category><category><![CDATA[clusters]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Wed, 12 Oct 2022 09:53:53 GMT</pubDate><content:encoded><![CDATA[<p><strong>Velero</strong></p>
<p>Velero is an open-source tool for Kubernetes, to safely backup and restores the data in a cluster and disaster recovery of the cluster. With the help of velero, we can store the backup of EKS and volumes will store in a bucket and disk (in AWS).</p>
<p>Velero can be installed in the cluster using the helm chart in the velero namespace. Can check by kubectl get all -n velero</p>
<p>After installing velero we need to establish the connection between cluster and bucket and if disk(If need to take backup of volumes in a cluster) is required. And the credential file is required, by using this command </p>
<pre><code>velero install --provider aws --image velero/velero:v1<span class="hljs-number">.8</span><span class="hljs-number">.0</span> --plugins velero/velero-plugin-<span class="hljs-keyword">for</span>-aws:v1<span class="hljs-number">.4</span><span class="hljs-number">.0</span> --bucket &lt;bucket_name&gt;  --secret-file &lt;path_of_credential_file&gt;  --use-volume-snapshots=<span class="hljs-literal">false</span>&lt;true_if_volume_required&gt; --backup-location-config region=<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">region_of_the_bucket</span>&gt;</span></span>
</code></pre><p>It will take a backup of the cluster or a particular namespace that can be backuped up based on the requirement, these backups will be stored.</p>
<pre><code>velero backup create t2 (-n &lt;name space name&gt; <span class="hljs-keyword">if</span> required)
</code></pre><p>These backups will be replaced when the cluster gets destroyed or some deployment fails or some release has some bucks. </p>
<p>If the connection is established between the EKS and S3 bucket</p>
<pre><code>velero restore create &lt;restore name&gt; --<span class="hljs-keyword">from</span>-backup &lt;backup name&gt;
</code></pre><p>With the help of velero the backup can be automated using a scheduler in velero. It will work like a label selector if the command has a selector then the command will execute in the scheduled time period. It can be of Days or Months or Minutes.</p>
<pre><code>velero schedule create &lt;schedule name&gt;  --schedule=”&lt; time interval to take backup&gt;“--selector app=<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">selector</span> <span class="hljs-attr">name</span>&gt;</span></span>
</code></pre><p>The time interval contains 5 values </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665558894770/PYNkXIcw-.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665558963598/YctCOm9CZ.png" alt="image.png" /></p>
<p>Author:  <a target="_blank" href="https://www.linkedin.com/in/viswanth-selvaraj-3270aa1a9/">Viswanth S</a> </p>
<p><a target="_blank" href="https://www.bootlabs.in/">BootLabs</a></p>
]]></content:encoded></item><item><title><![CDATA[Encapsulated Network]]></title><description><![CDATA[Before going there, 
What is CNI?
A framework for dynamically configuring network resources is known as a container network interface (CNI), and CANAL is the standard CNI network provider. Particularly written in Go are the CNI libraries. 
The CNI pl...]]></description><link>https://blog.bootlabstech.com/encapsulated-network</link><guid isPermaLink="true">https://blog.bootlabstech.com/encapsulated-network</guid><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Tue, 13 Sep 2022 07:38:18 GMT</pubDate><content:encoded><![CDATA[<p>Before going there, </p>
<p><strong>What is CNI?</strong></p>
<p>A framework for dynamically configuring network resources is known as a container network interface (CNI), and CANAL is the standard CNI network provider. Particularly written in Go are the CNI libraries. 
The CNI plugin allows you to accept traffic directly to your pods if you're using a sensitive application.</p>
<p><strong>Encapsulated Network</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663053943396/T8fSeAdo_.png" alt="image.png" /></p>
<p>A logical Layer 2 (L2) network is provided by this network model, which is encapsulated over the current Layer 3 (L3) network topology that connects the Kubernetes cluster nodes. With this paradigm, you can have an isolated L2 network for containers without the requirement for routing distribution, all for the small additional cost of processing and larger IP packages, which are brought on by an IP header produced by overlay encapsulation. Kubernetes workers exchange network control plane knowledge about how MAC addresses can be accessible by exchanging encapsulation information over UDP ports. VXLAN, Internet Protocol Security (IPSec), and IP-in-IP are common encapsulations used in this type of network paradigm.</p>
<p>In simple terms, this network model generates a form of network bridge that is stretched between Kubernetes workers, where connected pods are located.</p>
<p>Thank You for reading!!!</p>
<p>Author: <a target="_blank" href="https://www.linkedin.com/in/ravi-shankar-b56286194/">Ravishankar</a></p>
<p><a target="_blank" href="https://www.bootlabs.in/">BootLabs</a></p>
]]></content:encoded></item><item><title><![CDATA[Build your own CLI using GoLang and Cobra]]></title><description><![CDATA[The Go programming language is an open source project to make programmers more productive.
Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked mac...]]></description><link>https://blog.bootlabstech.com/build-your-own-cli-using-golang-and-cobra</link><guid isPermaLink="true">https://blog.bootlabstech.com/build-your-own-cli-using-golang-and-cobra</guid><category><![CDATA[cli]]></category><category><![CDATA[golang]]></category><category><![CDATA[cobra]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Thu, 14 Jul 2022 11:18:01 GMT</pubDate><content:encoded><![CDATA[<p>The Go programming language is an open source project to make programmers more productive.</p>
<p>Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, while its novel type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It’s a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657797285578/yxuyzr83g.png" alt="Go Programming.png" /></p>
<p><strong>Goals</strong></p>
<p>By the end of this tutorial, you will:</p>
<p>Learn how to use Cobra to create CLI(we will be creating a CLI command for GIT to list all the repo), and
Understand the parts of a web application written in Go.
Understand GitHub RESTful APIs interactions in GoLang.</p>
<p><strong>Prerequisites</strong></p>
<p>For this tutorial, you will need GoLang installed on your machine.</p>
<p>List of Packages we are going to use:</p>
<p>github.com/spf13/cobra</p>
<p><strong>Cobra</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657797303668/2A66Hxtgv.png" alt="Cobra.png" /></p>
<p>Cobra is a library for creating powerful modern CLI applications.</p>
<p>Cobra is used in many Go projects such as Kubernetes, Hugo, and Github CLI to name a few. This list contains a more extensive list of projects using Cobra.</p>
<p>Cobra is a library providing a simple interface to create powerful modern CLI interfaces similar to git &amp; go tools.</p>
<p>Cobra provides:</p>
<ul>
<li>Easy subcommand-based CLIs: app server, app fetch, etc.</li>
<li>Fully POSIX-compliant flags (including short &amp; long versions)</li>
<li>Nested subcommands</li>
<li>Global, local and cascading flags</li>
<li>Intelligent suggestions (app srver... did you mean app server?)</li>
<li>Automatic help generation for commands and flags</li>
<li>Automatic help flag recognition of -h, --help, etc.</li>
<li>Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)</li>
<li>Automatically generated man pages for your application</li>
<li>Command aliases so you can change things without breaking them</li>
<li>The flexibility to define your own help, usage, etc.</li>
<li>Optional seamless integration with viper for 12-factor apps</li>
</ul>
<p><strong>Let’s get started….</strong></p>
<p><strong>main.go</strong></p>
<pre><code>package main

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"go-cli-for-git/cmd"</span>
)

<span class="hljs-title">func</span> <span class="hljs-title">main</span>() {
   <span class="hljs-title">cmd</span>.<span class="hljs-title">Execute</span>()
}

<span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-title">cmd</span><span class="hljs-operator">/</span><span class="hljs-title">execute</span>.<span class="hljs-title">go</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span>

<span class="hljs-title">package</span> <span class="hljs-title">cmd</span>

<span class="hljs-title"><span class="hljs-keyword">import</span></span> (
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"github.com/spf13/cobra"</span>
   <span class="hljs-string">"os"</span>
)

<span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">rootCmd</span> <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span><span class="hljs-title">cobra</span>.<span class="hljs-title">Command</span>{
   <span class="hljs-title">Use</span>:   <span class="hljs-string">"cli"</span>,
   <span class="hljs-title">Short</span>: <span class="hljs-string">"git cli execution using cobra to get all the repositories and their clone url"</span>,
}

<span class="hljs-title">func</span> <span class="hljs-title">Execute</span>() {
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">rootCmd</span>.<span class="hljs-title">Execute</span>(); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      fmt.Println(err)
      os.Exit(<span class="hljs-number">1</span>)
   }
}
</code></pre><p>we will be initializing the cobra command to the rootCmd variable with short description and trying to Execute it.</p>
<p><strong>cmd/base.go</strong></p>
<pre><code>package cmd

<span class="hljs-keyword">import</span> (
   <span class="hljs-title">b64</span> <span class="hljs-string">"encoding/base64"</span>
   <span class="hljs-string">"encoding/json"</span>
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"github.com/spf13/cobra"</span>
   <span class="hljs-string">"io/ioutil"</span>
   <span class="hljs-string">"net/http"</span>
)

<span class="hljs-comment">// addCmd represents the add command</span>
<span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">addCmd</span> <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span><span class="hljs-title">cobra</span>.<span class="hljs-title">Command</span>{
   <span class="hljs-title">Use</span>:   <span class="hljs-string">"get"</span>,
   <span class="hljs-title">Short</span>: <span class="hljs-string">"get repo details"</span>,
   <span class="hljs-title">Long</span>:  `<span class="hljs-title">Get</span> <span class="hljs-title">Repo</span> <span class="hljs-title">information</span> <span class="hljs-title"><span class="hljs-keyword">using</span></span> <span class="hljs-title">the</span> <span class="hljs-title">Cobra</span> <span class="hljs-title">Command</span>`,
   <span class="hljs-title">Run</span>: <span class="hljs-title">func</span>(<span class="hljs-title">cmd</span> <span class="hljs-operator">*</span><span class="hljs-title">cobra</span>.<span class="hljs-title">Command</span>, <span class="hljs-title">args</span> []<span class="hljs-title"><span class="hljs-keyword">string</span></span>) {
      <span class="hljs-title">username</span>, <span class="hljs-title"><span class="hljs-keyword">_</span></span> :<span class="hljs-operator">=</span> <span class="hljs-title">rootCmd</span>.<span class="hljs-title">Flags</span>().<span class="hljs-title">GetString</span>(<span class="hljs-string">"username"</span>)
      <span class="hljs-title">password</span>, <span class="hljs-title"><span class="hljs-keyword">_</span></span> :<span class="hljs-operator">=</span> <span class="hljs-title">rootCmd</span>.<span class="hljs-title">Flags</span>().<span class="hljs-title">GetString</span>(<span class="hljs-string">"password"</span>)
      <span class="hljs-title">auth</span> :<span class="hljs-operator">=</span> <span class="hljs-title">fmt</span>.<span class="hljs-title">Sprintf</span>(<span class="hljs-string">"%s:%s"</span>, <span class="hljs-title">username</span>, <span class="hljs-title">password</span>)
      <span class="hljs-title">authEncode</span> :<span class="hljs-operator">=</span> <span class="hljs-title">b64</span>.<span class="hljs-title">StdEncoding</span>.<span class="hljs-title">EncodeToString</span>([]<span class="hljs-title"><span class="hljs-keyword">byte</span></span>(<span class="hljs-title">auth</span>))

      <span class="hljs-title">url</span> :<span class="hljs-operator">=</span> <span class="hljs-string">"https://api.github.com/user/repos"</span>
      <span class="hljs-title">method</span> :<span class="hljs-operator">=</span> <span class="hljs-string">"GET"</span>

      <span class="hljs-title">client</span> :<span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span><span class="hljs-title">http</span>.<span class="hljs-title">Client</span>{}
      <span class="hljs-title">req</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">http</span>.<span class="hljs-title">NewRequest</span>(<span class="hljs-title">method</span>, <span class="hljs-title">url</span>, <span class="hljs-title">nil</span>)

      <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
         <span class="hljs-title">fmt</span>.<span class="hljs-title">Println</span>(<span class="hljs-title">err</span>)
         <span class="hljs-title"><span class="hljs-keyword">return</span></span>
      }
      <span class="hljs-title">req</span>.<span class="hljs-title">Header</span>.<span class="hljs-title">Add</span>(<span class="hljs-string">"Authorization"</span>, <span class="hljs-title">fmt</span>.<span class="hljs-title">Sprintf</span>(<span class="hljs-string">"Basic %s"</span>, <span class="hljs-title">authEncode</span>))

      <span class="hljs-title">res</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">client</span>.<span class="hljs-title">Do</span>(<span class="hljs-title">req</span>)
      <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
         <span class="hljs-title">fmt</span>.<span class="hljs-title">Println</span>(<span class="hljs-title">err</span>)
         <span class="hljs-title"><span class="hljs-keyword">return</span></span>
      }
      <span class="hljs-title">defer</span> <span class="hljs-title">res</span>.<span class="hljs-title">Body</span>.<span class="hljs-title">Close</span>()

      <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">response</span> []<span class="hljs-title"><span class="hljs-keyword">interface</span></span>{}

      <span class="hljs-title">body</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">ioutil</span>.<span class="hljs-title">ReadAll</span>(<span class="hljs-title">res</span>.<span class="hljs-title">Body</span>)
      <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
         <span class="hljs-title">fmt</span>.<span class="hljs-title">Println</span>(<span class="hljs-title">err</span>)
         <span class="hljs-title"><span class="hljs-keyword">return</span></span>
      }

      <span class="hljs-title">err</span> <span class="hljs-operator">=</span> <span class="hljs-title">json</span>.<span class="hljs-title">Unmarshal</span>(<span class="hljs-title">body</span>, <span class="hljs-operator">&amp;</span><span class="hljs-title">response</span>)

      <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">_</span></span>, <span class="hljs-title">repoDetails</span> :<span class="hljs-operator">=</span> <span class="hljs-title">range</span> <span class="hljs-title">response</span> {
         <span class="hljs-title">repo</span> :<span class="hljs-operator">=</span> <span class="hljs-title">repoDetails</span>.(<span class="hljs-title">map</span>[<span class="hljs-title"><span class="hljs-keyword">string</span></span>]<span class="hljs-title"><span class="hljs-keyword">interface</span></span>{})
         <span class="hljs-title">fmt</span>.<span class="hljs-title">Println</span>(<span class="hljs-string">" name : "</span>, <span class="hljs-title">repo</span>[<span class="hljs-string">"name"</span>], <span class="hljs-string">" private :"</span>, <span class="hljs-title">repo</span>[<span class="hljs-string">"private"</span>], <span class="hljs-string">"clone_url :"</span>, <span class="hljs-title">repo</span>[<span class="hljs-string">"clone_url"</span>])
      }
   },
}

<span class="hljs-title">func</span> <span class="hljs-title">init</span>() {
   <span class="hljs-title">rootCmd</span>.<span class="hljs-title">AddCommand</span>(<span class="hljs-title">addCmd</span>)
   <span class="hljs-title">rootCmd</span>.<span class="hljs-title">PersistentFlags</span>().<span class="hljs-title">StringP</span>(<span class="hljs-string">"username"</span>, <span class="hljs-string">"u"</span>, <span class="hljs-string">""</span>, <span class="hljs-string">"the username of git"</span>)
   <span class="hljs-title">rootCmd</span>.<span class="hljs-title">PersistentFlags</span>().<span class="hljs-title">StringP</span>(<span class="hljs-string">"password"</span>, <span class="hljs-string">"p"</span>, <span class="hljs-string">""</span>, <span class="hljs-string">"the access token of the git"</span>)
}
</code></pre><p>we will be initializing the commands in the rootCmd and initialize the flags that are required to perform your operations.</p>
<p>we have initialized the <strong>addCmd</strong> in the <strong>rootCmd </strong>for the execution of the command and initialzed the flags -u and -p to get the username and accessToken of the gitHub account.</p>
<p><strong>rootCmd.PersistentFlags().StringP("username", "u", "", "the username of git")</strong> is the way you set the PersistentFlags in the command.</p>
<p><strong>rootCmd.Flags().GetString("username")</strong> is the way to get the PersistentFlag value while the command gets executed.</p>
<p>we have used <strong>gitHub RESTful APIs</strong> to get all repositories of an specific account.</p>
<p><strong>https://docs.github.com/en/rest</strong> this link will help you find the other REST API’s that is given by GitHub.</p>
<p>Build Binary of your Application using the below go command</p>
<p>go build -o git-cli
Perfect!! We are all set now. Let’s run this project:</p>
<p>./git-cli get -u  -p 
or</p>
<p>./git-cli get --username  --password </p>
<p>So, It’s a SUCCESS!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657797332086/VBK9HfaHY.png" alt="1_4WiwZX_Pdg3YM9k_5eKmcg.png" /></p>
<p>The Cobra Command got executed and the gitHub Repo’s has been listed in the terminal.</p>
<p>If you find any kind of difficulty following the above steps, please check this repo and run:</p>
<p>git clone https://github.com/venkateshsuresh/go-cli-for-git.git</p>
<p>I hope this article helped you. Thanks for reading and stay tuned!</p>
<p>@<a target="_blank" href="https://www.linkedin.com/in/venkatesh-c-s-603102173/">Venkatesh</a></p>
<p>@<a target="_blank" href="https://www.bootlabs.in/">BootLabs</a></p>
]]></content:encoded></item><item><title><![CDATA[GCP IAM Binding using Temporal and GoLang(Gin Framework)]]></title><description><![CDATA[Gin is the web framework written in Go(GoLang). Gin is a high-performance micro-framework that can be used to build web applications. It allows you to write middleware that can be plugged into one or more request handlers or groups of request handler...]]></description><link>https://blog.bootlabstech.com/gcp-iam-binding-using-temporal-and-golanggin-framework</link><guid isPermaLink="true">https://blog.bootlabstech.com/gcp-iam-binding-using-temporal-and-golanggin-framework</guid><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Thu, 14 Jul 2022 10:05:20 GMT</pubDate><content:encoded><![CDATA[<p>Gin is the web framework written in Go(GoLang). Gin is a high-performance micro-framework that can be used to build web applications. It allows you to write middleware that can be plugged into one or more request handlers or groups of request handlers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657806298813/1X7exPHyI.jpeg" alt="Gin Gonic.jpeg" /></p>
<p><strong>Goals</strong></p>
<p>By the end of this tutorial, you will:</p>
<ul>
<li>Learn how to use Gin to create RESTful APIs (we will be doing GCP IAM Binding using GoLang and Temporal), and</li>
<li>Understand the parts of a web application written in Go.</li>
<li>Understand Goroutine and how it is useful.</li>
<li>Understand Temporal WorkFlows and Activities.</li>
<li>Understand Cloud SDK Client interactions in GoLang.</li>
</ul>
<p><strong>Prerequisites</strong></p>
<p>For this tutorial, you will need GoLang, Temporal, docker, and postman installed on your machine.</p>
<p>Note: If you don’t have postman, you can use any other tool that you would use to test API endpoints.</p>
<p>List of Packages we are going to use:</p>
<pre><code>github.com/gin<span class="hljs-operator">-</span>gonic<span class="hljs-operator">/</span>gin
github.com/sirupsen<span class="hljs-operator">/</span>logrus
go.temporal.io/sdk
google.golang.org/api
</code></pre><p><strong>Goroutine</strong></p>
<p>Goroutine is a lightweight thread in Golang. All programs executed by Golang run on the Goroutine. That is, the main function is also executed on the Goroutine.</p>
<p>In other words, every program in Golang must have a least one Goroutine.</p>
<p>In Golang, you can use the Goroutine to execute the function with the go keyword like the below.</p>
<p><strong>Temporal</strong></p>
<p>A Temporal Application is a set of Temporal Workflow Executions. Each Temporal Workflow Execution has exclusive access to its local state, executes concurrently to all other Workflow Executions, and communicates with other Workflow Executions and the environment via message passing.</p>
<p>A Temporal Application can consist of millions to billions of Workflow Executions. Workflow Executions are lightweight components. A Workflow Execution consumes few compute resources; in fact, if a Workflow Execution is suspended, such as when it is in a waiting state, the Workflow Execution consumes no compute resources at all.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657806355124/NB3mBhcyy.png" alt="Temporal.png" /></p>
<p><strong>main.go</strong></p>
<pre><code><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"github.com/gin-gonic/gin"</span>
   <span class="hljs-string">"personalproject/temporal/worker"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
   r := gin.Default()
   channel1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">interface</span>{})
   <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
      channel1 &lt;- <span class="hljs-keyword">struct</span>{}{}
   }()
   <span class="hljs-keyword">go</span> iamWorkFlowInitialize(channel1)

   r.POST(<span class="hljs-string">"/iambinding"</span>, worker.IamWorkFlow)
   r.Run()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">iamWorkFlowInitialize</span><span class="hljs-params">(channel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">interface</span>{})</span></span> {
   err := worker.IamWorker.Run(channel)
   <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
      <span class="hljs-built_in">panic</span>(err)
   }
}
</code></pre><p>we will be running the temporal worker as a thread to intialize the worker and starting our Gin server in parallel.</p>
<p>Temporal Worker</p>
<p>In day-to-day conversations, the term Worker is used to denote either a Worker Program, a Worker Process, or a Worker Entity. Temporal documentation aims to be explicit and differentiate between them.</p>
<p><strong>worker/worker.go</strong></p>
<pre><code><span class="hljs-keyword">package</span> worker

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"go.temporal.io/sdk/client"</span>
   <span class="hljs-string">"go.temporal.io/sdk/worker"</span>
   <span class="hljs-string">"os"</span>
)

<span class="hljs-keyword">const</span> IAMTASKQUEUE = <span class="hljs-string">"IAM_TASK_QUEUE"</span>

<span class="hljs-keyword">var</span> IamWorker worker.Worker = newWorker()

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">newWorker</span><span class="hljs-params">()</span> <span class="hljs-title">worker</span>.<span class="hljs-title">Worker</span></span> {
   opts := client.Options{
      HostPort: os.Getenv(<span class="hljs-string">"TEMPORAL_HOSTPORT"</span>),
   }
   c, err := client.NewClient(opts)
   <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
      <span class="hljs-built_in">panic</span>(err)
   }

   w := worker.New(c, IAMTASKQUEUE, worker.Options{})
   w.RegisterWorkflow(IamBindingGoogle)
   w.RegisterActivity(AddIAMBinding)

   <span class="hljs-keyword">return</span> w
}
</code></pre><p>The IamBindingGoogle workFlow and AddIAMBinding Activity is registered in the Worker.</p>
<p>Workflow Definition refers to the source for the instance of a Workflow Execution, while a Workflow Function refers to the source for the instance of a Workflow Function Execution.</p>
<p>The purpose of an Activity is to execute a single, well-defined action (either short or long running), such as calling another service, transcoding a media file, or sending an email.</p>
<p><strong>worker/iam_model.go</strong></p>
<pre><code>package worker

<span class="hljs-keyword">type</span> IamDetails struct {
   ProjectID string `<span class="hljs-type">json</span>:"project_id"`
   <span class="hljs-keyword">User</span>      string `<span class="hljs-type">json</span>:"user"`
   <span class="hljs-keyword">Role</span>      string `<span class="hljs-type">json</span>:"role"`
}
</code></pre><p>This defines the schema of the Iam Inputs.</p>
<p><strong>worker/base.go</strong></p>
<pre><code>package worker

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"bytes"</span>
   <span class="hljs-string">"encoding/json"</span>
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"github.com/gin-gonic/gin"</span>
   <span class="hljs-string">"io"</span>
)

<span class="hljs-title">func</span> <span class="hljs-title">LoadData</span>(<span class="hljs-title">c</span> <span class="hljs-operator">*</span><span class="hljs-title">gin</span>.<span class="hljs-title">Context</span>, <span class="hljs-title">model</span> <span class="hljs-title"><span class="hljs-keyword">interface</span></span>{}) <span class="hljs-title"><span class="hljs-keyword">error</span></span> {
   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">body</span> <span class="hljs-title"><span class="hljs-keyword">bytes</span></span>.<span class="hljs-title">Buffer</span>

   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title"><span class="hljs-keyword">_</span></span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">io</span>.<span class="hljs-title">Copy</span>(<span class="hljs-operator">&amp;</span><span class="hljs-title">body</span>, <span class="hljs-title">c</span>.<span class="hljs-title">Request</span>.<span class="hljs-title">Body</span>); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      customErr :<span class="hljs-operator">=</span> fmt.Errorf(<span class="hljs-string">"response parsing failed %w"</span>, err)

      <span class="hljs-keyword">return</span> customErr
   }

   <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> json.Unmarshal(body.Bytes(), <span class="hljs-operator">&amp;</span>model)

   <span class="hljs-keyword">return</span> nil
}
</code></pre><p>LoadData function is used to Unmarshal the data that is recieved in the Api request.</p>
<p><strong>worker/workflowsvc.go</strong></p>
<pre><code>package worker

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"context"</span>
   <span class="hljs-string">"go.temporal.io/sdk/client"</span>
   <span class="hljs-string">"os"</span>
)

<span class="hljs-title"><span class="hljs-keyword">var</span></span> (
   <span class="hljs-title">IamSvc</span> <span class="hljs-title">IamServiceI</span> <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span><span class="hljs-title">iamServiceStruct</span>{}
)

<span class="hljs-title"><span class="hljs-keyword">type</span></span> <span class="hljs-title">IamServiceI</span> <span class="hljs-title"><span class="hljs-keyword">interface</span></span> {
   <span class="hljs-title">IamBindingService</span>(<span class="hljs-title">details</span> <span class="hljs-title">IamDetails</span>) <span class="hljs-title"><span class="hljs-keyword">error</span></span>
}

<span class="hljs-title"><span class="hljs-keyword">type</span></span> <span class="hljs-title">iamServiceStruct</span> <span class="hljs-title"><span class="hljs-keyword">struct</span></span> {
}

<span class="hljs-title"><span class="hljs-keyword">type</span></span> <span class="hljs-title">iamServiceModel</span> <span class="hljs-title"><span class="hljs-keyword">struct</span></span> {
   <span class="hljs-title">client</span>     <span class="hljs-title">client</span>.<span class="hljs-title">Client</span>
   <span class="hljs-title">workflowID</span> <span class="hljs-title"><span class="hljs-keyword">string</span></span>
}

<span class="hljs-title">func</span> (<span class="hljs-operator">*</span><span class="hljs-title">iamServiceStruct</span>) <span class="hljs-title">IamBindingService</span>(<span class="hljs-title">details</span> <span class="hljs-title">IamDetails</span>) <span class="hljs-title"><span class="hljs-keyword">error</span></span> {
   <span class="hljs-title">cr</span> :<span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">new</span></span>(<span class="hljs-title">iamServiceModel</span>)
   <span class="hljs-title">opts</span> :<span class="hljs-operator">=</span> <span class="hljs-title">client</span>.<span class="hljs-title">Options</span>{
      <span class="hljs-title">HostPort</span>: <span class="hljs-title">os</span>.<span class="hljs-title">Getenv</span>(<span class="hljs-string">"TEMPORAL_HOSTPORT"</span>),
   }
   <span class="hljs-title">c</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">client</span>.<span class="hljs-title">NewClient</span>(<span class="hljs-title">opts</span>)
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">panic</span>(<span class="hljs-title">err</span>)
   }

   <span class="hljs-title">cr</span>.<span class="hljs-title">client</span> <span class="hljs-operator">=</span> <span class="hljs-title">c</span>

   <span class="hljs-title">workflowOptions</span> :<span class="hljs-operator">=</span> <span class="hljs-title">client</span>.<span class="hljs-title">StartWorkflowOptions</span>{
      <span class="hljs-title">TaskQueue</span>: <span class="hljs-title">IAMTASKQUEUE</span>,
   }

   <span class="hljs-title"><span class="hljs-keyword">_</span></span>, <span class="hljs-title">err</span> <span class="hljs-operator">=</span> <span class="hljs-title">cr</span>.<span class="hljs-title">client</span>.<span class="hljs-title">ExecuteWorkflow</span>(<span class="hljs-title">context</span>.<span class="hljs-title">Background</span>(), <span class="hljs-title">workflowOptions</span>, <span class="hljs-title">IamBindingGoogle</span>, <span class="hljs-title">details</span>)
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">err</span>
   }

   <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">nil</span>
}
</code></pre><p>here is the service layer of the WorkFlow where there is an interface which implements the methods which is defined on the interface.</p>
<p><strong>worker/workflow.go</strong></p>
<pre><code>package worker

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"github.com/gin-gonic/gin"</span>
   <span class="hljs-string">"github.com/sirupsen/logrus"</span>
   <span class="hljs-string">"go.temporal.io/sdk/temporal"</span>
   <span class="hljs-string">"go.temporal.io/sdk/workflow"</span>
   <span class="hljs-string">"net/http"</span>
   <span class="hljs-string">"time"</span>
)

<span class="hljs-title">func</span> <span class="hljs-title">IamWorkFlow</span>(<span class="hljs-title">c</span> <span class="hljs-operator">*</span><span class="hljs-title">gin</span>.<span class="hljs-title">Context</span>) {
   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">details</span> <span class="hljs-title">IamDetails</span>
   <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">LoadData</span>(<span class="hljs-title">c</span>, <span class="hljs-operator">&amp;</span><span class="hljs-title">details</span>)

   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">logrus</span>.<span class="hljs-title"><span class="hljs-built_in">Error</span></span>(<span class="hljs-title">err</span>)
      <span class="hljs-title">c</span>.<span class="hljs-title">JSON</span>(<span class="hljs-title">http</span>.<span class="hljs-title">StatusBadRequest</span>, <span class="hljs-title">err</span>)

      <span class="hljs-title"><span class="hljs-keyword">return</span></span>
   }

   <span class="hljs-title">err</span> <span class="hljs-operator">=</span> <span class="hljs-title">IamSvc</span>.<span class="hljs-title">IamBindingService</span>(<span class="hljs-title">details</span>)
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">logrus</span>.<span class="hljs-title"><span class="hljs-built_in">Error</span></span>(<span class="hljs-title">err</span>)
      <span class="hljs-title">c</span>.<span class="hljs-title">JSON</span>(<span class="hljs-title">http</span>.<span class="hljs-title">StatusBadRequest</span>, <span class="hljs-title">err</span>)

      <span class="hljs-title"><span class="hljs-keyword">return</span></span>
   }

   <span class="hljs-title">c</span>.<span class="hljs-title">JSON</span>(<span class="hljs-title">http</span>.<span class="hljs-title">StatusOK</span>, <span class="hljs-title">err</span>)
}

<span class="hljs-title">func</span> <span class="hljs-title">IamBindingGoogle</span>(<span class="hljs-title">ctx</span> <span class="hljs-title">workflow</span>.<span class="hljs-title">Context</span>, <span class="hljs-title">details</span> <span class="hljs-title">IamDetails</span>) (<span class="hljs-title"><span class="hljs-keyword">string</span></span>, <span class="hljs-title"><span class="hljs-keyword">error</span></span>) {

   <span class="hljs-title">iamCtx</span> :<span class="hljs-operator">=</span> <span class="hljs-title">workflow</span>.<span class="hljs-title">WithActivityOptions</span>(
      <span class="hljs-title">ctx</span>,
      <span class="hljs-title">workflow</span>.<span class="hljs-title">ActivityOptions</span>{
         <span class="hljs-title">StartToCloseTimeout</span>:    1 <span class="hljs-operator">*</span> <span class="hljs-title">time</span>.<span class="hljs-title">Hour</span>,
         <span class="hljs-title">ScheduleToCloseTimeout</span>: 1 <span class="hljs-operator">*</span> <span class="hljs-title">time</span>.<span class="hljs-title">Hour</span>,
         <span class="hljs-title">RetryPolicy</span>: <span class="hljs-operator">&amp;</span><span class="hljs-title">temporal</span>.<span class="hljs-title">RetryPolicy</span>{
            <span class="hljs-title">MaximumAttempts</span>: 3,
         },
         <span class="hljs-title">TaskQueue</span>: <span class="hljs-title">IAMTASKQUEUE</span>,
      })

   <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">workflow</span>.<span class="hljs-title">ExecuteActivity</span>(<span class="hljs-title">iamCtx</span>, <span class="hljs-title">AddIAMBinding</span>, <span class="hljs-title">details</span>).<span class="hljs-title">Get</span>(<span class="hljs-title">ctx</span>, <span class="hljs-title">nil</span>)

   <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-string">""</span>, <span class="hljs-title">err</span>
}
</code></pre><p>A Workflow Execution effectively executes once to completion, while a Workflow Function Execution occurs many times during the life of a Workflow Execution.</p>
<p>The IamBindingGoogle WorkFlow has been using the context of workflow and the iamDetails which contains information of google_project_id, user_name and the role that should be given in gcp. Those details will be send to an activity function which executes IAM Binding.</p>
<p>The ExecuteActivity function should have the Activity options such as StartToCloseTimeout, ScheduleToCloseTimeout, Retry policy and TaskQueue. Each Activity function can return the output that is defined the Activity.</p>
<p><strong>worker/activity.go</strong></p>
<pre><code>package worker

<span class="hljs-keyword">import</span> (
   <span class="hljs-string">"context"</span>
   <span class="hljs-string">"flag"</span>
   <span class="hljs-string">"fmt"</span>
   <span class="hljs-string">"github.com/sirupsen/logrus"</span>
   <span class="hljs-string">"google.golang.org/api/cloudresourcemanager/v1"</span>
   <span class="hljs-string">"google.golang.org/api/option"</span>
   <span class="hljs-string">"os"</span>
   <span class="hljs-string">"strings"</span>
   <span class="hljs-string">"time"</span>
)

<span class="hljs-title">func</span> <span class="hljs-title">AddIAMBinding</span>(<span class="hljs-title">details</span> <span class="hljs-title">IamDetails</span>) <span class="hljs-title"><span class="hljs-keyword">error</span></span> {
   <span class="hljs-title">projectID</span> :<span class="hljs-operator">=</span> <span class="hljs-title">details</span>.<span class="hljs-title">ProjectID</span>
   <span class="hljs-title">member</span> :<span class="hljs-operator">=</span> <span class="hljs-title">fmt</span>.<span class="hljs-title">Sprintf</span>(<span class="hljs-string">"user:%s"</span>, <span class="hljs-title">details</span>.<span class="hljs-title">User</span>)
   <span class="hljs-title">flag</span>.<span class="hljs-title">Parse</span>()

   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">role</span> <span class="hljs-title"><span class="hljs-keyword">string</span></span> <span class="hljs-operator">=</span> <span class="hljs-title">details</span>.<span class="hljs-title">Role</span>
   <span class="hljs-title">ctx1</span> :<span class="hljs-operator">=</span> <span class="hljs-title">context</span>.<span class="hljs-title">TODO</span>()
   <span class="hljs-title">crmService</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">NewService</span>(<span class="hljs-title">ctx1</span>, <span class="hljs-title">option</span>.<span class="hljs-title">WithCredentialsFile</span>(<span class="hljs-title">os</span>.<span class="hljs-title">Getenv</span>(<span class="hljs-string">"GOOGLE_APPLICATION_CREDENTIALS"</span>)))
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">logrus</span>.<span class="hljs-title">Errorf</span>(<span class="hljs-string">"cloudresourcemanager.NewService: %v"</span>, <span class="hljs-title">err</span>)

      <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">err</span>
   }

   <span class="hljs-title">addBinding</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>, <span class="hljs-title">member</span>, <span class="hljs-title">role</span>)
   <span class="hljs-title">policy</span> :<span class="hljs-operator">=</span> <span class="hljs-title">getPolicy</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>)
   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">binding</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Binding</span>
   <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">_</span></span>, <span class="hljs-title">b</span> :<span class="hljs-operator">=</span> <span class="hljs-title">range</span> <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span> {
      <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">b</span>.<span class="hljs-title">Role</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-title">role</span> {
         <span class="hljs-title">binding</span> <span class="hljs-operator">=</span> <span class="hljs-title">b</span>
         <span class="hljs-title"><span class="hljs-keyword">break</span></span>
      }
   }
   <span class="hljs-title">fmt</span>.<span class="hljs-title">Println</span>(<span class="hljs-string">"Role: "</span>, <span class="hljs-title">binding</span>.<span class="hljs-title">Role</span>)
   <span class="hljs-title">fmt</span>.<span class="hljs-title">Print</span>(<span class="hljs-string">"Members: "</span>, <span class="hljs-title">strings</span>.<span class="hljs-title">Join</span>(<span class="hljs-title">binding</span>.<span class="hljs-title">Members</span>, <span class="hljs-string">", "</span>))

   <span class="hljs-title">removeMember</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>, <span class="hljs-title">member</span>, <span class="hljs-title">role</span>)

   <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">nil</span>
}

<span class="hljs-title">func</span> <span class="hljs-title">addBinding</span>(<span class="hljs-title">crmService</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Service</span>, <span class="hljs-title">projectID</span>, <span class="hljs-title">member</span>, <span class="hljs-title">role</span> <span class="hljs-title"><span class="hljs-keyword">string</span></span>) {

   <span class="hljs-title">policy</span> :<span class="hljs-operator">=</span> <span class="hljs-title">getPolicy</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>)

   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">binding</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Binding</span>
   <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">_</span></span>, <span class="hljs-title">b</span> :<span class="hljs-operator">=</span> <span class="hljs-title">range</span> <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span> {
      <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">b</span>.<span class="hljs-title">Role</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-title">role</span> {
         <span class="hljs-title">binding</span> <span class="hljs-operator">=</span> <span class="hljs-title">b</span>
         <span class="hljs-title"><span class="hljs-keyword">break</span></span>
      }
   }

   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">binding</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">binding</span>.<span class="hljs-title">Members</span> <span class="hljs-operator">=</span> <span class="hljs-title">append</span>(<span class="hljs-title">binding</span>.<span class="hljs-title">Members</span>, <span class="hljs-title">member</span>)
   } <span class="hljs-title"><span class="hljs-keyword">else</span></span> {
      <span class="hljs-title">binding</span> <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Binding</span>{
         <span class="hljs-title">Role</span>:    <span class="hljs-title">role</span>,
         <span class="hljs-title">Members</span>: []<span class="hljs-title"><span class="hljs-keyword">string</span></span>{<span class="hljs-title">member</span>},
      }
      <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span> <span class="hljs-operator">=</span> <span class="hljs-title">append</span>(<span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span>, <span class="hljs-title">binding</span>)
   }

   <span class="hljs-title">setPolicy</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>, <span class="hljs-title">policy</span>)

}

<span class="hljs-title">func</span> <span class="hljs-title">removeMember</span>(<span class="hljs-title">crmService</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Service</span>, <span class="hljs-title">projectID</span>, <span class="hljs-title">member</span>, <span class="hljs-title">role</span> <span class="hljs-title"><span class="hljs-keyword">string</span></span>) {

   <span class="hljs-title">policy</span> :<span class="hljs-operator">=</span> <span class="hljs-title">getPolicy</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>)

   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">binding</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Binding</span>
   <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">bindingIndex</span> <span class="hljs-title"><span class="hljs-keyword">int</span></span>
   <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">i</span>, <span class="hljs-title">b</span> :<span class="hljs-operator">=</span> <span class="hljs-title">range</span> <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span> {
      <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">b</span>.<span class="hljs-title">Role</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-title">role</span> {
         <span class="hljs-title">binding</span> <span class="hljs-operator">=</span> <span class="hljs-title">b</span>
         <span class="hljs-title">bindingIndex</span> <span class="hljs-operator">=</span> <span class="hljs-title">i</span>
         <span class="hljs-title"><span class="hljs-keyword">break</span></span>
      }
   }

   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">len</span>(<span class="hljs-title">binding</span>.<span class="hljs-title">Members</span>) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> 1 {
      <span class="hljs-title">last</span> :<span class="hljs-operator">=</span> <span class="hljs-title">len</span>(<span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span>) <span class="hljs-operator">-</span> 1
      <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span>[<span class="hljs-title">bindingIndex</span>] <span class="hljs-operator">=</span> <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span>[<span class="hljs-title">last</span>]
      <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span> <span class="hljs-operator">=</span> <span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span>[:<span class="hljs-title">last</span>]
   } <span class="hljs-title"><span class="hljs-keyword">else</span></span> {
      <span class="hljs-title"><span class="hljs-keyword">var</span></span> <span class="hljs-title">memberIndex</span> <span class="hljs-title"><span class="hljs-keyword">int</span></span>
      <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">i</span>, <span class="hljs-title">mm</span> :<span class="hljs-operator">=</span> <span class="hljs-title">range</span> <span class="hljs-title">binding</span>.<span class="hljs-title">Members</span> {
         <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">mm</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-title">member</span> {
            <span class="hljs-title">memberIndex</span> <span class="hljs-operator">=</span> <span class="hljs-title">i</span>
         }
      }
      <span class="hljs-title">last</span> :<span class="hljs-operator">=</span> <span class="hljs-title">len</span>(<span class="hljs-title">policy</span>.<span class="hljs-title">Bindings</span>[<span class="hljs-title">bindingIndex</span>].<span class="hljs-title">Members</span>) <span class="hljs-operator">-</span> 1
      <span class="hljs-title">binding</span>.<span class="hljs-title">Members</span>[<span class="hljs-title">memberIndex</span>] <span class="hljs-operator">=</span> <span class="hljs-title">binding</span>.<span class="hljs-title">Members</span>[<span class="hljs-title">last</span>]
      <span class="hljs-title">binding</span>.<span class="hljs-title">Members</span> <span class="hljs-operator">=</span> <span class="hljs-title">binding</span>.<span class="hljs-title">Members</span>[:<span class="hljs-title">last</span>]
   }

   <span class="hljs-title">setPolicy</span>(<span class="hljs-title">crmService</span>, <span class="hljs-title">projectID</span>, <span class="hljs-title">policy</span>)

}

<span class="hljs-title">func</span> <span class="hljs-title">getPolicy</span>(<span class="hljs-title">crmService</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Service</span>, <span class="hljs-title">projectID</span> <span class="hljs-title"><span class="hljs-keyword">string</span></span>) <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Policy</span> {

   <span class="hljs-title">ctx</span> :<span class="hljs-operator">=</span> <span class="hljs-title">context</span>.<span class="hljs-title">Background</span>()

   <span class="hljs-title">ctx</span>, <span class="hljs-title">cancel</span> :<span class="hljs-operator">=</span> <span class="hljs-title">context</span>.<span class="hljs-title">WithTimeout</span>(<span class="hljs-title">ctx</span>, <span class="hljs-title">time</span>.<span class="hljs-title">Second</span><span class="hljs-operator">*</span>10)
   <span class="hljs-title">defer</span> <span class="hljs-title">cancel</span>()
   <span class="hljs-title">request</span> :<span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">new</span></span>(<span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">GetIamPolicyRequest</span>)
   <span class="hljs-title">policy</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">crmService</span>.<span class="hljs-title">Projects</span>.<span class="hljs-title">GetIamPolicy</span>(<span class="hljs-title">projectID</span>, <span class="hljs-title">request</span>).<span class="hljs-title">Do</span>()
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">logrus</span>.<span class="hljs-title">Errorf</span>(<span class="hljs-string">"Projects.GetIamPolicy: %v"</span>, <span class="hljs-title">err</span>)
   }

   <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">policy</span>
}

<span class="hljs-title">func</span> <span class="hljs-title">setPolicy</span>(<span class="hljs-title">crmService</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Service</span>, <span class="hljs-title">projectID</span> <span class="hljs-title"><span class="hljs-keyword">string</span></span>, <span class="hljs-title">policy</span> <span class="hljs-operator">*</span><span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">Policy</span>) {

   <span class="hljs-title">ctx</span> :<span class="hljs-operator">=</span> <span class="hljs-title">context</span>.<span class="hljs-title">Background</span>()

   <span class="hljs-title">ctx</span>, <span class="hljs-title">cancel</span> :<span class="hljs-operator">=</span> <span class="hljs-title">context</span>.<span class="hljs-title">WithTimeout</span>(<span class="hljs-title">ctx</span>, <span class="hljs-title">time</span>.<span class="hljs-title">Second</span><span class="hljs-operator">*</span>10)
   <span class="hljs-title">defer</span> <span class="hljs-title">cancel</span>()
   <span class="hljs-title">request</span> :<span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">new</span></span>(<span class="hljs-title">cloudresourcemanager</span>.<span class="hljs-title">SetIamPolicyRequest</span>)
   <span class="hljs-title">request</span>.<span class="hljs-title">Policy</span> <span class="hljs-operator">=</span> <span class="hljs-title">policy</span>
   <span class="hljs-title">policy</span>, <span class="hljs-title">err</span> :<span class="hljs-operator">=</span> <span class="hljs-title">crmService</span>.<span class="hljs-title">Projects</span>.<span class="hljs-title">SetIamPolicy</span>(<span class="hljs-title">projectID</span>, <span class="hljs-title">request</span>).<span class="hljs-title">Do</span>()
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-title">nil</span> {
      <span class="hljs-title">logrus</span>.<span class="hljs-title">Errorf</span>(<span class="hljs-string">"Projects.SetIamPolicy: %v"</span>, <span class="hljs-title">err</span>)
   }
}
</code></pre><p>Google Cloud Go SDK is used here for actual iamBinding.</p>
<ul>
<li>Initializes the Resource Manager service, which manages Google Cloud projects.</li>
<li>Reads the allow policy for your project.</li>
<li>Modifies the allow policy by granting the role that you are sending in the request to your Google Account.</li>
<li>Writes the updated allow policy.</li>
<li>Revokes the role again.</li>
</ul>
<p>Finally we need temporal setup using docker,</p>
<p><strong>.local/quickstart.yml</strong></p>
<pre><code><span class="hljs-attribute">version</span>: '3.2'

<span class="solidity">services:
  elasticsearch:
    container_name: temporal<span class="hljs-operator">-</span>elasticsearch
    environment:
      <span class="hljs-operator">-</span> cluster.routing.allocation.disk.threshold_enabled=<span class="hljs-literal">true</span>
      <span class="hljs-operator">-</span> cluster.routing.allocation.disk.watermark.low=512mb
      <span class="hljs-operator">-</span> cluster.routing.allocation.disk.watermark.high=256mb
      <span class="hljs-operator">-</span> cluster.routing.allocation.disk.watermark.flood_stage=128mb
      <span class="hljs-operator">-</span> discovery.type=single<span class="hljs-operator">-</span>node
      <span class="hljs-operator">-</span> ES_JAVA_OPTS<span class="hljs-operator">=</span><span class="hljs-operator">-</span>Xms100m <span class="hljs-operator">-</span>Xmx100m
    volumes:
      <span class="hljs-operator">-</span> esdata:<span class="hljs-operator">/</span>usr<span class="hljs-operator">/</span>share<span class="hljs-operator">/</span>elasticsearch<span class="hljs-operator">/</span>data:rw
    image: elasticsearch:<span class="hljs-number">7.10</span><span class="hljs-number">.1</span>
    networks:
      <span class="hljs-operator">-</span> temporal<span class="hljs-operator">-</span>network
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">9200</span>:<span class="hljs-number">9200</span>
  postgresql:
    container_name: temporal<span class="hljs-operator">-</span>postgresql
    environment:
      POSTGRES_PASSWORD: temporal
      POSTGRES_USER: temporal
    image: postgres:<span class="hljs-number">9.6</span>
    networks:
      <span class="hljs-operator">-</span> temporal<span class="hljs-operator">-</span>network
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">5432</span>:<span class="hljs-number">5432</span>
  temporal:
    container_name: temporal
    depends_on:
      <span class="hljs-operator">-</span> postgresql
      <span class="hljs-operator">-</span> elasticsearch
    environment:
      <span class="hljs-operator">-</span> DB<span class="hljs-operator">=</span>postgresql
      <span class="hljs-operator">-</span> DB_PORT<span class="hljs-operator">=</span><span class="hljs-number">5432</span>
      <span class="hljs-operator">-</span> POSTGRES_USER<span class="hljs-operator">=</span>temporal
      <span class="hljs-operator">-</span> POSTGRES_PWD<span class="hljs-operator">=</span>temporal
      <span class="hljs-operator">-</span> POSTGRES_SEEDS<span class="hljs-operator">=</span>postgresql
      <span class="hljs-operator">-</span> DYNAMIC_CONFIG_FILE_PATH<span class="hljs-operator">=</span>config<span class="hljs-operator">/</span>dynamicconfig<span class="hljs-operator">/</span>development_es.yaml
      <span class="hljs-operator">-</span> ENABLE_ES<span class="hljs-operator">=</span><span class="hljs-literal">true</span>
      <span class="hljs-operator">-</span> ES_SEEDS<span class="hljs-operator">=</span>elasticsearch
      <span class="hljs-operator">-</span> ES_VERSION<span class="hljs-operator">=</span>v7
    image: temporalio<span class="hljs-operator">/</span>auto<span class="hljs-operator">-</span>setup:<span class="hljs-number">1.13</span><span class="hljs-number">.1</span>
    networks:
      <span class="hljs-operator">-</span> temporal<span class="hljs-operator">-</span>network
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">7233</span>:<span class="hljs-number">7233</span>
    volumes:
      <span class="hljs-operator">-</span> ./dynamicconfig:<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>temporal<span class="hljs-operator">/</span>config<span class="hljs-operator">/</span>dynamicconfig
  temporal<span class="hljs-operator">-</span>admin<span class="hljs-operator">-</span>tools:
    container_name: temporal<span class="hljs-operator">-</span>admin<span class="hljs-operator">-</span>tools
    depends_on:
      <span class="hljs-operator">-</span> temporal
    environment:
      <span class="hljs-operator">-</span> TEMPORAL_CLI_ADDRESS<span class="hljs-operator">=</span>temporal:<span class="hljs-number">7233</span>
    image: temporalio<span class="hljs-operator">/</span>admin<span class="hljs-operator">-</span>tools:<span class="hljs-number">1.13</span><span class="hljs-number">.1</span>
    networks:
      <span class="hljs-operator">-</span> temporal<span class="hljs-operator">-</span>network
    stdin_open: <span class="hljs-literal">true</span>
    tty: <span class="hljs-literal">true</span>
  temporal<span class="hljs-operator">-</span>web:
    container_name: temporal<span class="hljs-operator">-</span>web
    depends_on:
      <span class="hljs-operator">-</span> temporal
    environment:
      <span class="hljs-operator">-</span> TEMPORAL_GRPC_ENDPOINT<span class="hljs-operator">=</span>temporal:<span class="hljs-number">7233</span>
      <span class="hljs-operator">-</span> TEMPORAL_PERMIT_WRITE_API<span class="hljs-operator">=</span><span class="hljs-literal">true</span>
    image: temporalio<span class="hljs-operator">/</span>web:<span class="hljs-number">1.13</span><span class="hljs-number">.0</span>
    networks:
      <span class="hljs-operator">-</span> temporal<span class="hljs-operator">-</span>network
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">8088</span>:<span class="hljs-number">8088</span>
networks:
  temporal<span class="hljs-operator">-</span>network:
    driver: bridge
  intranet:
volumes:
  esdata:
    driver: local</span>
</code></pre><p><strong>Export the environment variables in terminal :</strong></p>
<pre><code><span class="hljs-built_in">export</span> TEMPORAL_HOSTPORT=localhost:7233
<span class="hljs-built_in">export</span> GOOGLE_APPLICATION_CREDENTIALS={{path of your SPN File}}
</code></pre><p><strong>Run the docker-compose file to start the temporal :</strong></p>
<pre><code>docker<span class="hljs-operator">-</span>compose <span class="hljs-operator">-</span>f .local/quickstart.yml up <span class="hljs-operator">-</span><span class="hljs-operator">-</span>build <span class="hljs-operator">-</span><span class="hljs-operator">-</span>force<span class="hljs-operator">-</span>recreate <span class="hljs-operator">-</span>d
</code></pre><p><strong>Perfect!! We are all set now. Let’s run this project:</strong></p>
<pre><code><span class="hljs-keyword">go</span> run main.<span class="hljs-keyword">go</span>
</code></pre><p>And I can see an Engine instance has been created and the APIs are running and the temporal is started as a thread:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657806739695/BHShQPmV7.png" alt="1_3ed-kX1d3dRkYBR1DolVmg.png" /></p>
<pre><code>                    Running Gin <span class="hljs-keyword">server</span>…
</code></pre><p>And Even the Temporal UI is on http://localhost:8088</p>
<p>Let’s hit our POST API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657806581842/S0cQQcjXa.png" alt="1_pSSUFWSCk3PYe_GnQpClAw.png" /></p>
<p>So, It’s a SUCCESS!</p>
<p>The Workflow is completed and IamBinding is Done is GCP also.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657806518050/PFoSXCFNq.png" alt="1_RjJwj44_MKtPq5sN57jEHg.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657806397064/jt7Mtmh7_.png" alt="1_hLk5MttBI9WSlX21fn-ZKw.png" /></p>
<p>If you find any kind of difficulty following the above steps, please check this repo and run:</p>
<p>git clone https://github.com/venkateshsuresh/temporal-iamBinding-GCP-workflow.git</p>
<p>I hope this article helped you. Thanks for reading and stay tuned!</p>
<p><a target="_blank" href="linkedin.com/in/venkatesh-c-s-603102173/">Venkatesh</a></p>
<p><a target="_blank" href="https://www.bootlabs.in/">BootLabs</a></p>
]]></content:encoded></item><item><title><![CDATA[Generic way to rotate secrets and passwords irrespective of any public cloud provider.]]></title><description><![CDATA[Nowadays everybody is shifting their infrastructure to cloud environments either from on premise or from cloud to cloud due to cost, scalability, higher availability or maybe for other 100s of benefit.
And to manage all the infrastructure under a hoo...]]></description><link>https://blog.bootlabstech.com/generic-way-to-rotate-secrets-and-passwords-irrespective-of-any-public-cloud-provider</link><guid isPermaLink="true">https://blog.bootlabstech.com/generic-way-to-rotate-secrets-and-passwords-irrespective-of-any-public-cloud-provider</guid><category><![CDATA[AWS]]></category><category><![CDATA[Azure]]></category><category><![CDATA[secretmanager]]></category><category><![CDATA[Vault]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Mon, 04 Jul 2022 09:26:59 GMT</pubDate><content:encoded><![CDATA[<p>Nowadays everybody is shifting their infrastructure to cloud environments either from on premise or from cloud to cloud due to cost, scalability, higher availability or maybe for other 100s of benefit.</p>
<p>And to manage all the infrastructure under a hood one can use terraform for the purpose.</p>
<p><strong>Problem statement</strong>: When we think of implementing the secrets and passwords rotation, we usually see the solutions that are cloud specific like rotation in aws secret manager, azure vault etc. These solutions work only when the infrastructure secrets is in one specific cloud.</p>
<p>So to tackle this situation we can create the secrets through terraform with the below resource blocks:</p>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"random_password"</span> <span class="hljs-string">"create_password” {
  length = 10
  upper  = true
  lower  = true
  special          = true
  override_special = "</span>!<span class="hljs-comment">#&amp;-_+?"</span>
  keepers = { 
     <span class="hljs-attribute">time</span> = time_rotating.example.id 
  }
}
</code></pre><p>The random_password block will create the password as per our required parameters like password length, using special character etc.
<em>Keepers</em>: keepers block hold the <em>time_rotating</em> resource reference in the random resource block.</p>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"time_rotating"</span> <span class="hljs-string">"example"</span> {
  <span class="hljs-attribute">rotation_days</span> = <span class="hljs-number">2</span> 
}
</code></pre><p>With above resource you can setup the rotation on monthly, hourly or minutes basis.</p>
<p>Please check for other required rotation options from official documentation page.
https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating</p>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"aws_secretsmanager_secret"</span> <span class="hljs-string">"rds_postgress"</span> {
  <span class="hljs-attribute">name</span>        = <span class="hljs-string">"test"</span>
  description = <span class="hljs-string">"temp secret"</span>
}
</code></pre><pre><code>resource <span class="hljs-string">"aws_secretsmanager_secret_version"</span> <span class="hljs-string">"rds_postgress_passwd"</span> {
  secret_id     <span class="hljs-operator">=</span> aws_secretsmanager_secret.rds_postgress.id
  secret_string <span class="hljs-operator">=</span> <span class="hljs-operator">&lt;</span><span class="hljs-operator">&lt;</span>EOF
{
  <span class="hljs-string">"username"</span>: <span class="hljs-string">"${random_password.create_password.result}"</span>
}
EOF
}
</code></pre><p>This block will create the secret in AWS secret manager service.
You can use any other service to setup the random passwords as well as per your requirements.</p>
<p>From the above example passwords will be rotated after 2 days and to make this possible please run the <strong>Terraform apply</strong> command again after 2 days as the password will change only after running the terraform apply command or setup the terraform apply in the respective scheduled pipelines.</p>
<p>Hope the above example gives clarity on rotating passwords using terraform.</p>
<p>Thank You…</p>
<p>Mohit Huria</p>
<p><a target="_blank" href="linkedin.com/in/mohit-huria-515648b5">Mohit-LinkedIn</a></p>
<p><a target="_blank" href="https://www.bootlabs.in/">Bootlabs</a></p>
]]></content:encoded></item><item><title><![CDATA[AKS Security Practices | Access Control using RBAC with Terraform Code | Part 1]]></title><description><![CDATA[Author : Aravinda Kumar
(LinkedIn )                                              
When you create an AKS cluster to be used by multiple developers from different product teams, access to the api server
has to be carefully managed. At the same time ac...]]></description><link>https://blog.bootlabstech.com/aks-security-practices-or-access-control-using-rbac-with-terraform-code-or-part-1</link><guid isPermaLink="true">https://blog.bootlabstech.com/aks-security-practices-or-access-control-using-rbac-with-terraform-code-or-part-1</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[golang]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[BootLabs]]></dc:creator><pubDate>Thu, 26 May 2022 14:05:28 GMT</pubDate><content:encoded><![CDATA[<p><strong><em>Author :</em></strong> Aravinda Kumar
(<a target="_blank" href="https://www.linkedin.com/in/aravinda-kumar/">LinkedIn</a> )                                              </p>
<p>When you create an AKS cluster to be used by multiple developers from different product teams, access to the api server
has to be carefully managed. At the same time access should not be restrictive in any way, especially 
with respect to K8S. In this AKS series, we'll be looking at different operational solutions for AKS.</p>
<p>This first part will help you define a workflow for user access control to the api server as shown below. Example 
Terraform code is available for all configurations.</p>
<p>You can find the source code in this repository https://github.com/aravindarc/aks-access-control</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653572756025/5xCeJ4e88.png" alt="image.png" /></p>
<h2 id="heading-cluster-creation">Cluster creation</h2>
<blockquote>
<p>caution</p>
<p>This code creates a public cluster with default network configurations. When you create a cluster, always create a 
private cluster with proper network configurations.</p>
</blockquote>
<pre><code>resource <span class="hljs-string">"azurerm_kubernetes_cluster"</span> <span class="hljs-string">"aks1"</span> {
  name                <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.aks_name
  location            <span class="hljs-operator">=</span> azurerm_resource_group.aks-rg.location
  resource_group_name <span class="hljs-operator">=</span> azurerm_resource_group.aks-rg.<span class="hljs-built_in">name</span>
  dns_prefix          <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.aks_dns_prefix

  default_node_pool {
    name       <span class="hljs-operator">=</span> <span class="hljs-string">"default"</span>
    node_count <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.aks_default_node_pool_count
    vm_size    <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.aks_default_vm_size
  }

  azure_active_directory_role_based_access_control {
    managed                <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
    admin_group_object_ids <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.aks_admin_group_object_ids
    azure_rbac_enabled     <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
  }

  identity {
    <span class="hljs-keyword">type</span> <span class="hljs-operator">=</span> <span class="hljs-string">"SystemAssigned"</span>
  }
}

resource <span class="hljs-string">"azurerm_role_assignment"</span> <span class="hljs-string">"admin"</span> {
  for_each <span class="hljs-operator">=</span> toset(<span class="hljs-keyword">var</span>.aks_admin_group_object_ids)
  scope <span class="hljs-operator">=</span> azurerm_kubernetes_cluster.aks1.id
  role_definition_name <span class="hljs-operator">=</span> <span class="hljs-string">"Azure Kubernetes Service Cluster User Role"</span>
  principal_id <span class="hljs-operator">=</span> each.<span class="hljs-built_in">value</span>
}

resource <span class="hljs-string">"azurerm_role_assignment"</span> <span class="hljs-string">"namespace-groups"</span> {
  for_each <span class="hljs-operator">=</span> toset(<span class="hljs-keyword">var</span>.ad_groups)
  scope <span class="hljs-operator">=</span> azurerm_kubernetes_cluster.aks1.id
  role_definition_name <span class="hljs-operator">=</span> <span class="hljs-string">"Azure Kubernetes Service Cluster User Role"</span>
  principal_id <span class="hljs-operator">=</span> azuread_group.groups[each.<span class="hljs-built_in">value</span>].id
}
</code></pre><pre><code>
resource <span class="hljs-string">"azurerm_resource_group"</span> <span class="hljs-string">"aks-rg"</span> {
  name     <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.resource_group_name
  location <span class="hljs-operator">=</span> <span class="hljs-keyword">var</span>.resource_group_location
}
</code></pre><pre><code>variable <span class="hljs-string">"resource_group_name"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"resource group name"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"resource_group_location"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"resource group location"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"aks_name"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"aks name"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"aks_dns_prefix"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"aks dns prefix"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"aks_default_node_pool_count"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"aks default node pool count"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> number
}

variable <span class="hljs-string">"aks_default_vm_size"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"aks default vm size"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"aks_admin_group_object_ids"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"aks admin group ids"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> list(<span class="hljs-keyword">string</span>)
}
</code></pre><pre><code><span class="hljs-attr">resource_group_name</span>         = <span class="hljs-string">"aks-resources"</span>
<span class="hljs-attr">resource_group_location</span>     = <span class="hljs-string">"Central India"</span>
<span class="hljs-attr">aks_name</span>                    = <span class="hljs-string">"aks1"</span>
<span class="hljs-attr">aks_dns_prefix</span>              = <span class="hljs-string">"aks1"</span>
<span class="hljs-attr">aks_default_node_pool_count</span> = <span class="hljs-number">2</span>
<span class="hljs-attr">aks_default_vm_size</span>         = <span class="hljs-string">"Standard_D2_v2"</span>
<span class="hljs-attr">aks_admin_group_object_ids</span>  = [<span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>]
</code></pre><p>The block <code>azure_active_directory_role_based_access_control</code> manages the cluster's rbac, the key <code>admin_group_object_ids</code>
is used to configure the ops group with admin access.</p>
<blockquote>
<p>info</p>
<p>Whether it be admin access or restricted access, all principals have to be provided with <code>Azure Kubernetes Service 
Cluster User Role</code>. Only then the users will be able to list and get credentials of the cluster.</p>
</blockquote>
<h2 id="heading-groups-creation">Groups Creation</h2>
<p>We'll create one AD group per k8s namespace, users of the group will be given access to one particular Namespace in the 
AKS cluster.</p>
<p>Once the group is created we have to create a Role and RoleBinding with the subject as the AD group.</p>
<pre><code>resource <span class="hljs-string">"azuread_group"</span> <span class="hljs-string">"groups"</span> {
  for_each         <span class="hljs-operator">=</span> toset(<span class="hljs-keyword">var</span>.ad_groups)
  display_name     <span class="hljs-operator">=</span> each.<span class="hljs-built_in">value</span>
  owners           <span class="hljs-operator">=</span> [data.azuread_client_config.current.object_id]
  security_enabled <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
}
</code></pre><pre><code>variable <span class="hljs-string">"ad_groups"</span> {
  description <span class="hljs-operator">=</span> <span class="hljs-string">"ad groups to be used in aks rolebindings"</span>
  <span class="hljs-keyword">type</span>        <span class="hljs-operator">=</span> list(<span class="hljs-keyword">string</span>)
}
</code></pre><pre><code><span class="hljs-attr">ad_groups</span>                   = [<span class="hljs-string">"product1"</span>, <span class="hljs-string">"product2"</span>]
</code></pre><p>This will create the Azure AD groups, it is a good convention to use the same name for the AD group and the K8S 
Namespace.</p>
<h2 id="heading-k8s-manifests">K8S Manifests</h2>
<p>We have to create a Role and RoleBinding in the namespace. This K8S manifest cannot be added to the application specific
helm chart. This has to be executed with admin rights. I have used helm to install the </p>
<pre><code>{{<span class="hljs-bullet">-</span> <span class="hljs-string">range</span> <span class="hljs-string">.Values.namespaces</span> }}
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Namespace</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.name</span> }}
<span class="hljs-meta">---</span>
{{<span class="hljs-bullet">-</span> <span class="hljs-string">end</span> }}
</code></pre><pre><code>{{<span class="hljs-operator">-</span> range .Values.namespaces }}
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: {{ .<span class="hljs-built_in">name</span> }}<span class="hljs-operator">-</span>user<span class="hljs-operator">-</span>full<span class="hljs-operator">-</span>access
  namespace: {{ .<span class="hljs-built_in">name</span> }}
rules:
<span class="hljs-operator">-</span> apiGroups: [<span class="hljs-string">""</span>, <span class="hljs-string">"extensions"</span>, <span class="hljs-string">"apps"</span>]
  resources: [<span class="hljs-string">"*"</span>]
  verbs: [<span class="hljs-string">"*"</span>]
<span class="hljs-operator">-</span> apiGroups: [<span class="hljs-string">"batch"</span>]
  resources:
  <span class="hljs-operator">-</span> jobs
  <span class="hljs-operator">-</span> cronjobs
  verbs: [<span class="hljs-string">"*"</span>]
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
{{<span class="hljs-operator">-</span> end }}
</code></pre><pre><code>{{<span class="hljs-operator">-</span> range .Values.namespaces }}
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: {{ .<span class="hljs-built_in">name</span> }}<span class="hljs-operator">-</span>user<span class="hljs-operator">-</span>access
  namespace: {{ .<span class="hljs-built_in">name</span> }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: {{ .<span class="hljs-built_in">name</span> }}<span class="hljs-operator">-</span>user<span class="hljs-operator">-</span>full<span class="hljs-operator">-</span>access
subjects:
<span class="hljs-operator">-</span> kind: Group
  namespace: {{ .<span class="hljs-built_in">name</span> }}
  name: {{ .objectid }}
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
{{<span class="hljs-operator">-</span> end }}
</code></pre><blockquote>
<p>tip</p>
<p>You can use terraform outputs to output the group names and their object-ids, and use it in helm command with <code>--set</code> flag to do a seamless
integration. Here I am just hard-coding the namespaces in the values.yaml.</p>
</blockquote>
<pre><code><span class="hljs-attribute">namespaces</span>:
  - <span class="hljs-attribute">name</span>: <span class="hljs-string">"product1"</span>
    <span class="hljs-attribute">objectid</span>: <span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>
  - <span class="hljs-attribute">name</span>: <span class="hljs-string">"product2"</span>
    <span class="hljs-attribute">objectid</span>: <span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>
</code></pre><p>Now you're all set. Add users to the appropriate product group and try accessing the cluster, below I am trying to 
access the product1 namespace using a service principal that I created and added into the product1 AD group.</p>
<pre><code><span class="hljs-operator">&gt;</span> az login <span class="hljs-operator">-</span><span class="hljs-operator">-</span>service<span class="hljs-operator">-</span>principal <span class="hljs-operator">-</span>u 00000000<span class="hljs-operator">-</span>0000<span class="hljs-operator">-</span>0000<span class="hljs-operator">-</span>0000<span class="hljs-operator">-</span>000000000000 <span class="hljs-operator">-</span>p <span class="hljs-string">'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>tenant 00000000<span class="hljs-operator">-</span>0000<span class="hljs-operator">-</span>0000<span class="hljs-operator">-</span>0000<span class="hljs-operator">-</span>000000000000 <span class="hljs-operator">-</span><span class="hljs-operator">-</span>allow<span class="hljs-operator">-</span>no<span class="hljs-operator">-</span>subscriptions
[
  {
    <span class="hljs-string">"cloudName"</span>: <span class="hljs-string">"AzureCloud"</span>,
    <span class="hljs-string">"homeTenantId"</span>: <span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>,
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>,
    <span class="hljs-string">"isDefault"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"managedByTenants"</span>: [],
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"Pay-As-You-Go"</span>,
    <span class="hljs-string">"state"</span>: <span class="hljs-string">"Enabled"</span>,
    <span class="hljs-string">"tenantId"</span>: <span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>,
    <span class="hljs-string">"user"</span>: {
      <span class="hljs-string">"name"</span>: <span class="hljs-string">"00000000-0000-0000-0000-000000000000"</span>,
      <span class="hljs-string">"type"</span>: <span class="hljs-string">"servicePrincipal"</span>
    }
  }
]
<span class="hljs-operator">&gt;</span> az aks get<span class="hljs-operator">-</span>credentials <span class="hljs-operator">-</span><span class="hljs-operator">-</span>resource<span class="hljs-operator">-</span>group aks<span class="hljs-operator">-</span>resources <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name aks1 <span class="hljs-operator">-</span><span class="hljs-operator">-</span>overwrite<span class="hljs-operator">-</span>existing
Merged <span class="hljs-string">"aks1"</span> <span class="hljs-keyword">as</span> current context in <span class="hljs-operator">/</span>Users<span class="hljs-operator">/</span>aravindarc<span class="hljs-operator">/</span>.kube/config
<span class="hljs-operator">&gt;</span> kubelogin remove<span class="hljs-operator">-</span>tokens
<span class="hljs-operator">&gt;</span> kubelogin convert<span class="hljs-operator">-</span>kubeconfig <span class="hljs-operator">-</span>l azurecli
<span class="hljs-operator">&gt;</span> kubectl get po <span class="hljs-operator">-</span>n product1
No resources found in product1 namespace.
</code></pre><p>But when I try to access something from default namespace, I will be blocked.</p>
<pre><code>&gt; kubectl <span class="hljs-keyword">get</span> po -n <span class="hljs-keyword">default</span>
Error <span class="hljs-keyword">from</span> <span class="hljs-keyword">server</span> (Forbidden): pods <span class="hljs-keyword">is</span> forbidden: <span class="hljs-keyword">User</span> "00000000-0000-0000-0000-000000000000" cannot list resource "pods" <span class="hljs-keyword">in</span> API <span class="hljs-keyword">group</span> "" <span class="hljs-keyword">in</span> the namespace "default": <span class="hljs-keyword">User</span> does <span class="hljs-keyword">not</span> have <span class="hljs-keyword">access</span> <span class="hljs-keyword">to</span> the resource <span class="hljs-keyword">in</span> Azure. <span class="hljs-keyword">Update</span> <span class="hljs-keyword">role</span> assignment <span class="hljs-keyword">to</span> allow <span class="hljs-keyword">access</span>.
</code></pre><h2 id="heading-next-step">Next Step</h2>
<p>In the next post we'll cover the network level security measures that one should take with respect to AKS. </p>
<p>hang on!</p>
]]></content:encoded></item></channel></rss>