<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Josh Duffney</title>
    <description>The latest articles on DEV Community by Josh Duffney (@joshduffney).</description>
    <link>https://dev.to/joshduffney</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F259840%2F0e46ad0f-6556-4384-b59f-1a391bbd5865.jpeg</url>
      <title>DEV Community: Josh Duffney</title>
      <link>https://dev.to/joshduffney</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joshduffney"/>
    <language>en</language>
    <item>
      <title>Continuously patch GHCR images with Copacetic</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Fri, 28 Feb 2025 15:05:37 +0000</pubDate>
      <link>https://dev.to/joshduffney/continuously-patch-ghcr-images-with-copacetic-334k</link>
      <guid>https://dev.to/joshduffney/continuously-patch-ghcr-images-with-copacetic-334k</guid>
      <description>&lt;p&gt;Keep your GHCR-hosted images secure and updated with an effortless GitHub workflow. Follow along to see how!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fox0gofxyjrho67bbziaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fox0gofxyjrho67bbziaf.png" alt="Image description" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚙️ &lt;strong&gt;How It Works&lt;/strong&gt;:&lt;br&gt;
Here’s the gist of what we’re building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Query&lt;/strong&gt;: Fetch all your images from GHCR.&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;Scan&lt;/strong&gt;: Use Trivy to spot vulnerabilities.&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Patch&lt;/strong&gt;: Apply fixes with Copacetic and update the images on GHCR.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Gathering Images with Glue Code
&lt;/h2&gt;

&lt;p&gt;To patch all your images, we first need to know what’s in your GHCR. Both Trivy and Copacetic have GitHub Actions, making automation a breeze—we can loop through every image in a workflow.&lt;/p&gt;

&lt;p&gt;🧐 &lt;strong&gt;The Challenge&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dynamically generating the list of images and tags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;strong&gt;The Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;A bit of Go "&lt;strong&gt;glue code&lt;/strong&gt;" that hits GitHub’s &lt;strong&gt;REST API&lt;/strong&gt;, grabs all your images and tags under your username, and outputs them to a &lt;code&gt;matrix.json&lt;/code&gt; file. This file feeds into the workflow via the fromJson function, driving the patching process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfiymzk2rthbkvthcrte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfiymzk2rthbkvthcrte.png" alt="Image description" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  🔄 Continuous Patching Workflow
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens—a GitHub workflow that ties everything together. It starts by generating the image list, then loops through each one to scan, patch, and update it on GHCR.&lt;/p&gt;

&lt;p&gt;The workflow splits into two jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1️⃣ Setup: Prepares the list of images.&lt;/li&gt;
&lt;li&gt;2️⃣ Patch: Runs the scanning, patching, and updating.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  1️⃣ The Setup Job
&lt;/h3&gt;

&lt;p&gt;The setup job kicks things off by creating the matrix of images to patch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.set-matrix.outputs.matrix }}&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout to repository&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate image list&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./listImages&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set matrix data&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;set-matrix&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "matrix=$(jq -c . &amp;lt; ./matrix.json)" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹 What's Happening Here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Checks out your repo to access necessary files.&lt;/li&gt;
&lt;li&gt;✔️ Runs listImages, a Go program that builds matrix.json.&lt;/li&gt;
&lt;li&gt;✔️ Stores the matrix in a &lt;code&gt;GITHUB_OUTPUT&lt;/code&gt; variable for the next job.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ The Patch Job
&lt;/h3&gt;

