<?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[Aditya Samant]]></title><description><![CDATA[A passionate software professional with a knack for exploring and sharing my insights in the tech world.]]></description><link>https://blog.adityasamant.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 17 May 2026 02:26:07 GMT</lastBuildDate><atom:link href="https://blog.adityasamant.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Istio Documentation - Local Build and Deploy]]></title><description><![CDATA[Istio is the most popular service mesh in the industry today. It is backed by a comprehensive documentation hosted at https://istio.io/latest/docs/
Like any open source project, Istio's documentation relies heavily on the contributions from the commu...]]></description><link>https://blog.adityasamant.dev/istio-documentation-local-build-and-deploy</link><guid isPermaLink="true">https://blog.adityasamant.dev/istio-documentation-local-build-and-deploy</guid><category><![CDATA[#istio]]></category><category><![CDATA[documentation]]></category><category><![CDATA[istio service mesh]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Tue, 02 Apr 2024 06:42:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/zIwAchjDirM/upload/0664dc83ebaea7392a287ab176db3dbb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://istio.io/latest/">Istio</a> is the most popular <a target="_blank" href="https://istio.io/latest/about/service-mesh/#what-is-a-service-mesh">service mesh</a> in the industry today. It is backed by a comprehensive documentation hosted at <a target="_blank" href="https://istio.io/latest/docs/">https://istio.io/latest/docs/</a></p>
<p>Like any open source project, Istio's documentation relies heavily on the contributions from the community. If you are passionate about documentation and would like to make a contribution, the first step is to set up the documentation site locally. In this article, we will learn the basic steps to achieve this.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p><a target="_blank" href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">Git</a></p>
</li>
<li><p><a target="_blank" href="https://docs.docker.com/desktop/">Docker Desktop</a></p>
</li>
</ul>
<h2 id="heading-local-setup">Local Setup</h2>
<h3 id="heading-fork-the-repository">Fork the repository</h3>
<p>Create a <a target="_blank" href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo">fork</a> of the <a target="_blank" href="https://github.com/istio/istio.io">istio/istio.io</a> repository</p>
<h3 id="heading-clone-the-fork">Clone the fork</h3>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Use the url of your forked repo</div>
</div>

<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/adityasamant25/istio.io
</code></pre>
<h3 id="heading-create-a-branch">Create a branch</h3>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">'issue-10' has been used as an example for the branch name</div>
</div>

<pre><code class="lang-bash">// Navigate to the project directory
<span class="hljs-built_in">cd</span> istio.io
git checkout -b issue-10
</code></pre>
<h3 id="heading-deploy-the-website-locally">Deploy the website locally</h3>
<p>Istio provides a Docker image with all the tools needed, including <code>Hugo</code> which is the site generator. To build and publish the website locally within the docker container, execute the command below:</p>
<pre><code class="lang-bash">make serve
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">In case you receive an error stating <em>"Failed to read Git log: fatal: detected dubious ownership in repository at '/work'"</em>, execute the below command locally and then run the <code>make serve</code> command again.</div>
</div>

<pre><code class="lang-bash">git config --global --add safe.directory /work
</code></pre>
<p>Once the website builds successfully, you should see the following message:</p>
<pre><code class="lang-plaintext">Built in 18037 ms
Environment: "development"
Serving pages from disk
Web Server is available at http://localhost:1313/latest/ (bind address 0.0.0.0)
</code></pre>
<p>Access the URL <a target="_blank" href="http://localhost:1313/latest/">http://localhost:1313/latest/</a> from within a browser. The Istio documentation is now available to you locally.</p>
<h3 id="heading-contribute-changes">Contribute changes</h3>
<p>The documentation for the English language is available at the following path:</p>
<pre><code class="lang-plaintext">/content/en/docs
</code></pre>
<p>Navigate to the .md file you need to edit. Make the appropriate changes and save the file. Saving the changes automatically triggers a redeployment of the site. The browser refreshes automatically and this enables you to immediately validate your changes locally.</p>
<p>Once you are satisfied with your changes, perform the following steps:</p>
<ul>
<li><p>Commit the changes to your local branch</p>
<pre><code class="lang-bash">  git add &lt;file paths&gt;
  git commit -m <span class="hljs-string">"Fixed issue 10"</span>