&lt;p&gt;The patch job takes the matrix and runs the full patching cycle for each image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ fromJson(needs.setup.outputs.matrix) }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹 Key Features&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Depends on setup (via needs: setup).&lt;/li&gt;
&lt;li&gt;✔️ Prevents failure from stopping all patches (fail-fast: false).&lt;/li&gt;
&lt;li&gt;✔️ Loads image list from matrix.json using fromJson().&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔍 &lt;strong&gt;Patch Step 1: Scan with Trivy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate Trivy Report&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aquasecurity/trivy-action@0.29.0&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;scan-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image"&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json"&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;report.json"&lt;/span&gt;
    &lt;span class="na"&gt;ignore-unfixed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;vuln-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os"&lt;/span&gt;
    &lt;span class="na"&gt;image-ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.REGISTRY}}/${{ matrix.images }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Scans the image for OS vulnerabilities and outputs a report.json file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📊 &lt;strong&gt;Patch Step 2: Count Vulnerabilities&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check vulnerability count&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vuln_count&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;report_file="report.json"&lt;/span&gt;
    &lt;span class="s"&gt;vuln_count=$(jq 'if .Results then [.Results[] | select(.Class=="os-pkgs" and .Vulnerabilities!=null) | .Vulnerabilities[]] | length else 0 end' "$report_file")&lt;/span&gt;
    &lt;span class="s"&gt;echo "vuln_count=$vuln_count" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Extracts the number of fixable vulnerabilities and saves the count as &lt;code&gt;vuln_count&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🏷️ &lt;strong&gt;Patch Step 3: Create Patch Tag&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create patch tag&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;patch_tag&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;imageName=$(echo ${{ matrix.images }} | cut -d ':' -f1)&lt;/span&gt;
    &lt;span class="s"&gt;current_tag=$(echo ${{ matrix.images }} | cut -d ':' -f2)&lt;/span&gt;
    &lt;span class="s"&gt;if [[ $current_tag == *-[0-9] ]]; then&lt;/span&gt;
      &lt;span class="s"&gt;numeric_tag=$(echo "$current_tag" | awk -F'-' '{print $NF}')&lt;/span&gt;
      &lt;span class="s"&gt;non_numeric_tag=$(echo "$current_tag" | sed "s#-$numeric_tag##g")&lt;/span&gt;
      &lt;span class="s"&gt;incremented_tag=$((numeric_tag+1))&lt;/span&gt;
      &lt;span class="s"&gt;new_tag="$non_numeric_tag-$incremented_tag"&lt;/span&gt;
    &lt;span class="s"&gt;else&lt;/span&gt;
      &lt;span class="s"&gt;new_tag="$current_tag-1"&lt;/span&gt;
    &lt;span class="s"&gt;fi&lt;/span&gt;
    &lt;span class="s"&gt;echo "tag=$new_tag" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
    &lt;span class="s"&gt;echo "imageName=$imageName" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Generates a new patch tag (e.g., &lt;code&gt;app:0.1.0&lt;/code&gt; → &lt;code&gt;app:0.1.0-1&lt;/code&gt; → &lt;code&gt;app:0.1.0-2&lt;/code&gt;) using the &lt;a href="https://project-copacetic.github.io/copacetic/website/best-practices#static-incremental-tags" rel="noopener noreferrer"&gt;static incremental tagging strategy&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔧 &lt;strong&gt;Patch Step 4: Patch with Copacetic&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run copa action&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.vuln_count.outputs.vuln_count != '0'&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copa&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project-copacetic/copa-action@v1.2.1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.REGISTRY}}/${{ matrix.images }}&lt;/span&gt;
    &lt;span class="na"&gt;image-report&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;report.json"&lt;/span&gt;
    &lt;span class="na"&gt;patched-tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.patch_tag.outputs.tag }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Applies patches only if vulnerabilities exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📤 &lt;strong&gt;Patch Step 5: Push to GHCR&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to GHCR&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.copa.conclusion == 'success'&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@3.3.0&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GHCR_TOKEN }}&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push patched image&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.login.conclusion == 'success'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;docker push ${{ steps.copa.outputs.patched-image }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Authenticates with GHCR and pushes the patched image if patching was successful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ready to try it?
&lt;/h2&gt;

&lt;p&gt;Check the repo's &lt;a href="https://github.com/duffney/continuous-patching/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; to set up your own continuous patching workflow! 🎉&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This solution only works for public registries, like GHCR. To use copa to patch locally, see &lt;a href="https://github.com/project-copacetic/copa-action?tab=readme-ov-file#option-2-connect-using-defaults-through-a-custom-socket" rel="noopener noreferrer"&gt;Option 2&lt;/a&gt; in the Copa docs. And for private see &lt;a href="https://github.com/project-copacetic/copa-action?tab=readme-ov-file#option-1-connect-to-buildx-instance-default" rel="noopener noreferrer"&gt;Option 1&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>security</category>
    </item>
    <item>
      <title>⚡ Secure your containers faster—without disrupting your workflow</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Fri, 28 Feb 2025 15:04:55 +0000</pubDate>
      <link>https://dev.to/joshduffney/secure-your-containers-faster-without-disrupting-your-workflow-a1c</link>
      <guid>https://dev.to/joshduffney/secure-your-containers-faster-without-disrupting-your-workflow-a1c</guid>
      <description>&lt;p&gt;Security flaws in container images can't always wait. Instead of rebuilding or relying on a new base image, &lt;strong&gt;you can patch vulnerabilities directly&lt;/strong&gt;—without disrupting your workflow.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;What you'll learn&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scan a container image with Trivy.&lt;/li&gt;
&lt;li&gt;Apply OS-level patches using Copacetic.&lt;/li&gt;
&lt;li&gt;Push the patched image to a container registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you'll know how to quickly patch container images and keep your deployments secure with minimal effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, ensure you have the following installed on your system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/get-started/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://trivy.dev/v0.57/getting-started/installation/" rel="noopener noreferrer"&gt;Trivy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Copacetic
&lt;/h2&gt;

&lt;p&gt;On macOS with Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;copa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Define variables&lt;/span&gt;
&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0.9.0"&lt;/span&gt;
&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/project-copacetic/copacetic/releases/download/v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/copa_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux_amd64.tar.gz"&lt;/span&gt;

&lt;span class="c"&gt;# Download, extract, cleanup, and move copa binary&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"copa_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux_amd64.tar.gz"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; &lt;span class="s2"&gt;"copa_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux_amd64.tar.gz"&lt;/span&gt; copa &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"copa_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux_amd64.tar.gz"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;copa /usr/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Future releases can be found, &lt;a href="https://github.com/project-copacetic/copacetic/releases/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable containerd image store
&lt;/h2&gt;

&lt;p&gt;Copacetic uses &lt;a href="https://docs.docker.com/build/buildkit/" rel="noopener noreferrer"&gt;BuildKit&lt;/a&gt; to execute patch commands, think &lt;code&gt;apt update&lt;/code&gt; and &lt;code&gt;apt upgrade&lt;/code&gt;, which are essential for updating and securing container images.&lt;/p&gt;

&lt;p&gt;TODO: is this true/ accurate?&lt;br&gt;
&lt;a href="https://docs.docker.com/desktop/features/containerd/" rel="noopener noreferrer"&gt;https://docs.docker.com/desktop/features/containerd/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The containerd image store simplifies the image-building process by allowing images to be constructed directly within the containerd environment, eliminating the need to start a separate buildkit instance.&lt;/p&gt;

&lt;p&gt;Follow these steps to enable the image store:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Docker Desktop, go to &lt;code&gt;Settings&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;General&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then check &lt;code&gt;Use containerd for pulling and storing images&lt;/code&gt; and click &lt;code&gt;Apply &amp;amp; restart&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzl6p0nyxvr2nd5iym1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzl6p0nyxvr2nd5iym1l.png" alt="Image description" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For cases where the image store isn’t available or suitable, see the &lt;a href="https://project-copacetic.github.io/copacetic/website/custom-address#buildkit-connection-examples" rel="noopener noreferrer"&gt;Buildkit connection examples&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Patch the container image
&lt;/h2&gt;

&lt;p&gt;Copacetic provides &lt;strong&gt;two&lt;/strong&gt; patching choices: &lt;strong&gt;update all&lt;/strong&gt; packages to their latest versions, or apply &lt;strong&gt;targeted patches&lt;/strong&gt; to fix only vulnerable packages, keeping changes minimal and precise.&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 1: Update all packages
&lt;/h3&gt;

&lt;p&gt;When you're not concerned with updating specific packages, this is the option you want.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Patch the image:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;copa patch &lt;span class="nt"&gt;-i&lt;/span&gt; jduffney/hello-go:0.1.0
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrgovg8goejtf7i9sbgp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrgovg8goejtf7i9sbgp.png" alt="Image description" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Give it a try&lt;/strong&gt;: Replace &lt;code&gt;jduffney/hello-go:0.1.0&lt;/code&gt; with any container image you'd like and use copa to patch it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Option 2: Update only targeted packages
&lt;/h3&gt;

&lt;p&gt;This process is straightforward: Trivy scans your container image to generate a vulnerability report, identifying specific issues. Copacetic then ingests this report, utilizing the data to apply targeted patches exclusively to the flagged packages, ensuring precise and efficient remediation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate a vulnerability report:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy image &lt;span class="nt"&gt;--pkg-types&lt;/span&gt; os &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--ignore-unfixed&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; json &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-o&lt;/span&gt; vulnReport.json &lt;span class="se"&gt;\&lt;/span&gt;
golang:1.24rc3-alpine3.21
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apply targeted patches:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;copa patch &lt;span class="nt"&gt;-i&lt;/span&gt; golang:1.24rc3-alpine3.21 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-r&lt;/span&gt; vulnReport.json
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a3merzaog34qd3q3bfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a3merzaog34qd3q3bfu.png" alt="Image description" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Compare Scan Results
&lt;/h2&gt;

&lt;p&gt;Wanna see the patching magic in action? Fire up Trivy to scan your unpatched and patched images—boom, no more vulnerability.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Scan the unpatched image:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy image &lt;span class="nt"&gt;--pkg-types&lt;/span&gt; os &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--ignore-unfixed&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
jduffney/hello-go:0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scan the patched image:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy image &lt;span class="nt"&gt;--pkg-types&lt;/span&gt; os &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--ignore-unfixed&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
jduffney/hello-go:0.1.0-patched
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1tgcd76x51292oxteo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1tgcd76x51292oxteo1.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the time of writing, Copacetic &lt;strong&gt;successfully patched all 10 vulnerabilities&lt;/strong&gt; in the &lt;code&gt;jduffney/hello-go:0.1.0&lt;/code&gt; image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push the container image
&lt;/h2&gt;

&lt;p&gt;The only thing left to do is to push the freshly patched container image to your registry.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;docker push&lt;/code&gt; to upload the patched image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag jduffney/hello-go:0.1.0-patched &lt;span class="se"&gt;\&lt;/span&gt;
&amp;lt;yourRegistry&amp;gt;/hello-go:0.1.0-patched
docker push &amp;lt;yourRegistry&amp;gt;/hello-go:0.1.0-patched
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Replace &lt;code&gt;&amp;lt;yourRegistry&amp;gt;&lt;/code&gt; with your DockerHub or ghcr information.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🎉 Congrats! Your image is patched and deployment-ready. &lt;/p&gt;

&lt;p&gt;Awesome, right? But imagine &lt;strong&gt;automatically keeping every image in your registry patched&lt;/strong&gt;, all the time.&lt;/p&gt;

&lt;p&gt;Discover how in the next post. 👈&lt;/p&gt;

</description>
      <category>security</category>
      <category>containers</category>
      <category>docker</category>
    </item>
    <item>
      <title>Does Patching Containers Feel Like Whack-a-Mole?</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Fri, 28 Feb 2025 15:03:44 +0000</pubDate>
      <link>https://dev.to/joshduffney/does-patching-containers-feel-like-whack-a-mole-3obc</link>
      <guid>https://dev.to/joshduffney/does-patching-containers-feel-like-whack-a-mole-3obc</guid>
      <description>&lt;p&gt;Years ago, I thought switching to containers meant no more patching things—a dream come true—but the work just switched jerseys.&lt;/p&gt;

&lt;p&gt;Instead of remoting into VMs and running updates, I’m now hunting down Dockerfiles and tweaking base images. If anything, patching has become less fun and more scavenger hunt.&lt;/p&gt;

&lt;p&gt;Then I stumbled across Copacetic, a new project that’s makes it possible to automate container patching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the CNCF project Copacetic
&lt;/h2&gt;

&lt;p&gt;Copacetic, or "Copa" for short, is a powerful CLI tool built in Go and based on BuildKit, designed to streamline container image patching. By leveraging vulnerability scanning tools like Trivy, Copa can automatically patch existing container images, saving developers time and effort.&lt;/p&gt;

&lt;p&gt;Copa works by parsing vulnerability reports to identify necessary update packages, which are then processed using appropriate package managers like &lt;code&gt;apt&lt;/code&gt; or &lt;code&gt;apk&lt;/code&gt;. It then applies these updates to the container image with BuildKit, ensuring the image is up to date without requiring a rebuild.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flackbhr07ublvlbaxwtn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flackbhr07ublvlbaxwtn.png" alt="image" width="624" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of Copa’s key benefits is that it doesn't require you to modify their container build process or use specific tools to support patching. Additionally, it reduces the need for specialized knowledge, as it relies on the vulnerability remediation embedded in existing reports from popular scanning tools. This makes it much easier to patch OS-level vulnerabilities without waiting for external dependencies or expert intervention.&lt;/p&gt;

&lt;p&gt;Here's a short &lt;strong&gt;4min&lt;/strong&gt; lightning talk of Copacetic in action:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/g_8xDwXJRDM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  3 Ways Copacetic Tames CVEs
&lt;/h2&gt;

&lt;p&gt;I’ve seen Copa shine in three practical patterns:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Patching&lt;/strong&gt;: Run the Copa CLI or &lt;a href="https://github.com/project-copacetic/copa-extension" rel="noopener noreferrer"&gt;Docker Desktop plugin&lt;/a&gt;. Patch an image on your machine, push it to your registry—done. Perfect for quick fixes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Integration&lt;/strong&gt;: Add Trivy and Copa to GitHub Actions. It patches mid-build, keeps tags steady, and cuts vulnerabilities without breaking your flow. Security, streamlined.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry Automation&lt;/strong&gt;: Use a little scripting &amp;amp; Copa to scan your registry, patch images in bulk, and push them back with fresh tags. A cron job can drive it—devs grab the updates with no hasle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Copacetic Matters (and What’s Next)
&lt;/h2&gt;

&lt;p&gt;This post unpacked the mess: containers didn’t kill patching—they're still a headache, and Copacetic is like aspirin for the pain. &lt;/p&gt;

&lt;p&gt;It’s a new tool that automates the slog—locally, in CI/CD, or at scale—without demanding a full rework. Not every vulnerability vanishes, but it dials the chaos down to manageable.&lt;/p&gt;

&lt;p&gt;Ready to simplify patching? The &lt;strong&gt;next post&lt;/strong&gt; walks you step-by-step through using Copacetic—those CVEs are sitting there, waiting for you.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>security</category>
    </item>
    <item>
      <title>Automate Container Image Patching with Copacetic and GitHub Actions</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Mon, 02 Oct 2023 13:57:00 +0000</pubDate>
      <link>https://dev.to/azure/automate-container-image-patching-with-copacetic-and-github-actions-4mea</link>
      <guid>https://dev.to/azure/automate-container-image-patching-with-copacetic-and-github-actions-4mea</guid>
      <description>&lt;p&gt;In an era where software deployment speed and security are paramount, automating critical tasks in the development pipeline is essential. GitHub Actions offers a robust platform to achieve this automation seamlessly. &lt;/p&gt;

&lt;p&gt;In this article, we'll walk you through the creation of a GitHub Actions workflow that focuses on automating the patching and signing of container images using a CNCF sandbox project &lt;a href="https://github.com/project-copacetic/copacetic" rel="noopener noreferrer"&gt;Copacetic&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use copa-action to automate patching
&lt;/h2&gt;

&lt;p&gt;Copacetic is a command-line application that uses reports from vulnerability scanners, such as trivy, to directly patch containers images.&lt;/p&gt;

&lt;p&gt;It's able to do this by parsing the vulnerability reports generated by the scanners and creating additional layers on top of the container image that includes patches to the CVEs identified by the scanner.&lt;/p&gt;

&lt;p&gt;For my &lt;a href="https://www.youtube.com/watch?v=Mep9QWc3ByE" rel="noopener noreferrer"&gt;Microsoft Build talk&lt;/a&gt;, I had to setup a shell task in the workflow to run the copa CLI, but luckily for you an official action now exists. Here's how you set it up.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# .github/workflows/patch.yml&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;copaGitOps&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

          &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="c1"&gt;# provide relevant list of images to scan on each run&lt;/span&gt;
                  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3cexampleacr.azurecr.io/azure-voting-app-rust:v0.1.0-alpha'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

          &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Azure&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login&lt;/span&gt;
            &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azure/login@v1&lt;/span&gt;
            &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;creds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_CREDENTIALS }}&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Azure Container Registry&lt;/span&gt;
            &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;az acr login --name ${{ vars.ACR_NAME }}&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
            &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3.0.0&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate Trivy Report&lt;/span&gt;
            &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4&lt;/span&gt;
            &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;scan-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image'&lt;/span&gt;
              &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;json'&lt;/span&gt;
              &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;report.json'&lt;/span&gt;
              &lt;span class="na"&gt;ignore-unfixed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
              &lt;span class="na"&gt;vuln-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;os'&lt;/span&gt;
              &lt;span class="na"&gt;image-ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.images }}&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Vuln Count&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vuln_count&lt;/span&gt;
            &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;report_file="report.json"&lt;/span&gt;
              &lt;span class="s"&gt;vuln_count=$(jq '.Results | length' "$report_file")&lt;/span&gt;
              &lt;span class="s"&gt;echo "vuln_count=$vuln_count" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Patched Tag&lt;/span&gt; 
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extract_tag&lt;/span&gt;
            &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;imageName=$(echo ${{ matrix.images }} | cut -d ':' -f1)&lt;/span&gt;
              &lt;span class="s"&gt;current_tag=$(echo ${{ matrix.images }} | cut -d ':' -f2)&lt;/span&gt;

              &lt;span class="s"&gt;if [[ $current_tag == *-[0-9] ]]; then&lt;/span&gt;
                  &lt;span class="s"&gt;numeric_tag=$(echo "$current_tag" | awk -F'-' '{print $NF}')&lt;/span&gt;
                  &lt;span class="s"&gt;non_numeric_tag=$(echo "$current_tag" | sed "s#-$numeric_tag##g")&lt;/span&gt;
                  &lt;span class="s"&gt;incremented_tag=$((numeric_tag+1))&lt;/span&gt;
                  &lt;span class="s"&gt;new_tag="$non_numeric_tag-$incremented_tag"&lt;/span&gt;
              &lt;span class="s"&gt;else&lt;/span&gt;
                  &lt;span class="s"&gt;new_tag="$current_tag-1"&lt;/span&gt;
              &lt;span class="s"&gt;fi&lt;/span&gt;

              &lt;span class="s"&gt;echo "patched_tag=$new_tag" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
              &lt;span class="s"&gt;echo "imageName=$imageName" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copa Action&lt;/span&gt;
            &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.vuln_count.outputs.vuln_count != '0'&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copa&lt;/span&gt;
            &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project-copacetic/copa-action@v1.0.0&lt;/span&gt;
            &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.images }}&lt;/span&gt;
              &lt;span class="na"&gt;image-report&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;report.json'&lt;/span&gt;
              &lt;span class="na"&gt;patched-tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.extract_tag.outputs.patched_tag }}&lt;/span&gt;
              &lt;span class="na"&gt;buildkit-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v0.11.6'&lt;/span&gt;
              &lt;span class="c1"&gt;# optional, default is latest&lt;/span&gt;
              &lt;span class="na"&gt;copa-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.3.0'&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker Push Patched Image&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
            &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.login.conclusion == 'success'&lt;/span&gt;
            &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                &lt;span class="s"&gt;# docker push ${{ steps.copa.outputs.patched-image }}&lt;/span&gt;
                &lt;span class="s"&gt;echo "DIGEST=$(docker push ${{ steps.copa.outputs.patched-image }} | grep -oE 'sha256:[a-f0-9]{64}')" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There's a lot going on here, so let's walk through the steps in the job.&lt;/p&gt;