</code></pre>
</li>
<li><p>Push the changes to a branch on your fork</p>
<pre><code class="lang-plaintext">  git push origin issue-10
</code></pre>
</li>
<li><p>Raise a pull request from your branch to the istio.io master branch</p>
</li>
</ul>
<p>After you raise a pull request, wait for a maintainer to review the changes. In case of any review comments, incorporate the changes and push a new commit.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Hope this article helps in getting you started on your journey to contribute to Istio documentation. In case of any issues, please post a reply and I will try to assist.</p>
]]></content:encoded></item><item><title><![CDATA[MacOS: Revamp your Zsh terminal]]></title><description><![CDATA[As software professionals, we love to make things look cool, don't we? Imagine you are presenting a demo to a large audience and all they are seeing is a black and white terminal. Well, that would be hard to follow and many would lose interest. Inste...]]></description><link>https://blog.adityasamant.dev/macos-revamp-your-zsh-terminal</link><guid isPermaLink="true">https://blog.adityasamant.dev/macos-revamp-your-zsh-terminal</guid><category><![CDATA[zsh]]></category><category><![CDATA[oh-my-zsh]]></category><category><![CDATA[terminal]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Tue, 19 Mar 2024 11:07:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4Mw7nkQDByk/upload/30f30f89c88f2513171e2d82c6cdc5b4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As software professionals, we love to make things look cool, don't we? Imagine you are presenting a demo to a large audience and all they are seeing is a black and white terminal. Well, that would be hard to follow and many would lose interest. Instead, if you could present a terminal filled with colours, autocompletion and some syntax highlighting, wouldn't that be great?</p>
<p>Well, what are we waiting for? Let's go ahead and revamp our Zsh terminal.</p>
<h2 id="heading-oh-my-zsh">Oh My Zsh</h2>
<p><a target="_blank" href="https://github.com/ohmyzsh/ohmyzsh">Oh My Zsh</a> (<em>pronounced oh my zee shell</em>) is an open source, community-driven framework for managing your zsh configuration.</p>
<blockquote>
<p>Oh My Zsh will not make you a 10x developer...but you may feel like one.</p>
</blockquote>
<div data-node-type="callout">
<div data-node-type="callout-emoji">❗</div>
<div data-node-type="callout-text">Make a backup of your existing .zshrc file before executing any step in this lab.</div>
</div>

<p>Install <code>Oh My Zsh</code></p>
<pre><code class="lang-bash">sh -c <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)</span>"</span>
</code></pre>
<p>As soon as the installation is complete, you will see a hint of colour on your terminal.</p>
<p><code>Oh My Zsh</code> creates a new <code>.zshrc</code> file. Copy the custom alias, paths and secrets from your backup <code>.zshrc</code> file and paste it at the end of the new file.</p>
<p>Save the file and source it.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> ~/.zshrc
</code></pre>
<h2 id="heading-powerlevel10k">Powerlevel10k</h2>
<p><a target="_blank" href="https://github.com/romkatv/powerlevel10k">Powerlevel10k</a> is a theme for Zsh. It emphasizes speed, flexibility and out-of-the-box experience. It boosts the terminal with a nice new font and enables a fully customizable look and feel.</p>
<p>Install the <code>Meslo Nerd Font</code> by following the instructions mentioned <a target="_blank" href="https://github.com/romkatv/powerlevel10k?tab=readme-ov-file#manual-font-installation">here</a>.</p>
<p>After installing the Font, Open <em>Terminal → Settings → Profiles → Text</em>, click <em>Change</em> under <em>Font</em> and select <code>MesloLGS NF</code> family. If you're unable to spot the Font, ensure you select "All Fonts" under the "Collection" option. <em>Refer to the picture below.</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710843340338/95c7d4b1-c71f-46cc-8a52-99ccaf2faf11.png" alt class="image--center mx-auto" /></p>
<p>Install <code>Powerlevel10k</code> using the command below:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> --depth=1 https://github.com/romkatv/powerlevel10k.git <span class="hljs-variable">${ZSH_CUSTOM:-<span class="hljs-variable">$HOME</span>/.oh-my-zsh/custom}</span>/themes/powerlevel10k
</code></pre>
<p>Next step is to use the <code>Powerlevel10k</code> these for <code>Oh My Zsh</code>. To do that, open the <code>~/.zshrc</code> file and edit the value of the existing field <code>ZSH_THEME</code> from <code>robbyrussell</code> to <code>powerlevel10k/powerlevel10k</code> as follows:</p>
<pre><code class="lang-bash">ZSH_THEME=<span class="hljs-string">"powerlevel10k/powerlevel10k"</span>
</code></pre>
<p>After installation, restart <code>Zsh</code> using the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">exec</span> zsh
</code></pre>
<p>This will open the <code>Powerlevel10k</code> configuration wizard.</p>
<p>Here are the options that worked best for me:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Prompt</td><td>Answer (Option entered)</td></tr>
</thead>
<tbody>
<tr>
<td>Does this look like a diamond (rotated square)?</td><td>Yes (y)</td></tr>
<tr>
<td>Does this look like a lock?</td><td>Yes (y)</td></tr>
<tr>
<td>Does this look like an upward arrow?</td><td>Yes (y)</td></tr>
<tr>
<td>Do all these icons fit between the crosses?</td><td>Yes (y)</td></tr>
<tr>
<td>Prompt Style</td><td>Lean (1)</td></tr>
<tr>
<td>Character Set</td><td>ASCII (2)</td></tr>
<tr>
<td>Prompt Colors</td><td>256 (1)</td></tr>
<tr>
<td>Show current time?</td><td>No (n)</td></tr>
<tr>
<td>Prompt Height</td><td>One line (1)</td></tr>
<tr>
<td>Prompt Spacing</td><td>Compact (1)</td></tr>
<tr>
<td>Prompt Flow</td><td>Fluent (2)</td></tr>
<tr>
<td>Enable Transient Prompt</td><td>No (n)</td></tr>
<tr>
<td>Instant Prompt Mode</td><td>Verbose (1)</td></tr>
<tr>
<td>Apply changes to ~/.zshrc</td><td>Yes (y)</td></tr>
</tbody>
</table>
</div><p>Try a command such as</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/Documents
</code></pre>
<p>You will see the an immediate change:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710845418620/c56760db-c95d-4b04-bc18-a6a674f6d44b.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-zsh-autosuggestions">zsh-autosuggestions</h2>
<p><a target="_blank" href="https://github.com/zsh-users/zsh-autosuggestions">zsh-autosuggestions</a> suggests commands as you type based on history and completions.</p>
<p>Install the <code>zsh-autosuggestions</code> plugin using the following command:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/zsh-users/zsh-autosuggestions <span class="hljs-variable">${ZSH_CUSTOM:-~/.oh-my-zsh/custom}</span>/plugins/zsh-autosuggestions
</code></pre>
<p>Add the plugin to the list of existing plugins for <code>Oh My Zsh</code> to load (inside <code>~/.zshrc</code>)</p>
<pre><code class="lang-bash">plugins=(git zsh-autosuggestions)
</code></pre>
<p>Source the <code>.zshrc</code> using the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> ~/.zshrc
</code></pre>
<p>Try to type some commands you have used before and you will see that the terminal suggests an auto-completion. To choose the suggested option, simply click the right arrow key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710845344001/34d44d7f-0074-4d2e-931b-ebe621c6810a.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-zsh-syntax-highlighting">zsh-syntax-highlighting</h2>
<p><a target="_blank" href="https://github.com/zsh-users/zsh-syntax-highlighting">zsh-syntax-highlighting</a> provides syntax highlighting for <code>Zsh</code>. It enables highlighting of commands whilst they are typed at a <code>Zsh</code> prompt into an interactive terminal. This helps in reviewing commands before running them, particularly in catching syntax errors.</p>
<p>Install the <code>zsh-syntax-highlighting</code> plugin using the following command:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/zsh-users/zsh-syntax-highlighting.git <span class="hljs-variable">${ZSH_CUSTOM:-~/.oh-my-zsh/custom}</span>/plugins/zsh-syntax-highlighting
</code></pre>
<p>Add the plugin to the list of existing plugins for <code>Oh My Zsh</code> to load (inside <code>~/.zshrc</code>)</p>
<pre><code class="lang-bash">plugins=(git zsh-autosuggestions zsh-syntax-highlighting)
</code></pre>
<p>Source the <code>.zshrc</code> using the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> ~/.zshrc
</code></pre>
<p>Try to type some commands. You will find that the terminal highlights valid commands in green and invalid commands in red.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710845454420/9324a672-7d08-440b-91d9-845cf0213f4c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710845486105/18ee8443-a3a6-4c4d-b858-e5fa11ed9c32.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-kubernetes-joy">Kubernetes Joy</h2>
<p>For Kubernetes users, as soon as you type <code>kubectl</code> , the active context from the <code>kubeconfig</code> file is shown at the right of the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710845761623/d3966a66-0b5f-47bb-8582-7bcaf2c93cb7.png" alt class="image--center mx-auto" /></p>
<p>If the current namespace is anything other than default, it shows the custom namespace as well. In the below example, the context name is <code>istio</code> and the namespace is <code>temp</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710845917713/2cda427d-7754-4f2c-8f88-b6832c7f46e3.png" alt class="image--center mx-auto" /></p>
<p>Wow, now that's an awesome upgrade!</p>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Immutable ConfigMaps and Secrets in Kubernetes: A Complete Guide]]></title><description><![CDATA[ConfigMaps and Secrets are resources in Kubernetes that are used to govern configuration for applications. ConfigMaps maintain non-sensitive data whereas Secrets hold sensitive data like credentials and passwords.
In Kubernetes 1.21, a feature for Im...]]></description><link>https://blog.adityasamant.dev/immutable-configmaps-and-secrets-in-kubernetes-a-complete-guide</link><guid isPermaLink="true">https://blog.adityasamant.dev/immutable-configmaps-and-secrets-in-kubernetes-a-complete-guide</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[configmap]]></category><category><![CDATA[secrets]]></category><category><![CDATA[immutable]]></category><category><![CDATA[configuration]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Wed, 06 Mar 2024 11:51:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/SYTO3xs06fU/upload/ed68e66c2380fea30998b363fae2bf45.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://kubernetes.io/docs/concepts/configuration/configmap/">ConfigMaps</a> and <a target="_blank" href="https://kubernetes.io/docs/concepts/configuration/secret/">Secrets</a> are resources in Kubernetes that are used to govern configuration for applications. ConfigMaps maintain non-sensitive data whereas Secrets hold sensitive data like credentials and passwords.</p>
<p>In Kubernetes 1.21, a feature for Immutability of <a target="_blank" href="https://kubernetes.io/docs/concepts/configuration/configmap/#configmap-immutable">ConfigMaps</a> and <a target="_blank" href="https://kubernetes.io/docs/concepts/configuration/secret/#secret-immutable">Secrets</a> was GA. What exactly is this concept and its purpose? This article has the answers.</p>
<h1 id="heading-concept">Concept</h1>
<p>Kubernetes 1.21 was released back in 2021, and it provided an option to mark ConfigMaps and Secrets as immutable. This meant that once created, the data in these objects could not be altered.</p>
<h1 id="heading-purpose">Purpose</h1>
<p>Some would question the need for this feature. Since ConfigMaps and Secrets deal with configuration, and configuration is subject to change, why would we need such a feature?</p>
<h3 id="heading-performance">Performance</h3>
<p>It is important to understand that the <code>kubelet</code> on each node watches over each ConfigMap and Secret and periodically syncs its information to the Pods via the <code>kube-apiserver</code>. Large scale production deployments can have tens of thousands of such ConfigMaps and Secrets and obviously there is a cost associated with continuously keeping a watch for changes.</p>
<p>ConfigMaps and Secrets containing data that is constant over time can be marked as <code>immutable</code>. The kubelet does not watch over these resources. This in turn reduces the load on the kube-apiserver, saves compute resources and effectively improves the performance of the cluster.</p>
<h3 id="heading-security-and-reliability">Security and Reliability</h3>
<p>As the data for immutable ConfigMaps and Secrets cannot be altered, it can protect the sensitive and critical configuration from accidental (or unwanted) updates that could cause application outages.</p>
<p>It provides an additional layer of security against malicious actors attempting to manipulate data.</p>
<h3 id="heading-consistency">Consistency</h3>
<p>Immutability is ideal for scenarios where you want to ensure a consistent configuration across multiple Pods and Deployments.</p>
<h1 id="heading-definition">Definition</h1>
<p>Immutable ConfigMaps and Secrets are defined by setting the immutable field to true.</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">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-string">...</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-string">...</span>
<span class="hljs-attr">immutable:</span> <span class="hljs-literal">true</span>
</code></pre>
<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-string">...</span>
<span class="hljs-attr">data:</span> 
  <span class="hljs-string">...</span>
<span class="hljs-attr">immutable:</span> <span class="hljs-literal">true</span>
</code></pre>
<h1 id="heading-example">Example</h1>
<p>Let's check an example of an Immutable ConfigMap that is mounted as a Volume in a Pod. We will also demonstrate how to update configuration driven by an Immutable ConfigMap.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">⚠</div>
<div data-node-type="callout-text"><em>WARNING: Immutable ConfigMaps and Secrets are especially used for configuration which is constant and is not expected to change over time. In a production use-case, it is advisable to not change the contents of Immutable ConfigMaps and Secrets unless absolutely necessary.</em></div>
</div>

<p><strong>Prerequisites</strong></p>
<ol>
<li><p>A local Kubernetes cluster such as <a target="_blank" href="https://minikube.sigs.k8s.io/docs/start/">minikube</a>, <a target="_blank" href="https://kind.sigs.k8s.io/">kind</a>, <a target="_blank" href="https://k3s.io/">K3s</a> or <a target="_blank" href="https://microk8s.io/">MicroK8s</a>.</p>
</li>
<li><p>The <a target="_blank" href="https://kubernetes.io/docs/reference/kubectl/">kubectl</a> command line tool to interact with the Kubernetes cluster.</p>
</li>
</ol>
<p>An example manifest for an Immutable ConfigMap is shown below:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">season:</span> <span class="hljs-string">"summer"</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">immutable:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">season</span>
</code></pre>
<p>Create the Immutable ConfigMap:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/kubernetes/configmaps/examples/immutable-configmaps.yaml
</code></pre>
<p>Below is an example of a Deployment manifest with the Immutable ConfigMap season mounted as a volume into the Pod's only container:</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">immutable-configmap-volume</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">immutable-configmap-volume</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">immutable-configmap-volume</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">immutable-configmap-volume</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">alpine</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">alpine:3</span>
          <span class="hljs-attr">command:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">/bin/sh</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">-c</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">while</span> <span class="hljs-literal">true</span><span class="hljs-string">;</span> <span class="hljs-string">do</span> <span class="hljs-string">echo</span> <span class="hljs-string">"$(date) My preferred season is $(cat /etc/config/season)"</span><span class="hljs-string">;</span>
              <span class="hljs-string">sleep</span> <span class="hljs-number">10</span><span class="hljs-string">;</span> <span class="hljs-string">done;</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">config-volume</span>
              <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/config</span>
      <span class="hljs-attr">volumes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config-volume</span>
          <span class="hljs-attr">configMap:</span>
            <span class="hljs-attr">name:</span> <span class="hljs-string">season</span>
</code></pre>
<p>Create the Deployment:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/kubernetes/configmaps/examples/deployment-with-immutable-configmap-as-volume.yaml
</code></pre>
<p>Check the Deployment:</p>
<pre><code class="lang-plaintext">kubectl get deployment immutable-configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME               READY   UP-TO-DATE   AVAILABLE   AGE
configmap-volume   3/3     3            3           19s
</code></pre>
<p>Check the pods for this Deployment (matching by selector<a target="_blank" href="https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/">)</a>:</p>
<pre><code class="lang-plaintext">kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                                          READY   STATUS    RESTARTS   AGE
immutable-configmap-volume-5c677f6494-n7cg5   1/1     Running   0          30s
immutable-configmap-volume-5c677f6494-sdhkp   1/1     Running   0          30s
immutable-configmap-volume-5c677f6494-vsszf   1/1     Running   0          30s
</code></pre>
<p>The Pod's container refers to the data defined in the ConfigMap and uses it to print a report to stdout. You can check this report by viewing the logs for one of the Pods in that Deployment:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Pick one Pod that belongs to the Deployment, and view its logs</span>
kubectl logs deployments/immutable-configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">Found 3 pods, using pod/immutable-configmap-volume-5c677f6494-n7cg5
Tue Mar  5 11:38:27 UTC 2024 My preferred season is summer
Tue Mar  5 11:38:37 UTC 2024 My preferred season is summer
Tue Mar  5 11:38:47 UTC 2024 My preferred season is summer
Tue Mar  5 11:38:57 UTC 2024 My preferred season is summer
Tue Mar  5 11:39:07 UTC 2024 My preferred season is summer
</code></pre>
<p>Edit the ConfigMap:</p>
<pre><code class="lang-plaintext">kubectl edit configmap season
</code></pre>
<p>In the editor that appears, change the value of key season from summer to winter.</p>
<p>Here's an example of how that manifest could look after you edit it:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">season:</span> <span class="hljs-string">winter</span>
<span class="hljs-attr">immutable:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-comment"># You can leave the existing metadata as they are.</span>
<span class="hljs-comment"># The values you'll see won't exactly match these.</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">kubectl.kubernetes.io/last-applied-configuration:</span> <span class="hljs-string">|
      {"apiVersion":"v1","data":{"season":"summer"},"immutable":true,"kind":"ConfigMap","metadata":{"annotations":{},"name":"season","namespace":"default"}}      
</span>  <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-string">"2024-03-05T11:36:22Z"</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">season</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">resourceVersion:</span> <span class="hljs-string">"3840"</span>
  <span class="hljs-attr">uid:</span> <span class="hljs-string">10cdc5e6-47b8-4473-a9f3-ed401c2073f9</span>
</code></pre>
<p>Attempt to save the changes. You should see that the save fails and a temporary manifest is opened which contains the reason for failure:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># reopened with the relevant failures.</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># configmaps "season" was not valid:</span>
<span class="hljs-comment"># * data: Forbidden: field is immutable when `immutable` is set</span>
<span class="hljs-comment">#</span>
</code></pre>
<p>Without any further change, save the temporary manifest. You should see an output similar to:</p>
<pre><code class="lang-plaintext">A copy of your changes has been stored to "/var/folders/nn/bb6y1j8d69n9lhqppjlrfthm0000gn/T/kubectl-edit-139284375.yaml"
error: Edit cancelled, no valid changes were saved.
</code></pre>
<p>Once a ConfigMap is marked as immutable, it is not possible to revert this change nor to mutate the contents of the <code>data</code> or the <code>binaryData</code> field. In order to modify the contents, the only option is to delete and recreate the ConfigMap.</p>
<p>Delete and recreate the ConfigMap season by using the temporary manifest as follows:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Use the appropriate path to the temporary manifest</span>
kubectl replace --force -f /var/folders/nn/bb6y1j8d69n9lhqppjlrfthm0000gn/T/kubectl-edit-139284375.yaml
</code></pre>
<p>Tail (follow the latest entries in) the logs of one of the pods that belongs to this Deployment:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># As the text explains, the output does NOT change</span>
kubectl logs deployments/immutable-configmap-volume --follow
</code></pre>
<p>Notice that the output remains <strong><em>unchanged</em></strong>, even though you recreated the ConfigMap:</p>
<pre><code class="lang-bash">Tue Mar  5 12:02:07 UTC 2024 My preferred season is summer
Tue Mar  5 12:02:17 UTC 2024 My preferred season is summer
Tue Mar  5 12:02:27 UTC 2024 My preferred season is summer
Tue Mar  5 12:02:37 UTC 2024 My preferred season is summer
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">⚠</div>
<div data-node-type="callout-text"><em>CAUTION: Although the ConfigMap has been recreated, the existing Pods maintain a mount point to the deleted ConfigMap. To force an update, you would need to clean up every existing reference to the old ConfigMap. In this scenario, it equates to deleting the Deployment and recreating it. Exercise caution before executing these steps in a production environment.</em></div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">🗒</div>
<div data-node-type="callout-text"><em>A simple restart of the existing Deployment using the kubectl rollout restart command causes the Pods to be recreated, but is insufficient to refresh the contents of the ConfigMap.</em></div>
</div>

<p>Delete the Deployment:</p>
<pre><code class="lang-plaintext">kubectl delete deployment immutable-configmap-volume
</code></pre>
<p>Wait for all the Pods to terminate. Verify that the Pods have terminated using the following command:</p>
<pre><code class="lang-plaintext">kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">No resources found in default namespace.
</code></pre>
<p>Recreate the Deployment:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://k8s.io/examples/deployments/deployment-with-immutable-configmap-as-volume.yaml
</code></pre>
<p>Next, check the Deployment:</p>
<pre><code class="lang-plaintext">kubectl get deployment immutable-configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
immutable-configmap-volume   3/3     3            3           6s
</code></pre>
<p>Check the Pods:</p>
<pre><code class="lang-plaintext">kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                                          READY   STATUS    RESTARTS   AGE
immutable-configmap-volume-5c677f6494-dxjwg   1/1     Running   0          31s
immutable-configmap-volume-5c677f6494-k54fc   1/1     Running   0          31s
immutable-configmap-volume-5c677f6494-w9b4c   1/1     Running   0          31s
</code></pre>
<p>View the logs for a Pod in this Deployment:</p>
<pre><code class="lang-plaintext"># Pick one Pod that belongs to the Deployment, and view its logs
kubectl logs deployment/immutable-configmap-volume
</code></pre>
<p>You should see an output similar to the below:</p>
<pre><code class="lang-plaintext">Found 3 pods, using pod/immutable-configmap-volume-5c677f6494-dxjwg
Tue Mar  5 12:40:43 UTC 2024 My preferred season is winter
Tue Mar  5 12:40:53 UTC 2024 My preferred season is winter
Tue Mar  5 12:41:03 UTC 2024 My preferred season is winter
</code></pre>
<h1 id="heading-summary">Summary</h1>
<p>Once a ConfigMap is marked as immutable, it is not possible to revert this change nor to mutate the contents of the <code>data</code> or the <code>binaryData</code> field. You can only delete and recreate the ConfigMap. Because existing resources maintain a mount point to the deleted ConfigMap, it is necessary to ensure that every existing reference to the old ConfigMap (not just Pods, any reference) has been cleaned up.</p>
<h1 id="heading-cleaning-up">Cleaning up</h1>
<p>Delete the resources created during the tutorial:</p>
<pre><code class="lang-plaintext">kubectl delete deployment immutable-configmap-volume
kubectl delete configmap season
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Users, Groups, Roles and API Access in Kubernetes]]></title><description><![CDATA[This blog post describes the nuances of how users and groups are configured in Kubernetes and how the role-based access control (RBAC) mechanism applies for them.
We will also dive into the usage of the kubectl command line tool to check API access i...]]></description><link>https://blog.adityasamant.dev/users-groups-roles-and-api-access-in-kubernetes</link><guid isPermaLink="true">https://blog.adityasamant.dev/users-groups-roles-and-api-access-in-kubernetes</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[rbac]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Tue, 05 Mar 2024 05:48:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/376KN_ISplE/upload/7af14198aae84d1a97b02e882f3dc4f8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog post describes the nuances of how <code>users</code> and <code>groups</code> are configured in Kubernetes and how the <code>role-based access control</code> (RBAC) mechanism applies for them.</p>
<p>We will also dive into the usage of the <code>kubectl</code> command line tool to check API access in Kubernetes. It especially focuses on the difference between the <code>--user</code> and <code>--as</code> options in <code>kubectl</code><em>.</em></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">This is a hands-on article. You may choose to follow along with the article or view the video on Youtube</div>
</div>