&lt;p&gt;The workflow uses a matrix strategy with a list of images to generate a new job per image in the array of images.&lt;/p&gt;

&lt;p&gt;A private Azure Container Registry is used the workflow authenticates to azure and then runs the az acr login command to populate the docker login credentials. Which allows you to use docker commands to interact with the registry.&lt;/p&gt;

&lt;p&gt;The Trivy action is used to generate a list of vulnerabilities that exist for the targeted container image and outputs that to a &lt;code&gt;report.json&lt;/code&gt; file to be used for patching by Copacetic.&lt;/p&gt;

&lt;p&gt;A few lines of Bash are used to populate a variable that contains the number of vulnerabilities found.&lt;/p&gt;

&lt;p&gt;Extract Patch Tag is another shell task that uses for awksedfu to determine what the container image patched tag should be following the format &lt;code&gt;tag-1&lt;/code&gt;, with &lt;code&gt;-1&lt;/code&gt; indicating the image has been patched.&lt;/p&gt;

&lt;p&gt;Copa action installs and runs the Copa CLI to ingest the &lt;code&gt;reports.json&lt;/code&gt; and patch the remote container image, which results in a local version of the image that's been patched.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Once copa is done and a newly patched image has been created locally, the &lt;code&gt;docker push&lt;/code&gt; command is used to push the image to the container registry, but it also grabs the digest from the image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Curious what the digest is used for? Keep reading to find out. :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Signed patched container images with Notation
&lt;/h2&gt;

&lt;p&gt;Notation is another CNCF project that just hit version v1.0.0 in Aug. 202 and it allows you digitally sign artifacts. Those signatures become stamps of approval for those digital artifacts letting you know you can trust them to run in your environment. &lt;/p&gt;

&lt;p&gt;And if you're using admission controllers on your cluster with projects like &lt;a href="https://github.com/deislabs/ratify/blob/main/CONTRIBUTING.md#running-the-ratify-cli" rel="noopener noreferrer"&gt;Ratify&lt;/a&gt;, those signatures are even more important because without them your containers won't be allowed to deploy on the cluster.&lt;/p&gt;

&lt;p&gt;At any rate, adding the notary-actions to your GitHub Action Workflow is fairly straight forward:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Notation&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.push.conclusion == 'success'&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notaryproject/notation-action/setup@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Notation Sign&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.push.conclusion == 'success'&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notaryproject/notation-action/sign@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;plugin_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azure-kv&lt;/span&gt;
    &lt;span class="na"&gt;plugin_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/Azure/notation-azure-kv/releases/download/v1.0.1/notation-azure-kv_1.0.1_linux_amd64.tar.gz&lt;/span&gt;
    &lt;span class="na"&gt;plugin_checksum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;f8a75d9234db90069d9eb5660e5374820edf36d710bd063f4ef81e7063d3810b&lt;/span&gt;
    &lt;span class="na"&gt;key_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.KEY_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;target_artifact_reference&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.extract_tag.outputs.imageName }}@${{ steps.push.outputs.DIGEST }}&lt;/span&gt;
    &lt;span class="na"&gt;signature_format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cose&lt;/span&gt;
    &lt;span class="na"&gt;plugin_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
        &lt;span class="s"&gt;name=${{ vars.CERT_NAME }}&lt;/span&gt;
        &lt;span class="s"&gt;self_signed=false&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;notation-action/setup&lt;/code&gt; installs the notation CLI on the GitHub action runner at the specified version.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;notation-action/sign' optionally installs a plugin, in this case, the azure-kv plugin. It then takes the necessary arguments as parameters and then passes them to the&lt;/code&gt;notation sign` command that digitally signs the target artifact (container image)&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the Workflow
&lt;/h2&gt;

&lt;p&gt;If you've followed along with each tutorial in this series, you have the Flux resources for the Azure Voting app deployed along with Flux resources for the image automation to detect when new container images are pushed to the container registry. And now, you have a workflow that patches the Azure Voting app container images.&lt;/p&gt;

&lt;p&gt;So, now the fun begins. Once you commit and push the &lt;code&gt;.github.com/workflows/patch.yml&lt;/code&gt;file to the repository it will patch the Azure voting container image and push it to the container registry. Once that image is pushed the Flux image automation will detect that a new version of the image exists. When that happens the image automation will update the &lt;code&gt;kustomization.yaml&lt;/code&gt; with the newer tag, which will in turn trigger the Flux resources monitor your application's repo to redeploy the application to the Azure Kubernetes Cluster.&lt;/p&gt;