<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/I-iVrIWfMl8?si=PsM2iqvMbxCe98c2">https://youtu.be/I-iVrIWfMl8?si=PsM2iqvMbxCe98c2</a></div>
<p> </p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<p>You need to have a Kubernetes cluster, and the <a target="_blank" href="https://kubernetes.io/docs/tasks/tools/#kubectl">kubectl</a> command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one using <a target="_blank" href="https://minikube.sigs.k8s.io/docs/start/">minikube</a>.</p>
<p><a target="_blank" href="https://www.openssl.org/source/">OpenSSL</a> command line utility will be used to view the x509 certificates.</p>
<h1 id="heading-role-based-access-control">Role-based access control</h1>
<p>Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within your organisation.</p>
<p>You may choose to go through the <a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">Kubernetes RBAC documentation</a> before reading ahead.</p>
<h1 id="heading-checking-api-access">Checking API Access</h1>
<p>The <code>kubectl auth can-i</code> command can be used to determine whether a user has permissions to execute a certain action.</p>
<h2 id="heading-scenario-for-the-default-admin-user"><strong>Scenario for the Default Admin User</strong></h2>
<p>In the first example, we will work with the default admin user.</p>
<p>Check the contexts available in the minikube cluster:</p>
<pre><code class="lang-plaintext">kubectl config get-contexts minikube
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">CURRENT   NAME       CLUSTER    AUTHINFO   NAMESPACE
*         minikube   minikube   minikube   default
</code></pre>
<p>Check the name of the admin user that is created with our basic minikube installation:</p>
<pre><code class="lang-plaintext">kubectl config view -o jsonpath='{.contexts[?(@.name=="minikube")].context.user}'
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">minikube
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>There is no entity or resource named </em><code>User</code><em> in Kubernetes. A user in Kubernetes is nothing but a key and certificate pair issued by the Kubernetes cluster and presented to the Kubernetes API.</em></div>
</div>

<p>Check the certificate of the minikube user:</p>
<pre><code class="lang-plaintext">kubectl config view -o jsonpath='{.users[?(@.name=="minikube")].user.client-certificate}'
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">/Users/adityasamant/.minikube/profiles/minikube/client.crt
</code></pre>
<p>View the Subject of this certificate (use the path generated in the previous command):</p>
<pre><code class="lang-plaintext">openssl x509 -in /Users/adityasamant/.minikube/profiles/minikube/client.crt -text -noout | grep Subject | grep -v "Public Key Info"
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">Subject: O=system:masters, CN=minikube-user
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>CN is the name of the user and O is the group that this user will belong to. As can be seen above, the minikube admin user (marked by CN) is part of the system:masters group (marked by O).</em></div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>system:masters is a group which is hardcoded into the Kubernetes API server source code as having unrestricted rights to the Kubernetes API server. Any user who is a member of this group has full cluster-admin rights to the cluster. Even if every cluster role and role is deleted from the cluster, users who are members of this group retain full access to the cluster.</em></div>
</div>

<p>Use the <code>kubectl auth can-i</code> command to verify a few scenarios.</p>
<p>Check permissions to create pods:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to create deployments:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create deployments
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to delete secrets:</p>
<pre><code class="lang-plaintext">kubectl auth can-i delete secrets
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<h2 id="heading-scenario-for-a-normal-user"><strong>Scenario for a Normal User</strong></h2>
<p>Configure a normal user and verify how the <code>kubectl auth can-i</code> commands can be used to check the access. To do this we need to <a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user">issue a certificate</a> <a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user">for the user.</a></p>
<p>Create a private key and a csr file:</p>
<pre><code class="lang-plaintext">openssl genrsa -out jane.key 2048
openssl req -new -key jane.key -out jane.csr -subj "/CN=jane"
</code></pre>
<p>This will generate a private key named <code>jane.key</code> and a certificate signing request named <code>jane.csr</code><em>.</em></p>
<p>Get the base64 encoded value of the CSR file content:</p>
<pre><code class="lang-plaintext">cat jane.csr | base64 | tr -d "\n"
</code></pre>
<p>Create a CertificateSigningRequest:</p>
<pre><code class="lang-yaml"><span class="hljs-string">cat</span> <span class="hljs-string">&lt;&lt;EOF</span> <span class="hljs-string">|</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-bullet">-</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">certificates.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">CertificateSigningRequest</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">jane</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">request:</span> <span class="hljs-string">&lt;base64</span> <span class="hljs-string">encoded</span> <span class="hljs-string">csr&gt;</span>
  <span class="hljs-attr">signerName:</span> <span class="hljs-string">kubernetes.io/kube-apiserver-client</span>
  <span class="hljs-attr">expirationSeconds:</span> <span class="hljs-number">86400</span>  <span class="hljs-comment"># one day</span>
  <span class="hljs-attr">usages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">client</span> <span class="hljs-string">auth</span>
<span class="hljs-string">EOF</span>
</code></pre>
<p>Get the CSR:</p>
<pre><code class="lang-plaintext">kubectl get csr jane
</code></pre>
<pre><code class="lang-plaintext">NAME   AGE   SIGNERNAME                            REQUESTOR       REQUESTEDDURATION   CONDITION
jane   55s   kubernetes.io/kube-apiserver-client   minikube-user   24h                 Pending
</code></pre>
<p>Approve the CSR:</p>
<pre><code class="lang-plaintext">kubectl certificate approve jane
</code></pre>
<pre><code class="lang-plaintext">kubectl get csr jane
</code></pre>
<pre><code class="lang-plaintext">NAME   AGE     SIGNERNAME                            REQUESTOR       REQUESTEDDURATION   CONDITION
jane   2m17s   kubernetes.io/kube-apiserver-client   minikube-user   24h                 Approved,Issued
</code></pre>
<h2 id="heading-granting-permissions-via-rbac"><strong>Granting permissions via RBAC</strong></h2>
<p>Create a <code>clusterrole</code> granting permissions to only create pods:</p>
<pre><code class="lang-plaintext">kubectl create clusterrole createpods --verb=create --resource=pods
</code></pre>
<pre><code class="lang-plaintext">clusterrole.rbac.authorization.k8s.io/createpods created
</code></pre>
<p>Create a <code>clusterrolebinding</code> to bind the <code>clusterrole</code> with user <code>jane</code>:</p>
<pre><code class="lang-plaintext">kubectl create clusterrolebinding createpods --clusterrole=createpods --user=jane
</code></pre>
<pre><code class="lang-plaintext">clusterrolebinding.rbac.authorization.k8s.io/createpods created
</code></pre>
<h3 id="heading-difference-between-as-and-user-options-of-kubectl"><strong>Difference between ‘--as’ and ‘--user’ options of kubectl</strong></h3>
<p><code>kubectl</code> has a number of global <code>options</code> that can be passed as an argument to any <code>kubectl</code> command. The list can be found with the following command:</p>
<pre><code class="lang-plaintext">kubectl options
</code></pre>
<p>Two of the options are <code>--user</code> and <code>--as</code><em>.</em> It is important to understand the difference between them.  </p>
<pre><code class="lang-plaintext">--user='':
The name of the kubeconfig user to use

--as='':
Username to impersonate for the operation. 
User could be a regular user or a service account in a namespace.
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>The --user option is used when you want to trigger the kubectl command under the context of a user which is configured in the kubeconfig file. This option throws an error if the user is not present in the kubeconfig file.</em></div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>The --as option is used to impersonate any user or serviceaccount. --as can be used for a user irrespective of whether that user is present in the kubeconfig file or not.</em></div>
</div>

<p>Let’s put the theory into action with the help of the user we created.</p>
<h3 id="heading-using-as-to-check-access">Using ‘--as’ to Check Access</h3>
<p>Check permissions to create pods:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --as=jane
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to create deployments:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create deployments --as=jane
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p>Check permissions to delete secrets:</p>
<pre><code class="lang-plaintext">kubectl auth can-i delete secrets --as=jane
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p>Due to the fact that we explicitly assigned the <code>clusterrole createpods</code> to user <code>jane</code>, we see that <code>jane</code> has access to create pods, but no access to create deployments or delete secrets. Great, this is as expected.</p>
<h3 id="heading-using-user-to-check-access">Using ‘--user’ to Check Access</h3>
<p>Try the same commands, but this time using the <code>--user</code> option:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --user=jane
</code></pre>
<pre><code class="lang-plaintext">error: auth info "jane" does not exist
</code></pre>
<p>The command leads to an error. This is because we have not configured the user <code>jane</code> in the <code>kubeconfig</code> file.</p>
<p>We will fix this in the next step.</p>
<h3 id="heading-adding-the-user-to-kubeconfig">Adding the user to kubeconfig</h3>
<p>Get the user’s certificate:</p>
<pre><code class="lang-plaintext">kubectl get csr jane -o jsonpath='{.status.certificate}'| base64 -d &gt; jane.crt
</code></pre>
<p>Add the new credentials to kubeconfig:</p>
<pre><code class="lang-plaintext">kubectl config set-credentials jane --client-key=jane.key --client-certificate=jane.crt --embed-certs=true
</code></pre>
<pre><code class="lang-plaintext">User "jane" set.
</code></pre>
<p>Add the context to kubeconfig:</p>
<pre><code class="lang-plaintext">kubectl config set-context jane --cluster=minikube --user=jane
</code></pre>
<pre><code class="lang-plaintext">Context "jane" created.
</code></pre>
<p>Let’s try the <code>kubectl auth can-i</code> command once again to verify the permissions on user jane:</p>
<p>Check permissions to create pods:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --user=jane
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to create deployments:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create deployments --user=jane
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p>Check permissions to delete secrets:</p>
<pre><code class="lang-plaintext">kubectl auth can-i delete secrets --user=jane
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p>Now everything works as expected.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>The --as option does not check the actual presence of the user in the kubeconfig. It only checks the explicitly configured roles and bindings that are bound to a user and returns a response based on that.</em></div>
</div>

<h3 id="heading-example-for-a-non-existent-user">Example for a non-existent user</h3>
<p>If the <code>--as</code> option is used for a non-existent user, there is no error thrown as shown below.</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --as=nobody
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<h2 id="heading-scenario-for-a-custom-admin-user"><strong>Scenario for a Custom Admin User</strong></h2>
<p>Configure a new admin user and verify the behaviour of the <code>kubectl auth can-i</code> commands.</p>
<p>This time we will check the permissions that a <code>user</code> inherits via the <code>group</code> it is attached to.</p>
<p><a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user">Issue a certificate</a> for the new admin user.</p>
<p>Create a private key and a csr file:</p>
<pre><code class="lang-plaintext">openssl genrsa -out poweruser.key 2048
openssl req -new -key poweruser.key -out poweruser.csr -subj "/CN=poweruser/O=system:masters"
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>CN is the name of the user and O is the group that this user will belong to.</em></div>
</div>

<p>Create a CertificateSigningRequest:</p>
<pre><code class="lang-yaml"><span class="hljs-string">cat</span> <span class="hljs-string">&lt;&lt;EOF</span> <span class="hljs-string">|</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-bullet">-</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">certificates.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">CertificateSigningRequest</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">poweruser</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">request:</span> <span class="hljs-string">&lt;base64</span> <span class="hljs-string">encoded</span> <span class="hljs-string">csr&gt;</span>
  <span class="hljs-attr">signerName:</span> <span class="hljs-string">kubernetes.io/kube-apiserver-client</span>
  <span class="hljs-attr">expirationSeconds:</span> <span class="hljs-number">86400</span>  <span class="hljs-comment"># one day</span>
  <span class="hljs-attr">usages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">client</span> <span class="hljs-string">auth</span>
<span class="hljs-string">EOF</span>
</code></pre>
<p>The above command throws an error as the <code>kube-apiserver</code> blocks any <code>CertificateSigningRequest</code> that attempts to add a user as part of the <code>system:masters</code> group.</p>
<blockquote>
<p><em>Error from server (Forbidden): error when creating “STDIN”:</em> <a target="_blank" href="http://certificatesigningrequests.certificates.k8s.io"><em>certificatesigningrequests.certificates.k8s.io</em></a> <a target="_blank" href="http://certificatesigningrequests.certificates.k8s.io/"><em>“poweruser” is forbidden: use of</em></a> <a target="_blank" href="http://kubernetes.io/kube-apiserver-client"><em>kubernetes.io/kube-apiserver-client signer with system:masters group i</em></a><em>s not allowed</em></p>
</blockquote>
<h2 id="heading-granting-permissions-via-rbac-through-groups"><strong>Granting permissions via RBAC through groups</strong></h2>
<p>In order to create a new admin user we will create a custom admin group that replicates the behaviour of the <code>system:masters</code> group. Let’s call it <code>example:masters</code></p>
<p>To do this, create a new clusterrolebinding as below:</p>
<pre><code class="lang-yaml"><span class="hljs-string">cat</span> <span class="hljs-string">&lt;&lt;EOF</span> <span class="hljs-string">|</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-bullet">-</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">rbac.authorization.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRoleBinding</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">example-cluster-admin</span>
<span class="hljs-attr">roleRef:</span>
  <span class="hljs-attr">apiGroup:</span> <span class="hljs-string">rbac.authorization.k8s.io</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRole</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">cluster-admin</span>
<span class="hljs-attr">subjects:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroup:</span> <span class="hljs-string">rbac.authorization.k8s.io</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">Group</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">example:masters</span>
<span class="hljs-string">EOF</span>
</code></pre>
<p>The next step is to add the new admin user to the <code>example:masters</code> group.</p>
<p>Delete the previous files created for <code>poweruser</code>:</p>
<pre><code class="lang-plaintext">rm poweruser.key poweruser.csr
</code></pre>
<p>Create a new private key and a csr file:</p>
<pre><code class="lang-plaintext">openssl genrsa -out poweruser.key 2048
openssl req -new -key poweruser.key -out poweruser.csr -subj "/CN=poweruser/O=example:masters"
</code></pre>
<p>Create a CertificateSigningRequest:</p>
<pre><code class="lang-yaml"><span class="hljs-string">cat</span> <span class="hljs-string">&lt;&lt;EOF</span> <span class="hljs-string">|</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-bullet">-</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">certificates.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">CertificateSigningRequest</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">poweruser</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">request:</span> <span class="hljs-string">&lt;base64</span> <span class="hljs-string">encoded</span> <span class="hljs-string">csr&gt;</span>
  <span class="hljs-attr">signerName:</span> <span class="hljs-string">kubernetes.io/kube-apiserver-client</span>
  <span class="hljs-attr">expirationSeconds:</span> <span class="hljs-number">86400</span>  <span class="hljs-comment"># one day</span>
  <span class="hljs-attr">usages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">client</span> <span class="hljs-string">auth</span>
<span class="hljs-string">EOF</span>
</code></pre>
<p>Approve the CSR:</p>
<pre><code class="lang-plaintext">kubectl certificate approve poweruser
</code></pre>
<pre><code class="lang-plaintext">certificatesigningrequest.certificates.k8s.io/poweruser approved
</code></pre>
<p>Get the certificate:</p>
<pre><code class="lang-plaintext">kubectl get csr poweruser -o jsonpath='{.status.certificate}'| base64 -d &gt; poweruser.crt
</code></pre>
<p>View the Subject of this certificate:</p>
<pre><code class="lang-plaintext">openssl x509 -in poweruser.crt -text -noout | grep Subject | grep -v "Public Key Info"
</code></pre>
<pre><code class="lang-plaintext">Subject: O=example:masters, CN=poweruser
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>The above output shows that the user </em><code>poweruser</code><em> belongs to the </em><code>example:masters</code><em> group.</em></div>
</div>

<p>Add the new admin user to <code>kubeconfig</code>.</p>
<p>Add the new credentials:</p>
<pre><code class="lang-plaintext">kubectl config set-credentials poweruser --client-key=poweruser.key --client-certificate=poweruser.crt --embed-certs=true
</code></pre>
<pre><code class="lang-plaintext">User "poweruser" set.
</code></pre>
<p>Add the context:</p>
<pre><code class="lang-plaintext">kubectl config set-context poweruser --cluster=minikube --user=poweruser
</code></pre>
<pre><code class="lang-plaintext">Context "poweruser" created.
</code></pre>
<p>Try the <code>kubectl auth can-i</code> command to verify the permissions on the new admin user:</p>
<p>Check permissions to create pods:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --user=poweruser
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to create deployments:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create deployments --user=poweruser
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to delete secrets:</p>
<pre><code class="lang-plaintext">kubectl auth can-i delete secrets --user=poweruser
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Try the same commands but with using the <code>--as</code> option.</p>
<p>Check permissions to create pods:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --as=poweruser
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p>Check permissions to create deployments:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create deployments --as=poweruser
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p>Check permissions to delete secrets:</p>
<pre><code class="lang-plaintext">kubectl auth can-i delete secrets --as=poweruser
</code></pre>
<pre><code class="lang-plaintext">no
</code></pre>
<p><strong>Strange!!!</strong> The expected output was yes for all 3 commands, as <em>poweruser</em> is an admin user with full access to the cluster.</p>
<p>We can prove this as follows:</p>
<p>Switch the context to work with the <code>poweruser</code>:</p>
<pre><code class="lang-plaintext">kubectl config use-context poweruser
</code></pre>
<p>Create a pod:</p>
<pre><code class="lang-plaintext">kubectl run nginx --image=nginx
</code></pre>
<pre><code class="lang-plaintext">pod/nginx created
</code></pre>
<p>Create the deployment:</p>
<pre><code class="lang-plaintext">kubectl create deployment nginx-deploy --image=nginx
</code></pre>
<pre><code class="lang-plaintext">deployment.apps/nginx-deploy created
</code></pre>
<p>Create and delete a secret:</p>
<pre><code class="lang-plaintext">kubectl create secret generic test-secret --from-literal=secret=1234
</code></pre>
<pre><code class="lang-plaintext">secret/test-secret created
</code></pre>
<pre><code class="lang-plaintext">kubectl delete secrets test-secret
</code></pre>
<pre><code class="lang-plaintext">secret "test-secret" deleted
</code></pre>
<p>My first thought was that this is a defect in Kubernetes.</p>
<p>I raised issue <a target="_blank" href="https://github.com/kubernetes/kubernetes/issues/122579">#122579</a> to Kubernetes for confirmation.</p>
<p>The reality is that the behaviour is as-expected. The API server has no knowledge of group membership apart from what is encoded directly in the credential or provided by a token webhook.</p>
<p>To overcome this you need to pass the group you want to impersonate with the <code>--as-group</code> flag.  </p>
<h3 id="heading-the-as-group-option-of-kubectl">The ‘--as-group’ option of kubectl</h3>
<p>Try the same commands but this time append the <em>--as-group</em> option as well.</p>
<p>Check permissions to create pods:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create pods --as=poweruser --as-group=example:masters
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to create deployments:</p>
<pre><code class="lang-plaintext">kubectl auth can-i create deployments --as=poweruser --as-group=example:masters
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Check permissions to delete secrets:</p>
<pre><code class="lang-plaintext">kubectl auth can-i delete secrets --as=poweruser --as-group=example:masters
</code></pre>
<pre><code class="lang-plaintext">yes
</code></pre>
<p>Now the results are as expected.</p>
<h1 id="heading-summary">Summary</h1>
<p>The <code>kubectl auth can-i</code> command behaves differently for the <code>--user</code> and <code>--as</code> options.</p>
<p>The <code>--user</code> option checks for the actual presence of the user in the <code>kubeconfig</code> file and has the ability to check permissions derived from the group of the user.</p>
<p>The <code>--as</code> option can be used to check permissions for any user irrespective of its presence in the kubeconfig file. It checks permissions which are <strong><em>directly bound</em></strong> to the user through RBAC, and does not check permissions that are derived from the user’s group. The API server has no knowledge of group membership apart from whatever is encoded directly in the credential or provided by a token webhook.</p>
<p>The <code>--as-group</code> option should be used to check for permissions that are derived from the user’s group.</p>
<h1 id="heading-cleaning-up">Cleaning up</h1>
<p>Delete the resources created during this lab:</p>
<pre><code class="lang-plaintext">rm jane*
rm poweruser*
kubectl delete pod nginx
kubectl delete deployment nginx-deploy
</code></pre>
<p>Optionally, you can delete the entire minikube cluster:</p>
<pre><code class="lang-plaintext">minikube delete --all
</code></pre>
]]></content:encoded></item><item><title><![CDATA[ConfigMap Conundrum: Subtleties of Dynamic Updates in Kubernetes Configurations]]></title><description><![CDATA[ConfigMaps are a powerful tool in Kubernetes that help you (dynamically) update configuration within a Kubernetes deployment. In this tutorial, you will learn how to change the configuration for a running application. More importantly, you will under...]]></description><link>https://blog.adityasamant.dev/configmap-conundrum-subtleties-of-dynamic-updates-in-kubernetes-configurations</link><guid isPermaLink="true">https://blog.adityasamant.dev/configmap-conundrum-subtleties-of-dynamic-updates-in-kubernetes-configurations</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[configmap]]></category><category><![CDATA[configuration]]></category><category><![CDATA[configuration management]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Mon, 04 Mar 2024 10:35:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HaTIYO87qWQ/upload/a752328bc4c842dec449115c012f43e4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ConfigMaps are a powerful tool in Kubernetes that help you <em>(dynamically)</em> update configuration within a Kubernetes deployment. In this tutorial, you will learn how to change the configuration for a running application. More importantly, you will understand the subtle variation between ConfigMaps mounted as Volumes and ConfigMaps defined as environment variables.</p>
<h2 id="heading-objectives">Objectives</h2>
<ul>
<li><p>Update configuration via a ConfigMap mounted as a Volume</p>
</li>
<li><p>Update environment variables of a Pod via a ConfigMap</p>
</li>
<li><p>Learn the difference between the two</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ol>
<li><p>A local Kubernetes cluster such as <a target="_blank" href="https://minikube.sigs.k8s.io/docs/start/">minikube</a>, <a target="_blank" href="https://kind.sigs.k8s.io/">kind</a>, <a target="_blank" href="https://k3s.io/">K3s</a> or <a target="_blank" href="https://microk8s.io/">microk8s</a>.</p>
</li>
<li><p>The <a target="_blank" href="https://kubernetes.io/docs/tasks/tools/#kubectl">kubectl</a> command line tool to interact with the Kubernetes cluster.</p>
</li>
</ol>
<h2 id="heading-update-configuration-via-a-configmap-mounted-as-a-volume">Update configuration via a ConfigMap mounted as a Volume</h2>
<p>Use the <code>kubectl create configmap</code> command to create a ConfigMap from <a target="_blank" href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#create-configmaps-from-literal-values">literal values</a>:</p>
<pre><code class="lang-plaintext">kubectl create configmap sport --from-literal=sport=football
</code></pre>
<p>Below is an example of a Deployment manifest with the ConfigMap <em>sport</em> mounted as a <a target="_blank" href="https://kubernetes.io/docs/concepts/storage/volumes/">volume</a> into the Pod's only container.</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">configmap-volume</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">configmap-volume</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">configmap-volume</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">configmap-volume</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">alpine</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">alpine:3</span>
          <span class="hljs-attr">command:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">/bin/sh</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">-c</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">while</span> <span class="hljs-literal">true</span><span class="hljs-string">;</span> <span class="hljs-string">do</span> <span class="hljs-string">echo</span> <span class="hljs-string">"$(date) My preferred sport is $(cat /etc/config/sport)"</span><span class="hljs-string">;</span>
              <span class="hljs-string">sleep</span> <span class="hljs-number">10</span><span class="hljs-string">;</span> <span class="hljs-string">done;</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">config-volume</span>
              <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/config</span>
      <span class="hljs-attr">volumes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">config-volume</span>
          <span class="hljs-attr">configMap:</span>
            <span class="hljs-attr">name:</span> <span class="hljs-string">sport</span>