&lt;p&gt;Run the following commands &amp;amp; watch the magic happen:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
git add . &amp;amp;&amp;amp; git commit -m 'Add copa patching workflow' &amp;amp;&amp;amp; git push&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0xs9s7an2c192p15ks8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0xs9s7an2c192p15ks8.png" alt="Patching worflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2f6q8skn3ifs9lmivgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2f6q8skn3ifs9lmivgj.png" alt="Container registry images"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvstmf1l4kgrz21262dfd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvstmf1l4kgrz21262dfd.png" alt="kubectl describe pod output"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>github</category>
      <category>containers</category>
    </item>
    <item>
      <title>Configure Image Automation with Flux</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Fri, 29 Sep 2023 15:05:24 +0000</pubDate>
      <link>https://dev.to/azure/configure-image-automation-with-fluxcd-1ecc</link>
      <guid>https://dev.to/azure/configure-image-automation-with-fluxcd-1ecc</guid>
      <description>&lt;p&gt;In this post you'll continue the work built in Part 1 by adding Image Automation to the Azure Voting App deployment using FluxCD.&lt;/p&gt;

&lt;p&gt;FluxCD's Image Automation allows you to monitor container image registries, allowing it to detect new image versions and automatically triggering updates according to the policies you define.&lt;/p&gt;

&lt;p&gt;You can accomplish this by deploying three additional FluxCD resources, an image repository, image policy, and update automation. In addition to these three resources, you'll also make a slight change to the Kustomization.yml by marking the images so Flux knows what container images to update in the deployment.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an Image Repository
&lt;/h2&gt;

&lt;p&gt;Before Flux can monitor a registry for new images, it must know what repository to monitor and that's the purpose of the Image Repository resource.&lt;/p&gt;

&lt;p&gt;Now, because this tutorial uses a private Azure Container Registry instance you must do a little leg work first to set up a credential that Flux can use to authenticate to the registry.&lt;/p&gt;

&lt;p&gt;Flux has several authentication options, but in this tutorial, you'll create a Kubernetes secret that stores an ACR token.&lt;/p&gt;

&lt;p&gt;Run the following commands to create the ACR token, then create the Kubernetes secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;s3cexampleacr.azurecr.io
&lt;span class="nv"&gt;tokenName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flux

&lt;span class="nv"&gt;tokenPassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az acr token create &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$tokenName&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--registry&lt;/span&gt; &lt;span class="nv"&gt;$registry&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--scope-map&lt;/span&gt; _repositories_admin &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'credentials.passwords[0].value'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--only-show-errors&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--output&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

kubectl create secret docker-registry regcred &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$registry&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$tokenName&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$tokenPassword&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fluxcdbot@users.noreply.github.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the ACR token is stored in a Kubernetes secret, run the following command to create the image repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux create image repository azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;s3cexampleacr.azurecr.io/azure-voting-app-rust &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret-ref&lt;/span&gt; regcred &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--export&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./clusters/my-cluster/azure-voting-image.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create an Image Policy
&lt;/h2&gt;

&lt;p&gt;Next, you'll create an image policy. An image policy is the rules Flux follows to determine what images are newer. Often these rules are written to follow semantic versioning rules.&lt;/p&gt;

&lt;p&gt;For example, if my application's container images are begins with the tag &lt;code&gt;v0.1.0-alpha&lt;/code&gt; a valid select pattern would be &lt;code&gt;&amp;gt;=0.1.0-0&lt;/code&gt;. This would allow Flux to detect that any semver higher than &lt;code&gt;v0.1.0-alpha&lt;/code&gt; should be used to update the deployment.&lt;/p&gt;

&lt;p&gt;Run the following command to create the image polciy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux create image policy azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--select-semver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;=0.1.0-0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--export&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./clusters/my-cluster/azure-voting-image-policy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the image update
&lt;/h2&gt;

&lt;p&gt;At this stage, Flux has knowledge of the container registry and the specific container image you intend to track. It also possesses a policy for identifying newer images; however, it lacks information on which deployment files to alter when a newer version is identified. This is where image update resources come into play, as they serve the purpose of specifying which deployment files need modification when updates are detected.&lt;/p&gt;

&lt;p&gt;The image update is important for several reasons, but the main  reason is, it's the resource that's going to be writing to your repository. &lt;/p&gt;

&lt;p&gt;What do I mean it will be writing to my repository? Well, when a newer version of the container image is detected the image update resource updates the image reference in the &lt;code&gt;Kustomization.yaml&lt;/code&gt; will the latest image and image tag and that commit or change to the repository is what triggers the actual redeployment of the Kubernetes manifests by the other Flux resources that handle the application deployment.&lt;/p&gt;

&lt;p&gt;Having Flux commit directly to your existing branches might make you nervous, and for good reason. If that's the case, you can choose to have these changes staged in a new branch and merge them in before a deployment is triggered. To learn how to do that check out the Flux docs &lt;a href="https://fluxcd.io/flux/guides/image-update/#push-updates-to-a-different-branch" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But, for this tutorial, we'll keep it simple and have it commit directly to our existing branch.&lt;/p&gt;

&lt;p&gt;Run the following command to create the image update resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux create image update azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--git-repo-ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--git-repo-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./manifests"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--checkout-branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;copaGitOps &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--author-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fluxcdbot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--author-email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fluxcdbot@users.noreply.github.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--commit-template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{{range .Updated.Images}}{{println .}}{{end}}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--export&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./clusters/my-cluster/azure-voting-image-update.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mark the Kustomization manifest
&lt;/h2&gt;

&lt;p&gt;The last step in setting up image automation is to mark the Kustomization.yaml manifest with comments that indicate what container image should be modified by image update when new images version become available.&lt;/p&gt;

&lt;p&gt;To make these changes, open the &lt;code&gt;kustomization.yaml&lt;/code&gt; under &lt;code&gt;/manifests&lt;/code&gt; and add the following comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azure-voting-app-rust&lt;/span&gt;
    &lt;span class="na"&gt;newName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3cexampleacr.azurecr.io/azure-voting-app-rust&lt;/span&gt; &lt;span class="c1"&gt;# {"$imagepolicy": "flux-system:azure-voting:name"}&lt;/span&gt;
    &lt;span class="na"&gt;newTag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt; &lt;span class="c1"&gt;# {"$imagepolicy": "flux-system:azure-voting:tag"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's crazy how often automation comes back to &lt;code&gt;string/replace&lt;/code&gt;, isn't it? Anyway, that's it! You're finished. &lt;/p&gt;

&lt;p&gt;Now when you push a new version of the container image to your container registry Flux will pick it up, modify the &lt;code&gt;kustomization.yaml&lt;/code&gt; file, and that change will in turn trigger a new deployment to the Kubernetes cluster! Pretty awesome right? &lt;/p&gt;

&lt;p&gt;Well, it gets even better. In the next post, you'll learn how to use a new CNCF sandbox project called &lt;a href="https://project-copacetic.github.io/copacetic/website/" rel="noopener noreferrer"&gt;Copacetic &lt;/a&gt;to automatically patch your container images and use FluxCD to deploy the patched version. Keep your Kubernetes environment nice and secure!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow me&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/joshduffney"&gt;@joshduffney&lt;/a&gt; to catch my next post.&lt;/p&gt;

&lt;p&gt;Also, a quick shout out to my teammate Paul Yu who wrote a series of awesome &lt;a href="https://dev.to/azure/git-going-with-gitops-on-aks-a-step-by-step-guide-using-fluxcd-aks-extension-499m"&gt;articles &lt;/a&gt; on FluxCD. I referenced them a lot while writing this post.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>containers</category>
      <category>git</category>
    </item>
    <item>
      <title>Automating Kubernetes Deployments with Flux</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Wed, 27 Sep 2023 14:06:36 +0000</pubDate>
      <link>https://dev.to/azure/secure-supply-chain-with-gitops-fluxcd-1dlb</link>
      <guid>https://dev.to/azure/secure-supply-chain-with-gitops-fluxcd-1dlb</guid>
      <description>&lt;p&gt;Interested in making Kubernetes deployments more secure AND automated? If so, keep reading. :)&lt;/p&gt;

&lt;p&gt;FluxCD, commonly just called Flux, is an open-source and cloud-native Continuous Delivery (CD) and GitOps tool designed to automate deployments to Kubernetes. &lt;/p&gt;

&lt;p&gt;And in this post, you'll learn how to install and configure Flux to automate the deployment of patched and signed containers images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;In the graphic above, you'll notice that the secure pipeline has two phases; build and deploy. This post focuses on automating the deploy phase, but that requires you already have the Azure infrastructure setup and a GitHub Action Workflow configured to handle the build or CI portion of the pipeline. So, you'll need to do some prerequisite work to follow along.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/duffney/secure-supply-chain-on-aks/blob/GitOps/docs/setup.md" rel="noopener noreferrer"&gt;Setup the Azure environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/duffney/secure-supply-chain-on-aks/blob/GitOps/docs/bonus.md" rel="noopener noreferrer"&gt;Configure the GitHub Actions Workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've completed the setup, you should have a working Azure environment with two signed container images for the Azure Voting app hosted in ACR, which is the necessary starting point for this post.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The setup for this is heavy, even though it's automated. So, if you'd like just continue reading and learn how Flux can be used to automate Kubernetes deployments. :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup the Local Dev Env
&lt;/h2&gt;