</code></pre>
<p>Create the Deployment:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/kubernetes/configmaps/examples/deployment-with-configmap-as-volume.yaml
</code></pre>
<p>Check the Deployment:</p>
<pre><code class="lang-plaintext">kubectl get deployment configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME               READY   UP-TO-DATE   AVAILABLE   AGE
configmap-volume   3/3     3            3           19s
</code></pre>
<p>Check the pods for this Deployment (matching by <a target="_blank" href="https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/">selector):</a></p>
<pre><code class="lang-plaintext">kubectl get pods --selector=app.kubernetes.io/name=configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                                READY   STATUS    RESTARTS   AGE
configmap-volume-6b976dfdcf-qxvbm   1/1     Running   0          72s
configmap-volume-6b976dfdcf-skpvm   1/1     Running   0          72s
configmap-volume-6b976dfdcf-tbc6r   1/1     Running   0          72s
</code></pre>
<p>On each node where one of these Pods is running, the <code>kubelet</code> fetches the data for that ConfigMap and translates it to files in a local volume. The <code>kubelet</code> then mounts that volume into the container, as specified in the Pod template. The code running in that container loads the information from the file and uses it to print a report to stdout. You can check this report by viewing the logs for one of the Pods in that Deployment:</p>
<pre><code class="lang-plaintext">kubectl logs deployments/configmap-volume
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">Found 3 pods, using pod/configmap-volume-76d9c5678f-x5rgj
Thu Jan  4 14:06:46 UTC 2024 My preferred sport is football
Thu Jan  4 14:06:56 UTC 2024 My preferred sport is football
Thu Jan  4 14:07:06 UTC 2024 My preferred sport is football
Thu Jan  4 14:07:16 UTC 2024 My preferred sport is football
Thu Jan  4 14:07:26 UTC 2024 My preferred sport is football
</code></pre>
<p>Edit the ConfigMap:</p>
<pre><code class="lang-plaintext">kubectl edit configmap sport
</code></pre>
<p>In the editor that appears, change the value of key <code>sport</code> from <code>football</code> to <code>cricket</code>. Save your changes. The kubectl tool updates the ConfigMap accordingly (if you see an error, try again).</p>
<p>Here's an example of how that manifest could look after you edit it:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">sport:</span> <span class="hljs-string">cricket</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-comment"># You can leave the existing metadata as they are.</span>
<span class="hljs-comment"># The values you'll see won't exactly match these.</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-string">"2024-01-04T14:05:06Z"</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">sport</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">resourceVersion:</span> <span class="hljs-string">"1743935"</span>
  <span class="hljs-attr">uid:</span> <span class="hljs-string">024ee001-fe72-487e-872e-34d6464a8a23</span>
</code></pre>
<p>You should see the following output:</p>
<pre><code class="lang-plaintext">configmap/sport edited
</code></pre>
<p>Tail (follow the latest entries in) the logs of one of the pods that belongs to this Deployment:</p>
<pre><code class="lang-plaintext">kubectl logs -f deployments/configmap-volume
</code></pre>
<p>After few seconds, you should see the log output change as follows:</p>
<pre><code class="lang-plaintext">Thu Jan  4 14:11:36 UTC 2024 My preferred sport is football
Thu Jan  4 14:11:46 UTC 2024 My preferred sport is football
Thu Jan  4 14:11:56 UTC 2024 My preferred sport is football
Thu Jan  4 14:12:06 UTC 2024 My preferred sport is cricket
Thu Jan  4 14:12:16 UTC 2024 My preferred sport is cricket
</code></pre>
<blockquote>
<p>When you have a ConfigMap that is mapped into a running Pod using either a <code>configMap</code> volume or a <code>projected</code> volume, and you update that ConfigMap, the running Pod sees the update almost immediately. However, your application only sees the change if it is written to either poll for changes, or watch for file updates. An application that loads its configuration once at startup will not notice a change.</p>
<p>The total delay from the moment when the ConfigMap is updated to the moment when new keys are projected to the Pod can be as long as kubelet sync period. Also check <a target="_blank" href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#mounted-configmaps-are-updated-automatically">Mounted ConfigMaps are updated automatically.</a></p>
</blockquote>
<h2 id="heading-update-environment-variables-of-a-pod-via-a-configmap">Update environment variables of a Pod via a ConfigMap</h2>
<p>Use the <em>kubectl create configmap</em> command to create a ConfigMap from <a target="_blank" href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#create-configmaps-from-literal-values">literal values:</a></p>
<pre><code class="lang-plaintext">kubectl create configmap fruits --from-literal=fruits=apples
</code></pre>
<p>Below is an example of a Deployment manifest with an environment variable configured via the ConfigMap <code>fruits</code>.</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">configmap-env-var</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">configmap-env-var</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">configmap-env-var</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app.kubernetes.io/name:</span> <span class="hljs-string">configmap-env-var</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">alpine</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">alpine:3</span>
          <span class="hljs-attr">env:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">FRUITS</span>
              <span class="hljs-attr">valueFrom:</span>
                <span class="hljs-attr">configMapKeyRef:</span>
                  <span class="hljs-attr">key:</span> <span class="hljs-string">fruits</span>
                  <span class="hljs-attr">name:</span> <span class="hljs-string">fruits</span>
          <span class="hljs-attr">command:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">/bin/sh</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">-c</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">while</span> <span class="hljs-literal">true</span><span class="hljs-string">;</span> <span class="hljs-string">do</span> <span class="hljs-string">echo</span> <span class="hljs-string">"$(date) The basket is full of $FRUITS"</span><span class="hljs-string">;</span>
                <span class="hljs-string">sleep</span> <span class="hljs-number">10</span><span class="hljs-string">;</span> <span class="hljs-string">done;</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>Create the Deployment:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/kubernetes/configmaps/examples/deployment-with-configmap-as-envvar.yaml
</code></pre>
<p>Check the Deployment:</p>
<pre><code class="lang-plaintext">kubectl get deployment configmap-env-var
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                READY   UP-TO-DATE   AVAILABLE   AGE
configmap-env-var   3/3     3            3           7s
</code></pre>
<p>Check the pods for this Deployment (matching by <a target="_blank" href="https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/">selector):</a></p>
<pre><code class="lang-plaintext">kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                                 READY   STATUS    RESTARTS   AGE
configmap-env-var-59cfc64f7d-74d7z   1/1     Running   0          46s
configmap-env-var-59cfc64f7d-c4wmj   1/1     Running   0          46s
configmap-env-var-59cfc64f7d-dpr98   1/1     Running   0          46s
</code></pre>
<p>The key-value pair in the ConfigMap is configured as an environment variable in the container of the Pod. Check this by viewing the logs of the Pod.</p>
<pre><code class="lang-plaintext">kubectl logs deployment/configmap-env-var
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">Found 3 pods, using pod/configmap-env-var-7c994f7769-l74nq
Thu Jan  4 16:07:06 UTC 2024 The basket is full of apples
Thu Jan  4 16:07:16 UTC 2024 The basket is full of apples
Thu Jan  4 16:07:26 UTC 2024 The basket is full of apples
</code></pre>
<p>Edit the ConfigMap:</p>
<pre><code class="lang-plaintext">kubectl edit configmap fruits
</code></pre>
<p>In the editor that appears, change the value of key <code>fruits</code> from <code>apples</code> to <code>mangoes</code>. Save your changes. The kubectl tool updates the ConfigMap accordingly (if you see an error, try again).</p>
<p>Here's an example of how that manifest could look after you edit it:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">fruits:</span> <span class="hljs-string">mangoes</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-comment"># You can leave the existing metadata as they are.</span>
<span class="hljs-comment"># The values you'll see won't exactly match these.</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-string">"2024-01-04T16:04:19Z"</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">fruits</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">resourceVersion:</span> <span class="hljs-string">"1749472"</span>
</code></pre>
<p>You should see the following output:</p>
<pre><code class="lang-plaintext">configmap/fruits edited
</code></pre>
<p>Tail the logs of the Deployment and observe the output for few seconds:</p>
<pre><code class="lang-plaintext">kubectl logs deployment/configmap-env-var
</code></pre>
<p>Notice that the output remains <code>unchanged</code>, even though you edited the ConfigMap:</p>
<pre><code class="lang-plaintext">Thu Jan  4 16:12:56 UTC 2024 The basket is full of apples
Thu Jan  4 16:13:06 UTC 2024 The basket is full of apples
Thu Jan  4 16:13:16 UTC 2024 The basket is full of apples
Thu Jan  4 16:13:26 UTC 2024 The basket is full of apples
</code></pre>
<blockquote>
<p>Although the value of the key inside the ConfigMap has changed, the environment variable in the Pod still shows the earlier value. This is because environment variables for a process running inside a Pod are <strong><em>not</em></strong> updated when the source data changes; if you wanted to force an update, you would need to have Kubernetes replace your existing Pods. The new Pods would then run with the updated information.</p>
</blockquote>
<p>You can trigger that replacement. Perform a rollout for the Deployment, using <a target="_blank" href="https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/">kubectl rollout:</a></p>
<pre><code class="lang-plaintext"># Trigger the rollout
kubectl rollout restart deployment configmap-env-var

# Wait for the rollout to complete
kubectl rollout status deployment configmap-env-var --watch=true
</code></pre>
<p>Next, check the Deployment:</p>
<pre><code class="lang-plaintext">kubectl get deployment configmap-env-var
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                READY   UP-TO-DATE   AVAILABLE   AGE
configmap-env-var   3/3     3            3           12m
</code></pre>
<p>Check the Pods:</p>
<pre><code class="lang-plaintext">kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
</code></pre>
<p>The rollout caused Kubernetes to make a new ReplicaSet for the Deployment; that means that the existing Pods were terminated, and new ones have been created. You should finally see an output similar to:</p>
<pre><code class="lang-plaintext">NAME                                 READY   STATUS        RESTARTS   AGE
configmap-env-var-6d94d89bf5-2ph2l   1/1     Running       0          13s
configmap-env-var-6d94d89bf5-74twx   1/1     Running       0          8s
configmap-env-var-6d94d89bf5-d5vx8   1/1     Running       0          11s
</code></pre>
<p>View the logs for a Pod in this Deployment:</p>
<pre><code class="lang-plaintext">kubectl logs deployment/configmap-env-var
</code></pre>
<p>You should see an output similar to the below:</p>
<pre><code class="lang-plaintext">Found 3 pods, using pod/configmap-env-var-6d9ff89fb6-bzcf6
Thu Jan  4 16:30:35 UTC 2024 The basket is full of mangoes
Thu Jan  4 16:30:45 UTC 2024 The basket is full of mangoes
Thu Jan  4 16:30:55 UTC 2024 The basket is full of mangoes
</code></pre>
<p>This demonstrates the scenario of updating environment variables in a Pod that are derived from a ConfigMap. Changes to the ConfigMap values are applied to the Pod during the subsequent rollout. If Pods get created for another reason, such as scaling up the Deployment, then the new Pods also use the latest configuration values; if you don't trigger a rollout, then you might find that your app is running with a mix of old and new environment variable values.</p>
<h2 id="heading-summary">Summary</h2>
<p>Changes to a ConfigMap mounted as a Volume on a Pod are available seamlessly after the subsequent kubelet sync.</p>
<p>Changes to a ConfigMap that configures environment variables for a Pod are available after the subsequent rollout for the Pod.</p>
<h2 id="heading-cleaning-up">Cleaning up</h2>
<p>Delete the resources created during the tutorial:</p>
<pre><code class="lang-plaintext">kubectl delete deployment configmap-volume configmap-env-var
kubectl delete configmap sport fruits
</code></pre>
]]></content:encoded></item><item><title><![CDATA[AWS Certified Solutions Architect Associate: Recipe for Success]]></title><description><![CDATA[The syllabus for the AWS Certified Solutions Architect Associate exam is huge. It involves many days and maybe even months of preparation before a candidate feels ready to appear for the exam.
As you progress through the course, it is critical to pre...]]></description><link>https://blog.adityasamant.dev/aws-certified-solutions-architect-associate-recipe-for-success</link><guid isPermaLink="true">https://blog.adityasamant.dev/aws-certified-solutions-architect-associate-recipe-for-success</guid><category><![CDATA[AWS]]></category><category><![CDATA[AWS Certified Solutions Architect Associate]]></category><category><![CDATA[SAA-C03]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Thu, 29 Feb 2024 11:50:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709207131315/768a060a-e758-4e6c-b56b-11eb4cc4efc5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The syllabus for the AWS Certified Solutions Architect Associate exam is huge. It involves many days and maybe even months of preparation before a candidate feels ready to appear for the exam.</p>
<p>As you progress through the course, it is critical to prepare notes along the way and use them to revise the important concepts a few days before the exam.</p>
<p>I am sharing the notes that I prepared during the many days of preparation for this exam. Hopefully these will assist you.</p>
<p>Please visit the link below to access the content:</p>
<p><a target="_blank" href="https://articles.adityasamant.dev/blog/aws/certifications/solutions-architect-associate/introduction/">AWS Certified Solutions Architect Associate - Important Concepts</a></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">A kind request to please share this post with your network if you found it useful.</div>
</div>]]></content:encoded></item><item><title><![CDATA[Low cost CVE scanning with Trivy]]></title><description><![CDATA[Introduction
In a world of microservices, a production grade enterprise application comprises of hundreds of docker images. Organisations and their customers have a high focus on the security of applications and one of the key requirements is to keep...]]></description><link>https://blog.adityasamant.dev/low-cost-cve-scanning-with-trivy</link><guid isPermaLink="true">https://blog.adityasamant.dev/low-cost-cve-scanning-with-trivy</guid><category><![CDATA[trivy]]></category><category><![CDATA[CVE]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Thu, 29 Feb 2024 09:03:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709197124750/61659989-8ed4-433d-97fc-5d284da7c4f9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In a world of microservices, a production grade enterprise application comprises of hundreds of docker images. Organisations and their customers have a high focus on the security of applications and one of the key requirements is to keep the count of <a target="_blank" href="https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures">Common Vulnerabilities and Exposures (CVEs)</a> to a minimum. Many organisations have strict policies that prevent a vulnerable image to be deployed on production environments. Furthermore, docker images are often made up of layers. So a CVE in one of the base layers will propagate to all images built using the particular base layer.</p>
<p>CVEs are a moving target. New CVEs are identified and detected by vulnerability scanners each day. This calls for a process that scans and fixes these vulnerabilities.</p>
<p>Docker images are immutable. It means that the only way to fix a Docker image is to build a new patch containing the fix. The last thing you want is to release a new build only to realize it contains a bunch of CRITICAL CVEs and is a NO-GO for production.</p>
<p>There are a number of CVE scanners available, however in this article we will use <a target="_blank" href="https://github.com/aquasecurity/trivy">Trivy</a> from Aqua which is a free and open-source vulnerability scanner for images.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">🗒</div>
<div data-node-type="callout-text">v0.49 is the latest version at the time of writing this article.</div>
</div>