&lt;p&gt;Flux is a GitOps tool and every GitOps tool needs a Git repository. To get the repo setup follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork &lt;a href="https://github.com/duffney/secure-supply-chain-on-aks" rel="noopener noreferrer"&gt;secure-supply-chain-on-aks&lt;/a&gt; repo.&lt;/li&gt;
&lt;li&gt;Generate a token for Flux to access the repo. Here's a &lt;a href="https://youtu.be/5u45lXmhgxA?si=9mSjs_yO2bXFLI2R&amp;amp;t=836" rel="noopener noreferrer"&gt;great clip&lt;/a&gt; to walk you through those steps.&lt;/li&gt;
&lt;li&gt;Clone the repo to your local machine with, &lt;code&gt;git clone&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Switch to the GitOps branch with &lt;code&gt;git checkout GitOps&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bootstrap FluxCD on the AKS Cluster
&lt;/h2&gt;

&lt;p&gt;In order to get FluxCD working on the cluster, you're going to have to first deploy FluxCD operator to the cluster. Luckily, the FluxCLI makes this super easy. &lt;/p&gt;

&lt;p&gt;Here's what you need to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the &lt;a href="https://fluxcd.io/flux/installation/#install-the-flux-cli" rel="noopener noreferrer"&gt;FluxCLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export the environment variables &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; and &lt;code&gt;GITHUB_USER&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-token&amp;gt;
 &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the &lt;code&gt;flux boostrap&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux bootstrap github &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--components-extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;image-reflector-controller, image-automation-controller &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_USER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--repository&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secure-supply-chain-on-aks &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;copaGitOps &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clusters/my-cluster &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--read-write-key&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--personal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once this command completes, issue a &lt;code&gt;git pull&lt;/code&gt; command and you'll notice a new directory call &lt;code&gt;cluster&lt;/code&gt;. What the &lt;code&gt;flux bootstrap&lt;/code&gt; command did was commit all the necessary manifests to deploy Flux to your cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Flux Source
&lt;/h2&gt;

&lt;p&gt;Now that Flux has been deployed the next thing you need to do is create a source. &lt;/p&gt;

&lt;p&gt;Flux supports several source options, but in this post, you'll use &lt;code&gt;git&lt;/code&gt; as the source.&lt;/p&gt;

&lt;p&gt;Run the following command to create the flux source manifest file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux create secret git azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/duffney/secure-supply-chain-on-aks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_USER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_TOKEN&lt;/span&gt;

flux create &lt;span class="nb"&gt;source &lt;/span&gt;git azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/duffney/secure-supply-chain-on-aks/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;GitOps &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret-ref&lt;/span&gt; azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--export&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./clusters/my-cluster/azure-voting-source.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, command the FluxCLI was used to generate a Kubernetes manifest file that configures the desired Git repository as a source within the Flux system. What this essentially does is makes Flux aware of the Git repository that you want to monitor for changes.&lt;/p&gt;

&lt;p&gt;Run the following command to view all Flux Sources that use Git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux get sources git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Didn't see your repo listed⁉️ Well, that's the Flux system is looking at your GitHub repository for its changes! Flux itself uses GitOps to manage its deployments. 😅&lt;/p&gt;

&lt;p&gt;Run the following commands to push the new Git source to the Flux system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add azure-votiong-app GitRepository"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rerun &lt;code&gt;flux get sources git&lt;/code&gt; again and you'll see the newly created source. Next, you'll configure which files within that repository Flux will care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Flux Kustomization
&lt;/h2&gt;

&lt;p&gt;To use Flux with your Kubernetes deployments they need to be packaged somehow and one of the ways to do that is to use the popular resource configuration management tool, &lt;a href="https://kustomize.io/" rel="noopener noreferrer"&gt;Kustomize&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Kustomize gives you the ability to create a single file, called a &lt;code&gt;Kustmization.yaml&lt;/code&gt; that bundles your deployment. It's that bundle, so to speak, that Flux uses to determine which files to monitor for changes in your GitRepo.&lt;/p&gt;

&lt;p&gt;For this post, &lt;a href="https://kubectl.docs.kubernetes.io/installation/kustomize/" rel="noopener noreferrer"&gt;kustomize&lt;/a&gt; has already been used to create a &lt;a href="https://github.com/duffney/secure-supply-chain-on-aks/blob/GitOps/manifests/kustomization.yaml" rel="noopener noreferrer"&gt;Kustomization.yaml&lt;/a&gt; file that defines the Kubernetes manifests that are considered part of the deployment.&lt;/p&gt;

&lt;p&gt;If you're following along, but using your own appliaction you can create a Kustomization.yaml file by running &lt;code&gt;kustomize create --autodetect&lt;/code&gt; within the directory that contains the manifests.&lt;/p&gt;

&lt;p&gt;But just having a &lt;code&gt;Kustomization.yaml&lt;/code&gt; file within your repository isn't enough, Flux needs to be made aware of it and how you'd like it to run the deployment. &lt;/p&gt;

&lt;p&gt;So, to take care of that create a Flux Kustomization resources with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux create kustomization azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;azure-voting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./manifests"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--retry-interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--health-check-timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--export&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./clusters/my-cluster/azure-voting-kustomization.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, don't forget to push these changes to your repo too. :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add azure-voting-app Kustomization"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify Azure-Voting-App deployed
&lt;/h2&gt;

&lt;p&gt;Given the intervals set in the Kustomization and Source it shouldn't take long for Flux to deploy the Azure-Voting-App to the AKS cluster, but if you're eager to see what's going on you can run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test the Azure-Voting-App
&lt;/h2&gt;

&lt;p&gt;Get the public IP address of the webapp with &lt;code&gt;kubectl get ingress&lt;/code&gt;, and put it into a browsers and you should see the image below. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofjz661c4u2t7ppp2tw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofjz661c4u2t7ppp2tw8.png" alt="kubectl get ingress" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5v6dn2oo6s26fpm74sv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5v6dn2oo6s26fpm74sv.png" alt="Azure-voting-app" width="635" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This post picked up after the build phase of the secure supply chain that is responsible for scanning, patching, and signing container images. If you'd like to learn more about that check out the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/Mep9QWc3ByE?si=WgXJ4FE2XZrlmlzq" rel="noopener noreferrer"&gt;Securing container deployments on Azure Kubernetes Service with open-source tools | BRK264H&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://notaryproject.dev/" rel="noopener noreferrer"&gt;Notation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://project-copacetic.github.io/copacetic/website/" rel="noopener noreferrer"&gt;Copacetic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To dive deeper into GitOps, I highly recommend the follow articles written by my teammates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/just-enough-git-for-gitops-3ok8"&gt;Just Enough Git for GitOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/what-really-is-gitops-4n2d"&gt;What Really is GitOps?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/git-going-with-gitops-on-aks-a-step-by-step-guide-using-fluxcd-aks-extension-499m"&gt;Git going with GitOps on AKS: A Step-by-Step Guide using FluxCD AKS Extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/automating-image-updates-with-fluxcd-on-aks-3i2d"&gt;Automating Image Updates on AKS: A Step-by-Step Guide using open source FluxCD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/joshduffney"&gt;@joshduffney&lt;/a&gt; to catch my next post where I'll walk through using &lt;a href="https://github.com/project-copacetic/copacetic" rel="noopener noreferrer"&gt;Copacetic &lt;/a&gt;and FluxCD's Automate image updates to deploy patched container images.&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>security</category>
    </item>
    <item>
      <title>Level-up Container Security: 4 Open-Source Tools for Secure Software Supply Chain</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Tue, 05 Sep 2023 20:21:24 +0000</pubDate>
      <link>https://dev.to/azure/level-up-container-security-4-open-source-tools-for-secure-software-supply-chain-2gp5</link>
      <guid>https://dev.to/azure/level-up-container-security-4-open-source-tools-for-secure-software-supply-chain-2gp5</guid>
      <description>&lt;p&gt;In a &lt;a href="https://www.youtube.com/watch?v=JC-xCXcyNXI" rel="noopener noreferrer"&gt;thought-provoking presentation&lt;/a&gt; by Kelsey Hightower, he compares the act of plugging in a random USB key discovered at a coffee shop to the common practice of pulling code from GitHub.&lt;/p&gt;

&lt;p&gt;What's funny is that while people might give a suspicious look to someone plugging in a random USB key, they often don't think twice about grabbing a container image from a public registry and tossing it into production. But they really should. Lately, there have been a bunch of security breaches, and guess what? Many of them worked because they went after the weak spots in the software supply chain that no one bothered to check.&lt;/p&gt;

&lt;p&gt;Recent supply chain attacks like SolarWinds, Log4j, and Kaseya have underscored the vulnerability of software supply chains. In response, US Federal agencies, under the influence of Executive Order 14028 and similar directives, are taking proactive steps to enhance the security of software supply chains. This has led to the emergence of an entirely new category of tools aimed at securing the software supply chain.&lt;/p&gt;

&lt;p&gt;In this article, you'll be introduced to four open-source tools that, when integrated into your CI/CD pipeline, can significantly enhance the security of your container deployments, helping you avoid potential disasters arising from deploying compromised container images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Before diving into the specifics of these open-source tools, let's talk about the high-level picture of how they can seamlessly integrate into your pipelines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhiytadylau85z5mnheti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhiytadylau85z5mnheti.png" alt="secure-supply-chain-on-aks-overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During the build stage, Trivy, Copacetic, and Notation play pivotal roles in automating vulnerability scanning, patching, and image signing. Once the build phase concludes, you'll have a trustworthy container image hosted in a registry, ready for deployment. &lt;/p&gt;

&lt;p&gt;On the deployment end, two more tools, Gatekeeper and Ratify, are introduced to enforce policies that allow only signed container images to run on Kubernetes clusters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trivy: Vulnerability Scanner for Container Images
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://trivy.dev/" rel="noopener noreferrer"&gt;Trivy&lt;/a&gt; is CLI scanner tool and is a lifesaver for developers and DevOps teams navigating the intricate world of container security. &lt;/p&gt;

&lt;p&gt;With its user-friendly command-line interface, Trivy makes it a breeze to detect vulnerabilities lurking within container images.&lt;/p&gt;

&lt;p&gt;It has several different flags and options you can use to filter the results of the scanner to ensure the reports it generates remain actionable.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;azure-voting-app-rust:v0.1-alpha
trivy image &lt;span class="nv"&gt;$IMAGE&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--severity&lt;/span&gt; CRITICAL &lt;span class="nv"&gt;$IMAGE&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--vuln-type&lt;/span&gt; os &lt;span class="nt"&gt;--severity&lt;/span&gt; CRITICAL &lt;span class="nv"&gt;$IMAGE&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can also export the vulnerability report generated by Trivy to a file, which is especially useful for the next tool in the lineup.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

trivy image &lt;span class="nt"&gt;--exit-code&lt;/span&gt; 0 &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--output&lt;/span&gt; ./patch.json &lt;span class="nt"&gt;--scanners&lt;/span&gt; vuln &lt;span class="nt"&gt;--vuln-type&lt;/span&gt; os &lt;span class="nt"&gt;--ignore-unfixed&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Copacetic: Patching Container Images
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/project-copacetic/copacetic" rel="noopener noreferrer"&gt;Copacetic&lt;/a&gt;, another open-source gem, works in tandem with Trivy to tackle vulnerabilities in container images. &lt;/p&gt;

&lt;p&gt;It utilizes Trivy's vulnerability reports to identify weak spots and then introduces patched layers to rectify these vulnerabilities. It's worth noting that Copacetic currently only works with remote container registries.&lt;/p&gt;

&lt;p&gt;Here's an example of how it works:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Push unpatched image to a remote registry&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;azure-voting-app-rust:v0.1-alpha
&lt;span class="nv"&gt;ACR_IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ACR_NAME&lt;/span&gt;.azurecr.io/azure-voting-app-rust:v0.1-alpha
docker tag &lt;span class="nv"&gt;$IMAGE&lt;/span&gt; &lt;span class="nv"&gt;$ACR_IMAGE&lt;/span&gt;
docker push &lt;span class="nv"&gt;$ACR_IMAGE&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#Start buildkit &amp;amp; run copa patch command&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./bin/buildkitd &amp;amp;&amp;gt; /dev/null &amp;amp; 
&lt;span class="nb"&gt;sudo &lt;/span&gt;copa patch &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACR_IMAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ./patch.json &lt;span class="nt"&gt;-t&lt;/span&gt; v0.1-alpha-1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#Rescan the newly patched image to verify a reduction in vulnerabilities&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--severity&lt;/span&gt; CRITICAL &lt;span class="nt"&gt;--scanners&lt;/span&gt; vuln &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACR_IMAGE_PATCHED&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#Push the patched image to the registry&lt;/span&gt;
&lt;span class="nv"&gt;ACR_IMAGE_PATCHED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACR_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.azurecr.io/azure-voting-app-rust:v0.1-alpha-1

docker push &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACR_IMAGE_PATCHED&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Notation: Image Signing for Enhanced Security
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://notaryproject.dev/" rel="noopener noreferrer"&gt;Notation &lt;/a&gt;is another command-line too that lets you digitally sign artifacts. And those signatures essentially become the stamps of approval for the different things in your software supply chain. For example, container images.&lt;/p&gt;

&lt;p&gt;Using Notation is simple and straight forward. Once an image has gone through the scanning and patching steps, you  &lt;em&gt;sign&lt;/em&gt; the image with the &lt;code&gt;notation sign&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Once signed the container registry has an image index artifact with the signature. That signature will also be listed in the referrers of the container image.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;APP_DIGEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{index .RepoDigests 0}}'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACR_IMAGE_PATCHED&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
notation sign &lt;span class="nv"&gt;$APP_DIGEST&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: It's a best practice to use the container image digest for signing because tags are mutable, meaning they can be overwritten. Digests on the other hand are unique and change each time a layer is added to the container. By using digests for signing, an additional layer of security is added, ensuring the integrity and authenticity of container images.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Ratify: Ensuring Policy Compliance
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/deislabs/ratify" rel="noopener noreferrer"&gt;Ratify &lt;/a&gt;is an admission controller. It's available both as a binary and as a Kubernetes tool installed via a Helm Chart. It ensures that only signed images are deployed. It's an invaluable tool for safeguarding your AKS cluster by preventing unsigned container images from being deployed.&lt;/p&gt;

&lt;p&gt;To install it on an existing Kubernetes cluster, run the following commands:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://raw.githubusercontent.com/deislabs/ratify/main/helmfile.yaml | helmfile &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; - 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the installation is complete, any pods that use an unsigned image will be prevented from deploying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As software supply chain security takes center stage, the importance of integrating open-source tools into your CI/CD pipeline cannot be overstated. By adopting these tools, you'll shield your deployments against potential threats, contributing to a more secure software supply chain.&lt;/p&gt;

&lt;p&gt;Learn how to add these tools to your existing pipelines, with this step-by-step &lt;a href="https://github.com/duffney/secure-supply-chain-on-aks" rel="noopener noreferrer"&gt;workshop&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>containers</category>
      <category>opensource</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>What is Base62 Conversion?</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Thu, 27 Jul 2023 20:58:06 +0000</pubDate>
      <link>https://dev.to/joshduffney/what-is-base62-conversion-13o0</link>
      <guid>https://dev.to/joshduffney/what-is-base62-conversion-13o0</guid>
      <description>&lt;p&gt;In today's digital age, we are constantly dealing with large amounts of data, and finding efficient ways to store and process it has become crucial. &lt;/p&gt;

&lt;p&gt;One technique that stands out is base62 conversion - a powerful encoding scheme that allows for compact data storage and is widely used in short URL services. &lt;/p&gt;

&lt;p&gt;In this article, we'll explore the concept of base62 conversion, understand how it works, and see how it can be implemented using Golang.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Base62?
&lt;/h3&gt;

&lt;p&gt;At its core, base62 is an encoding system that derives its name from the characters it uses for encoding. The base62 system employs a set of sixty-two characters, consisting of digits 0-9, lowercase letters a-z, and uppercase letters A-Z. If we break down the character count, we have 10 digits + 26 lowercase letters + 26 uppercase letters, resulting in a total of 62 characters.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Conversion Process
&lt;/h3&gt;

&lt;p&gt;To convert a decimal number to base62, we follow a simple process using long division:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Divide the decimal number by 62.&lt;/li&gt;
&lt;li&gt;Record the quotient and keep track of the remainder.&lt;/li&gt;
&lt;li&gt;Map the remainder to the corresponding base62 character using its position within the base62 digits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's walk through an example to make things clearer. Suppose we want to convert the decimal number 123 to base62:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start by dividing 123 by 62:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;123 ÷ 62 = 1 quotient 61 remainder&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, divide 1 by 62:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 ÷ 62 = 0 quotient 1 remainder&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, we map the remainders to their respective base62 characters. Since the base62 system starts its index at zero, the last character "Z" is at position sixty-one, and "1" is at position one.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Therefore, the decimal number 123 converts to "1Z" in base62.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse Conversion: Base62 to Decimal
&lt;/h3&gt;

&lt;p&gt;So far, we've looked at converting a decimal number to base62. But what if we want to go the other way around? How do we convert a base62 number back to its decimal equivalent? Let's find out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the position value of each base62 character.&lt;/li&gt;
&lt;li&gt;Calculate the powers of 62 for each character based on its position.&lt;/li&gt;
&lt;li&gt;Multiply each character by its corresponding power of 62.&lt;/li&gt;
&lt;li&gt;Sum up the results to obtain the decimal representation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's demonstrate this process using the base62 number "1Z" we obtained earlier:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Determine the position value of each character:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"1" is in the 1st position.&lt;/li&gt;
&lt;li&gt;"Z" is in the 61st position.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Calculate the powers of 62:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The power of "Z" is determined by using the formula &lt;code&gt;len(base62) - i - 1&lt;/code&gt; (where &lt;code&gt;i&lt;/code&gt; represents the position of the digit):&lt;/li&gt;
&lt;li&gt;For "Z" in position 0, the calculation is &lt;code&gt;2 - 0 - 1 = 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For "1" in position 1, the calculation is &lt;code&gt;2 - 1 - 1 = 0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Convert back to decimals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiply each digit by its power of 62:

&lt;ul&gt;
&lt;li&gt;1 * 62^1 = 62&lt;/li&gt;
&lt;li&gt;Z * 62^0 = 61&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sum up the results: 62 + 61 = 123&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As we can see, we successfully obtained the original decimal number 123 from the base62 representation "1Z".&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Base62 Conversion in Golang
&lt;/h3&gt;