<h2 id="heading-installation">Installation</h2>
<p>Installing CVE is trivial. Follow the <a target="_blank" href="https://aquasecurity.github.io/trivy/v0.49/getting-started/installation/">steps</a> for your platform of choice.</p>
<h2 id="heading-using-the-cli">Using the CLI</h2>
<p>If installed using a package manager or as a binary, <code>trivy</code> is available through a command line tool.</p>
<p>Use the following command to verify the installation:</p>
<pre><code class="lang-plaintext">trivy version
</code></pre>
<p>To demonstrate the command used for scanning, let's use the <code>python:3.4-alpine</code> image:</p>
<pre><code class="lang-plaintext">trivy image python:3.4-alpine
</code></pre>
<p>The command results in an output that reports the <em>CVEs</em> in the image, along with the ID, severity, description and a fixed version (if available).</p>
<pre><code class="lang-plaintext">python:3.4-alpine (alpine 3.9.2)
</code></pre>
<pre><code class="lang-plaintext">Total: 37 (UNKNOWN: 0, LOW: 4, MEDIUM: 16, HIGH: 13, CRITICAL: 4)
</code></pre>
<p>To capture the output in a file:</p>
<pre><code class="lang-plaintext">trivy image python:3.4-alpine &gt; report.txt
</code></pre>
<p>Generally, <code>CRITICAL</code> and <code>HIGH</code> severity CVEs are considered as blockers for a release. So you may want the output to be filtered on <code>CRITICAL</code> and <code>HIGH</code> CVEs only.</p>
<p>For that, use the <code>-s</code> option</p>
<pre><code class="lang-plaintext">trivy image -s CRITICAL,HIGH python:3.4-alpine
</code></pre>
<h2 id="heading-run-as-a-docker-image">Run as a Docker Image</h2>
<p>An alternative way, is to run <code>trivy</code> as a docker container.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">🗒</div>
<div data-node-type="callout-text">For scanning container images with trivy, mount <code>docker.sock</code> from the host into the <code>trivy</code> container.</div>
</div>

<pre><code class="lang-plaintext">docker run -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/Library/Caches:/root/.cache/ aquasec/trivy:0.49.1 image python:3.4-alpine
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>Using <code>trivy</code> is an easy and cost-effective way of scanning images for CVEs. Integrating it in CI/CD pipelines is recommended.</p>
]]></content:encoded></item><item><title><![CDATA[Up and Running with Istio within minutes]]></title><description><![CDATA[Istio Overview
Istio is an open source service mesh that layers transparently onto distributed applications running on Kubernetes. Istio's powerful features provide a uniform and more efficient way to secure, connect, and monitor services.
In this po...]]></description><link>https://blog.adityasamant.dev/up-and-running-with-istio-within-minutes</link><guid isPermaLink="true">https://blog.adityasamant.dev/up-and-running-with-istio-within-minutes</guid><category><![CDATA[#istio]]></category><category><![CDATA[istio service mesh]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Aditya Samant]]></dc:creator><pubDate>Thu, 29 Feb 2024 05:44:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709185045335/59756f8b-492d-48e2-ab91-8424add8a9d9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-istio-overview">Istio Overview</h1>
<p><a target="_blank" href="https://istio.io/">Istio</a> is an open source service mesh that layers transparently onto distributed applications running on <a target="_blank" href="https://kubernetes.io/">Kubernetes</a>. Istio's powerful features provide a uniform and more efficient way to secure, connect, and monitor services.</p>
<p>In this post, I'll explain the bare minimum steps needed in order to successfully install and test Istio on your local machine.</p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<ol>
<li><p>A local Kubernetes cluster such as <a target="_blank" href="https://minikube.sigs.k8s.io/docs/start/">minikube,</a> <a target="_blank" href="https://kind.sigs.k8s.io/">kind</a>, <a target="_blank" href="https://k3s.io/">K3s</a> or <a target="_blank" href="https://microk8s.io/">microk8s</a>. In this blog post I have chosen <code>minikube</code> with the use of the <a target="_blank" href="https://minikube.sigs.k8s.io/docs/commands/tunnel/"><code>minikube tunnel</code></a> to provide a load balancer for use by Istio.</p>
</li>
<li><p>The <a target="_blank" href="https://kubernetes.io/docs/tasks/tools/#kubectl">kubectl</a> command line tool to interact with the Kubernetes cluster.</p>
</li>
</ol>
<h1 id="heading-installation">Installation</h1>
<p>Create a new minikube cluster:</p>
<pre><code class="lang-plaintext">minikube start -p minikube-istio
</code></pre>
<p>Start a minikube tunnel to provide a load balancer for use by Istio:</p>
<pre><code class="lang-plaintext">minikube tunnel -p minikube-istio
</code></pre>
<p>Download Istio as per the instructions in <a target="_blank" href="https://istio.io/latest/docs/setup/getting-started/#download">this</a> link:</p>
<pre><code class="lang-plaintext">curl -L https://istio.io/downloadIstio | sh -
</code></pre>
<p>Add the istio-*/bin directory to your environment PATH variable and check the version:</p>
<pre><code class="lang-plaintext">istioctl version
</code></pre>
<p>The output should be similar to:</p>
<pre><code class="lang-plaintext">no ready Istio pods in "istio-system"
1.20.3
</code></pre>
<p>Install Istio with the <em>demo</em> profile:</p>
<pre><code class="lang-plaintext">istioctl install --set profile=demo
</code></pre>
<p>After successful installation, you should have an istio-system namespace available</p>
<pre><code class="lang-plaintext">kubectl get ns
</code></pre>
<p>You should see an output similar to:</p>
<pre><code class="lang-plaintext">NAME              STATUS   AGE
kube-system       Active   4m22s
default           Active   4m22s
kube-public       Active   4m22s
kube-node-lease   Active   4m22s
istio-system      Active   2m49s
</code></pre>
<p>Check the Pods inside the istio-system namespace:</p>
<pre><code class="lang-plaintext">kubectl get pods -n istio-system
</code></pre>
<p>You should see the <code>istiod</code>, <code>istio-ingressgateway</code> and the <code>istio-egressgateway</code> pods up and running.</p>
<h1 id="heading-automatic-injection-of-sidecars">Automatic Injection of Sidecars</h1>
<p>Automatic injection of sidecar containers is achieved by setting the label <code>istio-injection=enabled</code> on namespaces.</p>
<pre><code class="lang-plaintext">kubectl label ns default istio-injection=enabled
</code></pre>
<p>Create a simple deployment of nginx to test the sidecar injection:</p>
<pre><code class="lang-plaintext">kubectl create deploy my-nginx --image=nginx
</code></pre>
<p>Get the details of the pod:</p>
<pre><code class="lang-plaintext">kubectl get pod
</code></pre>
<p>You will see two containers for the Pod, as it includes the sidecar container:</p>
<pre><code class="lang-plaintext">NAME                      READY   STATUS    RESTARTS   AGE
my-nginx-b8dd4cd6-4kh4x   2/2     Running   0          24s
</code></pre>
<p>Delete this deployment as we do not need it anymore:</p>
<pre><code class="lang-plaintext">kubectl delete deploy my-nginx
</code></pre>
<h1 id="heading-demo-application">Demo Application</h1>
<p>We will deploy an application with two Spring Boot microservices named <a target="_blank" href="https://github.com/adityasamant25/customers"><code>customers</code></a> and <a target="_blank" href="https://github.com/adityasamant25/rest-client"><code>rest-client</code></a>.</p>
<p>The <code>customers</code> microservice is a CRUD microservice that provides APIs to manipulate customer data.</p>
<p>The <code>rest-client</code> microservice acts as a client to the <code>customers</code> microservice and invokes it using the new <a target="_blank" href="https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-restclient"><code>RestClient</code></a> in Spring Framework 6.1.x</p>
<p>Create the <a target="_blank" href="https://istio.io/latest/docs/reference/config/networking/gateway/">Gateway</a> Resource:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/istio/demo/gateway.yaml
</code></pre>
<p>Create the <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">Deployment</a> and <a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/service/">Service</a> for the <code>rest-client</code> application:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/istio/demo/rest-client.yaml
</code></pre>
<p>Create the Deployment and Service for the <code>customers</code> application:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/istio/demo/customers-v1.yaml
</code></pre>
<p>Create a <a target="_blank" href="https://istio.io/latest/docs/reference/config/networking/virtual-service/">Virtual Service</a> for the <code>rest-client</code> and bind it to the Gateway resource:</p>
<pre><code class="lang-plaintext">kubectl apply -f https://raw.githubusercontent.com/adityasamant25/courses/main/istio/demo/rest-client-vs.yaml
</code></pre>
<p>Run CURL against the following URL:</p>
<pre><code class="lang-plaintext">curl http://127.0.0.1/api/customers
</code></pre>
<p>You should see an output of 3 customers as follows:</p>
<pre><code class="lang-json">[
{<span class="hljs-attr">"id"</span>:<span class="hljs-number">1</span>,<span class="hljs-attr">"firstName"</span>:<span class="hljs-string">"John"</span>,<span class="hljs-attr">"lastName"</span>:<span class="hljs-string">"Doe"</span>},
{<span class="hljs-attr">"id"</span>:<span class="hljs-number">2</span>,<span class="hljs-attr">"firstName"</span>:<span class="hljs-string">"Alice"</span>,<span class="hljs-attr">"lastName"</span>:<span class="hljs-string">"Smith"</span>},
{<span class="hljs-attr">"id"</span>:<span class="hljs-number">3</span>,<span class="hljs-attr">"firstName"</span>:<span class="hljs-string">"Bob"</span>,<span class="hljs-attr">"lastName"</span>:<span class="hljs-string">"Stevens"</span>}
]
</code></pre>
<h1 id="heading-summary">Summary</h1>
<p>Congratulations! You have a functional Istio cluster up and running on your local machine.</p>
<p>You can now use this cluster to further explore the various features of Istio.  </p>
<h1 id="heading-whats-next">What's Next</h1>
<p>Dig deeper into Istio's main features such as <a target="_blank" href="https://articles.adityasamant.dev/blog/istio/observability/">Observability</a>, <a target="_blank" href="https://articles.adityasamant.dev/blog/istio/traffic-management/">Traffic Management</a> and <a target="_blank" href="https://articles.adityasamant.dev/blog/istio/security/">Security</a>.</p>
]]></content:encoded></item></channel></rss>