&lt;p&gt;Now that we understand how base62 conversion works, let's explore how we can implement it using the Go programming language.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;base62Digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;convertToBase62&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;base62&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;62&lt;/span&gt;
        &lt;span class="n"&gt;base62&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base62Digits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;base62&lt;/span&gt;
        &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="m"&gt;62&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base62&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;decimalNumber&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;
    &lt;span class="n"&gt;base62Number&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;convertToBase62&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decimalNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base62Number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet, we have a &lt;code&gt;convertToBase62&lt;/code&gt; function that takes a decimal number as input and returns its base62 equivalent. The function uses successive division to calculate the remainders and builds the base62 representation by mapping each remainder to its respective character from the &lt;code&gt;base62Digits&lt;/code&gt; string. Finally, we divide the number by 62 to continue the division process until the quotient becomes zero. The &lt;code&gt;main&lt;/code&gt; function demonstrates how to use the &lt;code&gt;convertToBase62&lt;/code&gt; function by converting the decimal number 123 to base62 and printing the result.&lt;/p&gt;

&lt;p&gt;Feel free to run the code on the Go Playground to see it in action: &lt;a href="https://play.golang.com/p/DmFYZXWdzDU" rel="noopener noreferrer"&gt;Go Playground - Base62 Conversion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Base62 conversion provides a straightforward and efficient way to represent numbers using a limited character set. It's particularly useful for applications that require compact data storage, such as short URL services. By leveraging the power of base62, developers can create more concise and user-friendly representations of numbers without sacrificing accuracy.&lt;/p&gt;

&lt;p&gt;So, the next time you come across a shortened URL or need to optimize your data storage, think about the magic of base62 conversion and how it enables efficient handling of large numbers with just sixty-two characters! 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it now challenge&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a second function the reverses the base62 digits into their original decimal.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>math</category>
      <category>go</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Understanding Command-line (CLI) syntax for Gophers</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Sat, 01 Jul 2023 20:08:21 +0000</pubDate>
      <link>https://dev.to/joshduffney/understanding-command-line-cli-syntax-for-gophers-2ai0</link>
      <guid>https://dev.to/joshduffney/understanding-command-line-cli-syntax-for-gophers-2ai0</guid>
      <description>&lt;p&gt;The 2022 Go Developer Survey reveals that building CLI applications ranks as the &lt;strong&gt;second most popular application of Go&lt;/strong&gt;, with an impressive majority of over &lt;strong&gt;60% of respondents&lt;/strong&gt; expressing their preference for writing in Go.&lt;/p&gt;

&lt;p&gt;What this data means is, if you want to become a productive Go developer, learning how to build excellent and user friendly CLI tools is necessary. But, before you begin diving into the depths of &lt;code&gt;os.Args&lt;/code&gt;, the &lt;code&gt;flags&lt;/code&gt; package, and the &lt;code&gt;Cobra&lt;/code&gt; framework, it essential that you have a mental model for how command-line applications word and the terminology used to describe them.&lt;/p&gt;

&lt;p&gt;In this article, &lt;em&gt;you'll dig into the world of CLI syntax, unravel its complexities, and empower you to navigate the command line with confidence&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Natural language comparison
&lt;/h2&gt;

&lt;p&gt;Natural language comparison is a term used to describe a user's ability to interact with a CLI application by expressing their intentions in familiar, human-like language, which allows for a more intuitive and user-friendly experience.&lt;/p&gt;

&lt;p&gt;The Cobra Framework, the most popular CLI framework written in Go, suggests the following pattern in their concept's documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7rwhn5j6yq0laxhhkg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7rwhn5j6yq0laxhhkg6.png" alt="Natrual language comparison" width="650" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example: &lt;code&gt;APPNAME&lt;/code&gt; is the executable, &lt;code&gt;NOUN&lt;/code&gt; is the &lt;em&gt;argument&lt;/em&gt;, &lt;code&gt;VERB&lt;/code&gt; is a &lt;em&gt;command&lt;/em&gt;, and &lt;code&gt;ADJECTIVE&lt;/code&gt; is an option, also called a flag.&lt;/p&gt;

&lt;p&gt;If you used CLIs for any length of time you'll have noticed that not every CLI follows this exact pattern, another common implementation of the natural language comparison is verb noun, which PowerShell, a popular scripting language uses. With that said, noun verb is a much more common pattern among CLIs.&lt;/p&gt;

&lt;p&gt;Now that you've begun to build your mental model for building CLIs with natural language. Let's dive deeper into what exactly arguments are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Arguments
&lt;/h2&gt;

&lt;p&gt;Arguments are the nouns or things that commands act upon. &lt;/p&gt;

&lt;p&gt;They are the values that provide data or instruction to a command when it's invoked. Influencing the commands behavior or determining the task to that's performed.&lt;/p&gt;

&lt;p&gt;For example, in the command &lt;code&gt;touch file.txt&lt;/code&gt;, "touch" is the command, and "file.txt" is the argument or noun, indicating the file to be acted upon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvorumen827tuaxck26xc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvorumen827tuaxck26xc.png" alt="touch command example" width="782" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: "argument" and "parameter" are sometimes used interchangeably, but they have distinct meanings.&lt;/p&gt;

&lt;p&gt;Arguments are values passed during command execution, while parameters are variables defined within functions or methods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Exploring Commands
&lt;/h2&gt;

&lt;p&gt;Commands are - verbs - the main actions in a CLI application. &lt;/p&gt;

&lt;p&gt;You use them by typing the executable's name followed by arguments or options\flags. Which allows users to perform specific operations.&lt;/p&gt;

&lt;p&gt;For example, the following commands each represent different actions that can be executed within the respective CLI applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;Verb]

&lt;span class="nb"&gt;mkdir&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;Verb]

git commit
&lt;span class="o"&gt;[&lt;/span&gt;Command] &lt;span class="o"&gt;[&lt;/span&gt;Verb]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simple command-line tools like &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;mkdir&lt;/code&gt; the names of the commands themselves are the verbs. &lt;/p&gt;

&lt;p&gt;But that's not the case for more complex CLIs like &lt;code&gt;git&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt;. With more complex CLIs the verb shifts to the subcommands.&lt;/p&gt;

&lt;p&gt;Subcommands create a hierarchical structure within a CLI, grouping related actions together. &lt;/p&gt;

&lt;p&gt;They allow users to navigate and access different sets of functionality based on the context or domain they're working with. &lt;/p&gt;

&lt;p&gt;For example, within &lt;code&gt;git&lt;/code&gt;, there are subcommands like "git commit," "git pull," or "git branch."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git commit
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git pull
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each subcommand focuses on a specific aspect of version control within the overall "git" command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Flags
&lt;/h2&gt;

&lt;p&gt;Flags, also known as options, modify the behavior of a command or subcommand. &lt;/p&gt;

&lt;p&gt;They act as adjectives, enabling users to customize the command's operation by specifying specific settings, enabling, or disabling features, or providing additional information.&lt;/p&gt;

&lt;p&gt;Flags are typically represented by prefixed characters (short-flag) or words (long-flag) that indicate their purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="c"&gt;#short-flag&lt;/span&gt;
go &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="c"&gt;#long-flag&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above command, the &lt;code&gt;-v&lt;/code&gt; and &lt;code&gt;--version&lt;/code&gt; flag instruct the &lt;code&gt;go&lt;/code&gt; command to return version information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Short flags are reserved for the most commonly used flags only. Not every option or flag gets a short flag assigned to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, you explored the components of natural language comparison: arguments, commands, and flags. &lt;/p&gt;

&lt;p&gt;Arguments represent the nouns or entities acted upon by commands, while commands themselves are the verbs or actions performed within the CLI. Flags, acting as adjectives, modify the behavior of commands or subcommands, allowing users to customize their operations. &lt;/p&gt;

&lt;p&gt;Understanding these components is crucial for building effective and user-friendly CLI applications. By leveraging natural language comparison, CLI developers can create intuitive and accessible interfaces that streamline command execution and enhance the overall user experience.&lt;/p&gt;

</description>
      <category>cli</category>
      <category>beginners</category>
      <category>programming</category>
      <category>go</category>
    </item>
    <item>
      <title>Setup a GitHub Action for signing container images with Notary</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Wed, 02 Nov 2022 15:22:46 +0000</pubDate>
      <link>https://dev.to/azure/setup-a-github-action-for-signing-container-images-with-notary-4m3k</link>
      <guid>https://dev.to/azure/setup-a-github-action-for-signing-container-images-with-notary-4m3k</guid>
      <description>&lt;p&gt;In this article, you'll configure a GitHub Action to digitally sign container images hosted on Azure Container Registry with Notary.&lt;/p&gt;

&lt;p&gt;"&lt;em&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline&lt;/em&gt;". You'll use it to automate the process of building, signing, and pushing a Docker image to Azure Container Registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create GitHub Action Secrets
&lt;/h2&gt;

&lt;p&gt;A GitHub Action Secret allows you to securely pass a value to the GitHub Action runner, which prevents sensitive data from being displayed in the build logs. You'll &lt;/p&gt;

&lt;p&gt;Create several GitHub Action Secrets to securely pass your Azure and Notation credentials to your workflow.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;code&gt;Settings&lt;/code&gt; on the repository&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Secrets&lt;/code&gt;, then &lt;code&gt;Actions&lt;/code&gt;, &lt;/li&gt;
&lt;li&gt;Then click &lt;code&gt;New repository secret&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lo13pen5socjymlrebm.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Enter the secret name and value&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add secret&lt;/strong&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh57pei7b5n849ctul87.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Repeat steps 3-5&lt;/strong&gt; for each secrets listed below.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AZURE_CREDENTIALS&lt;/td&gt;
&lt;td&gt;JSON object of the Azure Service Principal output from the &lt;code&gt;az ad sp create-for-rbac&lt;/code&gt; command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOTATION_USERNAME&lt;/td&gt;
&lt;td&gt;Name of the Azure Container Registry token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOTATION_PASSWORD&lt;/td&gt;
&lt;td&gt;Password of the Azure Container Registry token&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;&lt;br&gt;
In case you didn't save the JSON output, rerunning the &lt;code&gt;az ad sp create-for-rbac&lt;/code&gt; command will reset the password of the service principal and generate a new JSON object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Update the GitHub Workflow
&lt;/h2&gt;

&lt;p&gt;With the Azure infrastructure deployed and your GitHub Actions secrets configured, the last thing you have to do is update the GitHub workflow file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the GitHub workflow file located at &lt;code&gt;.github/workflows/docker-image.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Replace all &lt;em&gt;&lt;/em&gt; values from the table below with the appropriate information.&lt;/li&gt;
&lt;li&gt;Issue the &lt;code&gt;git add&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt;, and &lt;code&gt;git push&lt;/code&gt; commands to push your changes to GitHub.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Placeholder&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;AzCli command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;registry-name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the Azure Container Registry&lt;/td&gt;
&lt;td&gt;az acr list --query '[].name' -o tsv&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;key-name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the signing certificate&lt;/td&gt;
&lt;td&gt;az keyvault certificate list --vault-name $vaultName --query '[].name' -o tsv&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;certificate-key-id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Key Id of the Azure Key Vault certificate&lt;/td&gt;
&lt;td&gt;az keyvault certificate show --name example --vault-name $vaultName  --query kid -o tsv&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;$vaultName&lt;/code&gt; with the name of your Azure Key Vault instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm the container image was signed
&lt;/h2&gt;

&lt;p&gt;Congratulations! 🎉 You've made it to the end of the tutorial. &lt;/p&gt;

&lt;p&gt;Your final tasks are to confirm the workflow executed properly and that there is a digital signature attached to the container image hosted on Azure Container Registry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;View the GitHub workflow run&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To confirm your workflow executed properly, &lt;strong&gt;open the repository&lt;/strong&gt; on GitHub and click the &lt;strong&gt;Actions&lt;/strong&gt; tab. You should see a workflow run that is green.&lt;/li&gt;
&lt;li&gt;Click to expand the steps within the workflow and examine the actions taken to sign the container image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Confirm the digital signature exists&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Azure portal by going to &lt;a href="//portal.azure.com"&gt;portal.azure.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to your Azure Container Registry instance&lt;/li&gt;
&lt;li&gt;Under Services, select &lt;em&gt;Repositories&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Select the web-app-sample repository&lt;/li&gt;
&lt;li&gt;Select the most recent tag&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Artifact&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Confirm cncf.notary.v2.signature exists on the artifact&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>cloudnative</category>
      <category>security</category>
      <category>devops</category>
      <category>azure</category>
    </item>
    <item>
      <title>Deploy Azure Key Vault and Azure Container Registry for Document Signing with Notary</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Wed, 02 Nov 2022 15:22:29 +0000</pubDate>
      <link>https://dev.to/azure/deploy-azure-key-vault-and-azure-container-registry-for-document-signing-with-notary-3o7k</link>
      <guid>https://dev.to/azure/deploy-azure-key-vault-and-azure-container-registry-for-document-signing-with-notary-3o7k</guid>
      <description>&lt;p&gt;In this article, you'll deploy an Azure Key Vault and Azure Container Registry instance with Terraform.&lt;/p&gt;

&lt;p&gt;Terraform is an infrastructure as code tool that lets you define your infrastructure resources with readable configuration files. You'll use it to deploy the necessary Azure infrastructure that your GitHub workflow depends on for signing container images.&lt;/p&gt;

&lt;p&gt;By the end of this article, you'll have deployed all the Azure resources needed to digitally sign container images with Notary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a service principal
&lt;/h2&gt;

&lt;p&gt;Your GitHub workflow and Terraform both need an service principal for authenticating with Azure. &lt;/p&gt;

&lt;p&gt;Create a new service principal by running the following &lt;code&gt;az&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az ad sp create-for-rbac &lt;span class="nt"&gt;--name&lt;/span&gt; notary-gh-sp &lt;span class="nt"&gt;--role&lt;/span&gt; contributor &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--scopes&lt;/span&gt; /subscriptions/&amp;lt;subscriptionId&amp;gt; &lt;span class="nt"&gt;--sdk-auth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;subscriptionId&lt;/code&gt; with your Azure subscriptions Id.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; &lt;em&gt;Store the JSON object in a secure place&lt;/em&gt;. You'll use it to create a credential to authenticate to Azure with the Azure Login GitHub Action.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Export Terraform environment variables
&lt;/h2&gt;

&lt;p&gt;One of several ways to pass credentials to Terraform is through environment variables, without these variables Terraform will failed to authenticate to Azure.&lt;/p&gt;

&lt;p&gt;Use the following &lt;code&gt;export&lt;/code&gt; commands to set the necessary environment variables for the Azure Terraform provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_SUBSCRIPTION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ARM_TENANT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"00000000-0000-0000-0000-000000000000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the &lt;code&gt;00000000&lt;/code&gt; with the values provided in the JSON from the &lt;code&gt;az ad sp create-for-rbac&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply the Terraform configuration
&lt;/h2&gt;

&lt;p&gt;With the service principal created and the environment variable set, you're now ready to apply the Terraform configuration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Change directories to the &lt;code&gt;terraform&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Initialize Terraform&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apply the Terraform configuration&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;When prompted type &lt;code&gt;yes&lt;/code&gt; into the terminal and hit &lt;strong&gt;enter&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakkmzxd98u8oyb4g00bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakkmzxd98u8oyb4g00bv.png" alt="Terraform apply command" width="494" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an Azure Container Registry Token
&lt;/h2&gt;

&lt;p&gt;Your last task in this tutorial is to create a token that Notation, the command-line tool for Notary, will use to authenticate to the registry when signing images.&lt;/p&gt;

&lt;p&gt;Run the following command to create an ACR token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az acr token create &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--name&lt;/span&gt; exampleToken &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--registry&lt;/span&gt; &amp;lt;registryName&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--scope-map&lt;/span&gt; _repositories_admin &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'credentials.passwords[0].value'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--only-show-errors&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--output&lt;/span&gt; tsv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; &lt;em&gt;Store the password value in a secure place&lt;/em&gt;. You'll need to store it as a GitHub secret later in the demo.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>cloudnative</category>
      <category>security</category>
      <category>devops</category>
      <category>azure</category>
    </item>
    <item>
      <title>Signing Container with Notary and GitHub Actions on Azure</title>
      <dc:creator>Josh Duffney</dc:creator>
      <pubDate>Wed, 02 Nov 2022 15:22:16 +0000</pubDate>
      <link>https://dev.to/azure/signing-container-with-notary-and-github-actions-on-azure-1jlo</link>
      <guid>https://dev.to/azure/signing-container-with-notary-and-github-actions-on-azure-1jlo</guid>
      <description>&lt;p&gt;In this series, you'll learn how to digitally sign a container image hosted in Azure Container Registry using Notary with a GitHub workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://notaryproject.dev/" rel="noopener noreferrer"&gt;Notary&lt;/a&gt; is a CNCF project that provides a set of tools that help you sign, store, and verify OCI artifacts using OCI-conformant registries. Digitally signing artifacts is one of many steps you can take to secure your software supply chains and improve the security of your software.&lt;/p&gt;

&lt;p&gt;By the end of this series, you'll have a GitHub workflow that builds a simple web app container image, pushes that image to ACR, and signs the container image with Notation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dynamics-nav/how-to--sign-up-for-a-microsoft-azure-subscription" rel="noopener noreferrer"&gt;Azure subscription&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/signup" rel="noopener noreferrer"&gt;GitHub account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.hashicorp.com/tutorials/terraform/install-cli" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a new repository with the template
&lt;/h2&gt;

&lt;p&gt;A sample repository is provided to give you all the scaffolding needed to setup a GitHub workflow for signing container images using Notary. &lt;/p&gt;

&lt;p&gt;Complete the following steps to create a new repository using the template.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://github.com/Azure-Samples/acr-notary-sign-images-sample" rel="noopener noreferrer"&gt;acr-notary-sign-images-sample&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Use this template&lt;/strong&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykfurboiwhe6vrndhxl1.png" alt="Image description" width="800" height="149"&gt;
&lt;/li&gt;
&lt;li&gt;Select an &lt;em&gt;Owner&lt;/em&gt; and enter a &lt;em&gt;Repository name&lt;/em&gt;, then click &lt;strong&gt;Create repository from template&lt;/strong&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4diprzfxrthgp9pjn4mw.png" alt="Image description" width="794" height="591"&gt;
&lt;/li&gt;
&lt;li&gt;Wait for the template to create, then the click &lt;strong&gt;Code&lt;/strong&gt; button Under the &lt;em&gt;Clone&lt;/em&gt; section, copy the URL.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1qzdrmiasdcooatlqss.png" alt="Image description" width="486" height="323"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next open a terminal window and use the &lt;code&gt;git clone&lt;/code&gt; command to pull down the new repository.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;yourRepoURLHere&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Continue to the next article to deploy the required Azure resources.&lt;/p&gt;

</description>
      <category>cloudnative</category>
      <category>security</category>
      <category>devops</category>
      <category>azure</category>
    </item>
  </channel>
</rss>
