<?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: Chainguard</title>
    <description>The latest articles on DEV Community by Chainguard (@chainguard).</description>
    <link>https://dev.to/chainguard</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%2Forganization%2Fprofile_image%2F5643%2F04d222cb-d58e-42ca-9e15-f35fd91dc419.png</url>
      <title>DEV Community: Chainguard</title>
      <link>https://dev.to/chainguard</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chainguard"/>
    <language>en</language>
    <item>
      <title>Deep Dive 🤿: Where Does Grype Data Come From?</title>
      <dc:creator>Patrick Smyth</dc:creator>
      <pubDate>Tue, 12 Nov 2024 16:16:18 +0000</pubDate>
      <link>https://dev.to/chainguard/deep-dive-where-does-grype-data-come-from-n9e</link>
      <guid>https://dev.to/chainguard/deep-dive-where-does-grype-data-come-from-n9e</guid>
      <description>&lt;p&gt;Grype is a vulnerability scanner for container images and filesystems. It's  developed by &lt;a href="https://anchore.com/" rel="noopener noreferrer"&gt;Anchore&lt;/a&gt; and written in Golang. When you point Grype at a container image, it will scan the files and folders on that image, compare what it finds to a database of CVEs (known vulnerabilities), and spit out a report telling you what CVEs have been detected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anchore/grype" rel="noopener noreferrer"&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%2Fb2fym4d9sjjkf3f6q2yt.png" alt="Grype logo, it's like a creepy monster guy" width="234" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We like Grype at &lt;a href="https://www.chainguard.dev/" rel="noopener noreferrer"&gt;Chainguard&lt;/a&gt; because it's open source, customizable, and reliable enough to integrate into our CI and CVE remediation workflows. (You can read more about &lt;a href="https://www.chainguard.dev/unchained/why-chainguard-uses-grype-as-its-first-line-of-defense-for-cves" rel="noopener noreferrer"&gt;why we like Grype in this post&lt;/a&gt;.)&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%2F0u8p5jtf43wxql1zfozr.png" class="article-body-image-wrapper"&gt;&lt;img alt="Expertly photoshopped I Like Ike pin edited to read I Like Grype" 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%2F0u8p5jtf43wxql1zfozr.png" width="794" height="596"&gt;&lt;/a&gt;&lt;br&gt;I know, my photoshop skills scare even me sometimes.
  &lt;/p&gt;

&lt;p&gt;In this article, we'll answer a question that comes up frequently: where does Grype's vulnerability data come from? In the process, we'll take a look at Grype's open data pipe line and do some light analysis of the vulnerability data that Grype uses to scan containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Grype Works
&lt;/h2&gt;

&lt;p&gt;If you haven't used Grype before, here's a brief overview of how it works.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You point Grype at a container image (or filesystem).&lt;/li&gt;
&lt;li&gt;Grype downloads a fresh instance of its &lt;code&gt;vulnerability.db&lt;/code&gt; database, then scans the image for specific packages, files, configurations, and so on, building a manifest in the form of a Software Bill of Materials (SBOM) itemizing the software contained in the image. (Under the hood, Grype uses a sister tool, &lt;a href="https://github.com/anchore/syft" rel="noopener noreferrer"&gt;Syft&lt;/a&gt;, for this step.) &lt;/li&gt;
&lt;li&gt;Grype then compares the specific versions of each package against the vulnerability data in its database. &lt;/li&gt;
&lt;li&gt;Finally, a list of CVEs detected in the image is returned to the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a comprehensive overview of Grype's functionality, check out &lt;a href="https://edu.chainguard.dev/chainguard/chainguard-images/working-with-images/scanners/grype-tutorial/" rel="noopener noreferrer"&gt;Using Grype to Scan Container Images for Vulnerabilities&lt;/a&gt; on Chainguard Academy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grype's Data Sources
&lt;/h2&gt;

&lt;p&gt;Grype relies on a set of upstream providers for its vulnerability data. As of November 2024, the list of providers includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://secdb.alpinelinux.org" rel="noopener noreferrer"&gt;Alpine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alas.aws.amazon.com/AL2/alas.rss%20&amp;amp;%20https://alas.aws.amazon.com/AL2022/alas.rss" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://security-tracker.debian.org/tracker/data/json%20&amp;amp;%20https://salsa.debian.org/security-tracker-team/security-tracker/raw/master/data/DSA/list" rel="noopener noreferrer"&gt;Debian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://api.github.com/graphql" rel="noopener noreferrer"&gt;GitHub Security Advisories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://services.nvd.nist.gov/rest/json/cves/2.0" rel="noopener noreferrer"&gt;NVD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linux.oracle.com/security/oval" rel="noopener noreferrer"&gt;Oracle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/security/data/oval" rel="noopener noreferrer"&gt;RedHat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ftp.suse.com/pub/projects/security/oval" rel="noopener noreferrer"&gt;SLES&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchpad.net/ubuntu-cve-tracker" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://packages.wolfi.dev" rel="noopener noreferrer"&gt;Wolfi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the above links are to endpoints where data is provided. Chainguard is one of the upstream providers, and updating scanners like Grype on the fixed status of packages in our upstream OS, &lt;a href="https://github.com/wolfi-dev" rel="noopener noreferrer"&gt;Wolfi&lt;/a&gt;, is a key element in maintaining the low-to-no CVE status of &lt;a href="https://www.chainguard.dev/chainguard-images?utm_source=cg-devrel" rel="noopener noreferrer"&gt;Chainguard Images&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Grype's &lt;code&gt;vulnerability.db&lt;/code&gt; gets rebuilt daily from data sourced from these upstream providers. To build this database, Grype uses two open source tools, &lt;a href="https://github.com/anchore/vunnel" rel="noopener noreferrer"&gt;&lt;code&gt;vunnel&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/anchore/grype-db" rel="noopener noreferrer"&gt;&lt;code&gt;grype-db&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;vunnel&lt;/code&gt; tool downloads, standardizes, and stores vulnerability data from the above upstream providers. Basically, it accesses the various provider endpoints and stores a local vulnerability database and metadata for each provider locally. The &lt;code&gt;grype-db&lt;/code&gt; utility collates this vulnerability data, building a much smaller &lt;code&gt;vulnerability.db&lt;/code&gt; usable by Grype.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Grype Database with &lt;code&gt;vunnel&lt;/code&gt; and &lt;code&gt;grype-db&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In this section, we'll try out the &lt;code&gt;vunnel&lt;/code&gt; and &lt;code&gt;grype-db&lt;/code&gt; utilities, building a local vulnerability cache and database.&lt;/p&gt;

&lt;p&gt;Since a built-daily &lt;code&gt;vulnerability.db&lt;/code&gt; file gets downloaded every time you run a Grype scan, why would you want to build Grype's &lt;code&gt;vulnerability.db&lt;/code&gt; manually? Building manually is useful if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to use a subset of upstream sources&lt;/li&gt;
&lt;li&gt;You'd like to integrate other sources to create a custom &lt;code&gt;vulnerability.db&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You require older Grype schemas&lt;/li&gt;
&lt;li&gt;You'd like to contribute to Grype&lt;/li&gt;
&lt;li&gt;You want to understand more about Grype's upstream providers and data structure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;vunnel&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The&lt;code&gt;grype-db&lt;/code&gt; utility uses &lt;code&gt;vunnel&lt;/code&gt; under the hood, but let's first try out &lt;code&gt;vunnel&lt;/code&gt; explicitly to see how it works. You'll need Python 3 installed for this section, and I'll assume it's accessible on your system using the &lt;code&gt;python&lt;/code&gt; command. (&lt;code&gt;vunnel&lt;/code&gt; is written in Python.)&lt;/p&gt;

&lt;p&gt;First, let's create a project folder:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/vulnerability-data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a virtual environment and activate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install &lt;code&gt;vunnel&lt;/code&gt; to the activated virtual environment:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once &lt;code&gt;vunnel&lt;/code&gt; is installed, we can use the &lt;code&gt;vunnel list&lt;/code&gt; command to show the current list of providers:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alpine
amazon
chainguard
...
sles
ubuntu
wolfi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can download a local cache of provider data for any of these providers with the following (using &lt;code&gt;chainguard&lt;/code&gt; as an example provider):&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;data&lt;/code&gt; folder where all provider data used as input and the standardized output database are contained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data
└── chainguard
    ├── checksums
    ├── input
    │   └── secdb
    │       └── security.json
    ├── metadata.json
    └── results
        └── results.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;grype-db&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;grype-db&lt;/code&gt; utility can pull provider data with &lt;code&gt;vunnel&lt;/code&gt; under the hood, and can also collect and package up this data into the &lt;code&gt;vulnerability.db&lt;/code&gt; file used by Grype.&lt;/p&gt;

&lt;p&gt;In the following, we'll use &lt;code&gt;grype-db&lt;/code&gt; to download all provider data using &lt;code&gt;vunnel&lt;/code&gt; under the hood, then build the &lt;code&gt;vulnerability.db&lt;/code&gt; file. The process of downloading all provider data can take some time and uses about 8 GB of disk space.&lt;/p&gt;

&lt;p&gt;First, download the &lt;code&gt;grype-db&lt;/code&gt; script to the project folder we created previously:&lt;br&gt;
&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;-sSfL&lt;/span&gt; https://raw.githubusercontent.com/anchore/grype-db/main/install.sh | sh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd like to build from all available data, you'll need a GitHub token capable of authenticating as a user. This is because GitHub rate limits API access for non-authenticated users. You can follow these &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens" rel="noopener noreferrer"&gt;instructions provided by GitHub&lt;/a&gt;, but in short head to &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;this token settings page on GitHub&lt;/a&gt;. Remember to safeguard your token as you would a password, and I recommend creating a scoped and short-lived (i.e. 7 days) token.&lt;/p&gt;

&lt;p&gt;Once you have your token, create a configuration file for &lt;code&gt;grype-db&lt;/code&gt; in our &lt;code&gt;~/vulnerability-data&lt;/code&gt; project folder. First, set your generated GitHub token to an environment variable:&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;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, generate the config in our &lt;code&gt;~/vulnerability-data&lt;/code&gt; project folder:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; ~/vulnerability-data/.grype-db.yaml
provider:
  vunnel:
    executor: local
    generate-configs: true
    env:
      GITHUB_TOKEN: &lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the &lt;code&gt;grype-db&lt;/code&gt; script and the &lt;code&gt;.grype-db.yaml&lt;/code&gt; configuration file in our project folder. Let's run a command that will pull all provider data, create a database file, and package it up for inclusion in a CI or other workflow. (For this step, you'll need &lt;code&gt;vunnel&lt;/code&gt; available, so the virtual environment created in the previous section on vunnel will need to still be activated, and you should run this command from the project folder where we downloaded the &lt;code&gt;grype-db&lt;/code&gt; script.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./grype-db &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Downloading and processing all provider data can take a long time, possibly hours, so go watch &lt;a href="https://www.youtube.com/watch?v=jG3iNpo3RnQ" rel="noopener noreferrer"&gt;Master of the Flying Guillotine&lt;/a&gt; or get some work done, I guess. ☕&lt;/p&gt;

&lt;p&gt;Once the process completes, a &lt;code&gt;build&lt;/code&gt; folder will have been created in our &lt;code&gt;~/vulnerability-data/&lt;/code&gt; project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build
├── listing.json
├── metadata.json
├── provider-metadata.json
├── vulnerability.db
└── vulnerability-db_v5_2024-11-05T19:09:08Z_1730848219.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vulnerability.db&lt;/code&gt; database should be the same as the database built daily for use by Grype.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grype's &lt;code&gt;vulnerability.db&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I plan on following up this post with an analysis of the data in Grype's &lt;code&gt;vulnerability.db&lt;/code&gt; database, but here are some quick notes on the structure of this SQLite3 file:&lt;/p&gt;

&lt;p&gt;When Grype runs, it checks against the last time the database was updated. If it's been longer than a day (Grype rebuilds the database daily) a new &lt;code&gt;vulnerability.db&lt;/code&gt; is downloaded to a cache. - On Linux, it's stored in &lt;code&gt;~/.cache/grype/db/5/vulnerability.db&lt;/code&gt;, where the numbered folder (&lt;code&gt;5&lt;/code&gt;) corresponds to the current Grype schema version number.) On Mac OS, it's stored in &lt;code&gt;~/Library/Caches/grype/db&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;vulnerability.db&lt;/code&gt; database has five tables, but only two have significant data. The &lt;code&gt;vulnerability_metadata&lt;/code&gt; table stores information on CVEs as they apply on a per-platform basis. The entities in the &lt;code&gt;vulnerability&lt;/code&gt; table represent vulnerabilities as they apply to specific package versions.&lt;/p&gt;

&lt;p&gt;The platforms with the most vulnerability metadata entries are Ubuntu, NVD (NIST's National Vulnerability Database,, and Susa. &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%2Fqcpz0a1l3xc7u91rqj9p.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%2Fqcpz0a1l3xc7u91rqj9p.png" alt="Chart showing the upstream providers, Ubuntu is the biggest, Chainguard is pretty new" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While these results are somewhat interesting when considering where Grype data comes from, the number here reflects many factors, mainly the date the provider started recording vulnerabilities and, for platform-specific providers, the attack surface of the platform. Other details such as duplication of distros lower the signal here and would require more analysis to parse out.&lt;/p&gt;

&lt;p&gt;We can also check the number of vulnerability metadata entries by year:&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%2Fzwus00tu84kwuwpdv09k.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%2Fzwus00tu84kwuwpdv09k.png" alt="Chart showing number of metadata entries ramping up from the late 90s andstabilizing around 2016-2017" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This chart mainly shows a movement toward maturity in the ecosystem, leading to some stability after 2016-2017. (The early twenty-teens were a time of rapid development in cloud technologies in particular.)&lt;/p&gt;

&lt;p&gt;Digging into the data in Grype's &lt;code&gt;vulnerability.db&lt;/code&gt; can also help to answer much more specific questions about how CVE affect different platforms. For example, imagine we host a mailserver and we wish to know the fixed status of &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2024-37383" rel="noopener noreferrer"&gt;CVE-2024-37383&lt;/a&gt;, a  known-exploited vulnerability which allows cross-site scripting in RoundCube, a webmail client. We can narrow the data in the vulnerability table to answer this question:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;fix_state&lt;/th&gt;
      &lt;th&gt;namespace&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed&lt;/td&gt;
      &lt;td&gt;nvd:cpe&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed&lt;/td&gt;
      &lt;td&gt;debian:distro:debian:11&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed&lt;/td&gt;
      &lt;td&gt;debian:distro:debian:12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed&lt;/td&gt;
      &lt;td&gt;debian:distro:debian:13&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed&lt;/td&gt;
      &lt;td&gt;debian:distro:debian:unstable&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;not-fixed&lt;/td&gt;
      &lt;td&gt;ubuntu:distro:ubuntu:20.04&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;not-fixed&lt;/td&gt;
      &lt;td&gt;ubuntu:distro:ubuntu:22.04&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed&lt;/td&gt;
      &lt;td&gt;ubuntu:distro:ubuntu:23.10&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In a follow-up post, I'll show you how to load Grype's &lt;code&gt;vulnerability.db&lt;/code&gt; database into &lt;a href="https://pandas.pydata.org/docs/" rel="noopener noreferrer"&gt;Pandas&lt;/a&gt; to get a better sense of Grype's data schema and how it can be used to answer specific questions in platform security and broader CVE trends.&lt;/p&gt;

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

&lt;p&gt;One of the most remarkable aspects of the Grype image scanner is the openness of its data pipeline. This is great for transparency and makes Grype's &lt;code&gt;vulnerability.db&lt;/code&gt; into a more flexible and useful tool. If you've found this post useful, let us know and maybe you'll see more security deep dives like this. 🛡️🤿 You can follow Chainguard on &lt;a href="https://dev.to/t/chainguard"&gt;dev.to&lt;/a&gt; or &lt;a href="https://www.linkedin.com/company/chainguard-dev/posts/?feedView=all" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://www.chainguard.dev/newsletter?utm_source=cg-devrel" rel="noopener noreferrer"&gt;sign up for our newsletter&lt;/a&gt; to keep in touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anchore.com/blog/build-your-own-grype-database/" rel="noopener noreferrer"&gt;Build Your Own Grype Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.chainguard.dev/unchained/why-chainguard-uses-grype-as-its-first-line-of-defense-for-cves" rel="noopener noreferrer"&gt;Why Chainguard uses Grype as its first line of defense for CVEs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anchore.com/software-supply-chain-security/open-source-container-vulnerability-scanning-tools" rel="noopener noreferrer"&gt;A Guide to Vulnerability Scanning with Open Source Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anchore/grype-db" rel="noopener noreferrer"&gt;Utility: &lt;code&gt;grype-db&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anchore/vunnel" rel="noopener noreferrer"&gt;Utility: &lt;code&gt;vunnel&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anchorecommunity.discourse.group/t/does-grype-fetches-data-from-different-sources-or-only-from-nvd/136" rel="noopener noreferrer"&gt;Forum Post: Where Does Grype Data Come From?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>security</category>
      <category>docker</category>
    </item>
    <item>
      <title>On the Supply Chain Security Trail with Docker Scout</title>
      <dc:creator>Adrian Mouat</dc:creator>
      <pubDate>Tue, 18 Jun 2024 10:23:19 +0000</pubDate>
      <link>https://dev.to/chainguard/on-the-supply-chain-security-trail-with-docker-scout-4ma6</link>
      <guid>https://dev.to/chainguard/on-the-supply-chain-security-trail-with-docker-scout-4ma6</guid>
      <description>&lt;p&gt;I believe &lt;a href="https://www.docker.com/products/docker-scout/"&gt;Docker Scout&lt;/a&gt; is one of the best supply-chain security and scanning tools available for the container ecosystem. Before we get into details, I want to make clear that I'm a &lt;a href="https://www.docker.com/community/captains/"&gt;Docker Captain&lt;/a&gt;, so I'm not entirely unbiased here! And Scout itself has a free tier, but if you want to monitor multiple repositories, then you'll need to purchase a subscription. Now that that's clear, please read on to understand why I like Scout and what separates it from other solutions.&lt;/p&gt;

&lt;p&gt;The first thing to be aware of is that Scout isn't just a vulnerability scanner, and Docker's marketing materials tend to avoid that term. Scout takes a holistic approach to supply chain security. It starts by analysing containers and extracting or creating a complete Software Bill of Materials (SBOM) for images. With the Hub integration, these can be monitored over time for compliance to policies. Policies include – predictably – flagging images with high or critical vulnerabilities, but there are also policies such as "avoiding copy-left licences" and verifying "supply chain attestations".&lt;/p&gt;

&lt;p&gt;Other features that go beyond simple vulnerability scanning include the ability to provide recommendations for mitigations and improvements and an already impressive list of integrations (not just with the Docker Hub, but also other registries, code editors, CI/CD platforms and build tooling).&lt;/p&gt;

&lt;p&gt;There are two main parts to Scout: &lt;a href="https://scout.docker.com/"&gt;a web application&lt;/a&gt; that you can see in the below image, and a CLI tool that I will use throughout the rest of this article.&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%2Fs77lql05h6dqafu0yvv2.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%2Fs77lql05h6dqafu0yvv2.png" alt="Image description" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To dig a bit deeper, let's try analysing a few images. We said already that the starting point for everything is the SBOM, so let's try generating one. Interestingly, Scout has its own &lt;code&gt;sbom&lt;/code&gt; command, separate from &lt;code&gt;docker sbom&lt;/code&gt; which is powered by syft. I assume the Scout version will eventually replace &lt;code&gt;docker sbom&lt;/code&gt;. Here's the SBOM for the &lt;a href="https://hub.docker.com/_/redis"&gt;official Redis image&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker scout sbom --format list redis
{"level":"info","msg":"SBOM of image already cached, 133 packages indexed\n","time":"2024-06-12T14:50:48+01:00"}

           Name                  Version          Type
────────────────────────────────────────────────────────────
  acl                     2.3.1-3                deb
  …
  gosu                    (devel)                golang
  …
  redis                   7.2.5                  generic
  stdlib                  go1.18.2               golang
  stdlib                  1.18.2                 golang
  sys                     0.13.0                 golang
  sys                     0.1.0                  golang
  …
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The majority of the output is &lt;code&gt;deb&lt;/code&gt; packages, most of which I've clipped to reduce the size of the output. Scout has found these packages by reading the package manager database. This is as far as some other tooling goes, but we can see it's also picked up a few more things. Scout has found the binaries for two more applications – &lt;code&gt;redis&lt;/code&gt; and &lt;code&gt;gosu&lt;/code&gt; – that were &lt;a href="https://github.com/docker-library/redis/blob/5f08363e6d64b97a0c2e651f4bdcec6e71a32ab4/7.2/debian/Dockerfile#L79"&gt;downloaded directly from a website&lt;/a&gt; rather than being installed via the package manager. It's also analysed the &lt;code&gt;gosu&lt;/code&gt; binary and found the libraries it links against. The effectiveness of supply chain tooling is strongly tied to its performance in identifying contents – if Scout had failed to find these binaries, it would not be able to scan them for vulnerabilities or ensure they match any policies.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://images.chainguard.dev/directory/image/redis/overview"&gt;equivalent Chainguard Image&lt;/a&gt; we can see everything is installed via the apk package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker scout sbom --format list cgr.dev/chainguard/redis
{"level":"info","msg":"SBOM of image already cached, 18 packages indexed\n","time":"2024-06-12T15:05:03+01:00"}

           Name               Version       Type
────────────────────────────────────────────────────
  bash                    5.2.21-r4         apk
  busybox                 1.36.1-r10        apk
  ca-certificates         20240315-r3       apk
  ca-certificates-bundle  20240315-r3       apk
  glibc                   2.39-r6           apk
  glibc-locale-posix      2.39-r6           apk
  ld-linux                2.39-r6           apk
  libcrypt1               2.39-r6           apk
  libcrypto3              3.3.0-r9          apk
  libssl3                 3.3.0-r9          apk
  libxcrypt               4.4.36-r7         apk
  ncurses                 6.4_p20231125-r3  apk
  ncurses-terminfo-base   6.4_p20231125-r3  apk
  openssl                 3.3.0-r9          apk
  posix-libc-utils        2.39-r6           apk
  redis-7.2               7.2.5-r2          apk
  redis-cli-7.2           7.2.5-r2          apk
  wolfi-baselayout        20230201-r11      apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the full output – clearly demonstrating how much leaner Chainguard Images are.&lt;/p&gt;

&lt;p&gt;An interesting question is what happens to containers you build yourself with a multi-stage process – will it pick everything up? As a test, I used an &lt;a href="https://github.com/chainguard-dev/learning-labs-static/tree/chainguard-multistage-go"&gt;example Go application with a multi-stage build&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker scout sbom test --format=list
{"level":"info","msg":"SBOM of image already cached, 7 packages indexed\n","time":"2024-06-12T15:46:18+01:00"}

           Name             Version      Type
──────────────────────────────────────────────────
  ca-certificates         20240315-r3   apk
  ca-certificates-bundle  20240315-r3   apk
  learninglabsstatic      (devel)       golang
  stdlib                  1.22.4        golang
  stdlib                  go1.22.4      golang
  tzdata                  2024a-r2      apk
  wolfi-baselayout        20230201-r11  apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks pretty good! It has found the binary, the libraries it links against and the handful of packages that are in the Chainguard static image. If you regularly use another scanner, I'd recommend running a test like this to make sure it's aware of everything in your image. I'd also recommend running against a few different builds using different ecosystems to check that it properly identifies your Rust binaries or Java jars.&lt;/p&gt;

&lt;p&gt;One of the best features in Scout is vulnerability scanning output. I often find it a struggle to get accurate high-level info from scanners, but it's easy with Scout. The simple tweak of displaying summary output at the end, instead of before a huge list of issues that you have to scroll backwards through, is much appreciated. Here's the output from scanning the current &lt;code&gt;nginx:alpine&lt;/code&gt; image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker scout cves nginx:alpine
    ✓ SBOM of image already cached, 82 packages indexed
    ✗ Detected 2 vulnerable packages with a total of 5 vulnerabilities


## Overview

                    │       Analyzed Image
────────────────────┼──────────────────────────────
  Target            │  nginx:alpine
    digest          │  4f49228258b6
    platform        │ linux/arm64
    vulnerabilities │    0C     0H     5M     0L
    size            │ 22 MB
    packages        │ 82


## Packages and Vulnerabilities

   0C     0H     4M     0L  busybox 1.36.1-r15
pkg:apk/alpine/busybox@1.36.1-r15?os_name=alpine&amp;amp;os_version=3.19

    ✗ MEDIUM CVE-2023-42366
      https://scout.docker.com/v/CVE-2023-42366
      Affected range : &amp;lt;1.36.1-r16
      Fixed version  : 1.36.1-r16

    ✗ MEDIUM CVE-2023-42365
      https://scout.docker.com/v/CVE-2023-42365
      Affected range : &amp;lt;1.36.1-r19
      Fixed version  : 1.36.1-r19

    ✗ MEDIUM CVE-2023-42364
      https://scout.docker.com/v/CVE-2023-42364
      Affected range : &amp;lt;1.36.1-r19
      Fixed version  : 1.36.1-r19

    ✗ MEDIUM CVE-2023-42363
      https://scout.docker.com/v/CVE-2023-42363
      Affected range : &amp;lt;1.36.1-r17
      Fixed version  : 1.36.1-r17


   0C     0H     1M     0L  curl 8.5.0-r0
pkg:apk/alpine/curl@8.5.0-r0?os_name=alpine&amp;amp;os_version=3.19

    ✗ MEDIUM CVE-2024-0853
      https://scout.docker.com/v/CVE-2024-0853
      Affected range : &amp;lt;8.6.0-r0
      Fixed version  : not fixed

5 vulnerabilities found in 2 packages
  LOW       0
  MEDIUM    5
  HIGH      0
  CRITICAL  0


What's next:
    View base image update recommendations → docker scout recommendations nginx:alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also possible to only get the initial summary output by calling the &lt;code&gt;quickview&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;In my testing, I've noticed that – compared to some other scanner tooling – Scout will report less CVEs. Docker claims this is because they are less vulnerable to false positives (where a CVE is wrongly attributed to an image). The primary reason they give for this is that Scout &lt;a href="https://qubitpi.github.io/docker-docs/scout/advisory-db-sources/#how-docker-scout-makes-more-precise-matches"&gt;uses Package URLs or PURLs to match software, rather than CPEs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, Scout will report more matches than some tooling (I realise this is confusing, but I don't want to name names without providing a full breakdown and analysis). This is because Scout uses a &lt;a href="https://qubitpi.github.io/docker-docs/scout/advisory-db-sources/#docker-scouts-advisory-database-sources"&gt;wide range of sources to build its vulnerability database&lt;/a&gt; – as well as the security data from Linux distributions, it includes NVD, GitHub and other advisory data. The more data sources that are used, the more likely you are to find matches.&lt;/p&gt;

&lt;p&gt;The last sentence in the command output hints at another feature in Scout – the ability to recommend fixes. One of the biggest flaws with current scanning tools is that they often flag issues that you can do nothing about – vulnerabilities for which there is no newer package or fix available. Scout helps you both filter out issues that you can't deal with and fix the ones you can. &lt;/p&gt;

&lt;p&gt;Here's the recommendations output for the Debian-based &lt;code&gt;nginx:latest&lt;/code&gt; image, which suggests changing to a different tag to reduce CVEs, and gives details of the potential impact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker scout recommendations nginx
    ✓ SBOM of image already cached, 231 packages indexed

    i Base image was auto-detected. To get more accurate recommendations, build images with max-mode provenance attestations.
      Review docs.docker.com ↗ for more information.
      Alternatively, use  docker scout recommendations --tag &amp;lt;base image tag&amp;gt;  to pass a specific base image tag.

  Target   │  nginx:latest
    digest │  705b7f60fea5

## Recommended fixes

  Base image is  debian:12-slim

  Name            │  12-slim
  Digest          │  sha256:6dc38501802c1554f0fd858d1153a6f0e18c71006c6d0b31cf19fa778900e658
  Vulnerabilities │    0C     0H     0M    23L
  Pushed          │ 1 month ago
  Size            │ 29 MB
  Packages        │ 125
  Flavor          │ debian
  OS              │ 12
  Slim            │ ✓


  │ The base image is also available under the supported tag(s)  12.5-slim ,  bookworm-slim . If you want to display recommendations specifically
  │ for a different tag, please re-run the command using the  --tag  flag.



Refresh base image
  Rebuild the image using a newer base image version. Updating this may result in breaking changes.


            Tag            │                  Details                   │    Pushed    │       Vulnerabilities
───────────────────────────┼────────────────────────────────────────────┼──────────────┼──────────────────────────────
   12-slim                 │ Benefits:                                  │ 11 hours ago │    0C     0H     0M    23L
  Newer image for same tag │ • Same OS detected                         │              │
  Also known as:           │ • Newer image for same tag                 │              │
  • 12.5-slim              │ • Tag was pushed more recently             │              │
  • bookworm-slim          │ • Image has similar size                   │              │
  • bookworm-20240612-slim │ • Image has same number of vulnerabilities │              │
                           │ • Image contains equal number of packages  │              │
                           │ • Tag is using slim variant                │              │
                           │                                            │              │
                           │ Image details:                             │              │
                           │ • Size: 29 MB                              │              │
                           │ • Flavor: debian                           │              │
                           │ • OS: 12                                   │              │
                           │ • Slim: ✓                                  │              │
                           │                                            │              │
                           │                                            │              │
                           │                                            │              │


Change base image
  The list displays new recommended tags in descending order, where the top results are rated as most suitable.


           Tag           │                    Details                    │    Pushed    │       Vulnerabilities
─────────────────────────┼───────────────────────────────────────────────┼──────────────┼──────────────────────────────
   stable-slim           │ Benefits:                                     │ 11 hours ago │    0C     0H     0M    23L
  Tag is preferred tag   │ • Same OS detected                            │              │
  Also known as:         │ • Tag is preferred tag                        │              │
  • stable-20240612-slim │ • Tag was pushed more recently                │              │
                         │ • Image has similar size                      │              │
                         │ • Image has same number of vulnerabilities    │              │
                         │ • Image contains equal number of packages     │              │
                         │ • Tag is using slim variant                   │              │
                         │ • stable-slim was pulled 46K times last month │              │
                         │                                               │              │
                         │ Image details:                                │              │
                         │ • Size: 29 MB                                 │              │
                         │ • Flavor: debian                              │              │
                         │ • OS: 12                                      │              │
                         │ • Slim: ✓                                     │              │
                         │                                               │              │
                         │                                               │              │
                         │                                               │              │
   12                    │ Benefits:                                     │ 11 hours ago │    0C     0H     0M    23L
  Tag is latest          │ • Same OS detected                            │              │
  Also known as:         │ • Tag was pushed more recently                │              │
  • 12.5                 │ • Tag is latest                               │              │
  • bookworm             │ • Image has same number of vulnerabilities    │              │
  • bookworm-20240612    │ • Image contains equal number of packages     │              │
  • latest               │                                               │              │
                         │ Image details:                                │              │
                         │ • Size: 50 MB                                 │              │
                         │ • Flavor: debian                              │              │
                         │ • OS: 12                                      │              │
                         │                                               │              │
                         │                                               │              │
                         │                                               │              │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's take a look at the &lt;a href="https://images.chainguard.dev/directory/image/nginx/overview"&gt;Chainguard nginx image&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker scout cves chainguard/nginx
    ✓ SBOM obtained from attestation, packages found
    ✓ No vulnerable package detected


## Overview

                    │       Analyzed Image
────────────────────┼──────────────────────────────
  Target            │  chainguard/nginx:latest
    digest          │  1ffdf2c788d4
    platform        │ linux/arm64
    vulnerabilities │    0C     0H     0M     0L
    size            │ 20 MB
    packages        │ 55


## Packages and Vulnerabilities

  No vulnerable packages detected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, there are no vulnerabilities, but also note the text at the start – "SBOM obtained from attestation, packages found". Scout has used the SBOM that Chainguard provided for the image and used that to enumerate the software in the image. This level of integration and support for external tooling is essential for creating a holistic supply chain security solution – every organisation uses a different set of tools and software from various vendors and it's imperative that we figure out how to get supply chain information from everyone in a verifiable and trustable manner.  &lt;/p&gt;

&lt;p&gt;Hopefully I've shown you why I like Docker Scout. It does a good job of finding all the packages in an image but also does a great job of reporting vulnerabilities. Vulnerabilities are less likely to be false positives through the use of PURLs and the summary output is easy to grab (which is great for videos!). Docker will tell you the real strengths lie in continuously monitoring repositories and reporting on progress towards supply chain policies, and I plan to create some further content on this in the future. &lt;/p&gt;

&lt;p&gt;Please give Docker Scout a whirl (compare some &lt;a href="https://images.chainguard.dev/"&gt;Chainguard Images&lt;/a&gt;!) and let me know what you think.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@mael_balland?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Maël BALLAND&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/people-sitting-on-green-grass-field-during-daytime-7rImz-goqfQ?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>Debugging Distroless Images with kubectl and cdebug</title>
      <dc:creator>Adrian Mouat</dc:creator>
      <pubDate>Fri, 31 May 2024 14:37:01 +0000</pubDate>
      <link>https://dev.to/chainguard/debugging-distroless-images-with-kubectl-and-cdebug-1dm0</link>
      <guid>https://dev.to/chainguard/debugging-distroless-images-with-kubectl-and-cdebug-1dm0</guid>
      <description>&lt;p&gt;&lt;a href="https://twitter.com/iximiuz"&gt;Ivan Velichko&lt;/a&gt; recently made me aware of &lt;a href="https://twitter.com/iximiuz/status/1779156877395312654"&gt;issues with debugging distroless containers&lt;/a&gt; in Kubernetes with &lt;code&gt;kubectl debug&lt;/code&gt;. This blog will take a look at the problem and show you can get access to the filesystem of a distroless pod for debugging purposes.&lt;/p&gt;

&lt;p&gt;The problem Ivan found was a lack of permissions to access the filesystem of the container being debugged. This is best explained with some examples. With a regular (non-distroless) container, you can do the following to start an ephemeral debug container that shares various namespaces with the target container:&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;$ &lt;/span&gt;kubectl run nginx-pod &lt;span class="nt"&gt;--image&lt;/span&gt; nginx
pod/nginx-pod created
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl debug &lt;span class="nt"&gt;-it&lt;/span&gt; nginx-pod &lt;span class="nt"&gt;--image&lt;/span&gt; alpine &lt;span class="nt"&gt;--target&lt;/span&gt; nginx-pod
Targeting container &lt;span class="s2"&gt;"nginx-pod"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; If you don&lt;span class="s1"&gt;'t see processes from this container it may be because the container runtime doesn'&lt;/span&gt;t support this feature.
Defaulting debug container name to debugger-h4fzv.
If you don&lt;span class="s1"&gt;'t see a command prompt, try pressing enter.
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   32 101       0:00 nginx: worker process
   33 101       0:00 nginx: worker process
   34 101       0:00 nginx: worker process
   35 101       0:00 nginx: worker process
   36 101       0:00 nginx: worker process
   37 101       0:00 nginx: worker process
   38 101       0:00 nginx: worker process
   39 101       0:00 nginx: worker process
   40 101       0:00 nginx: worker process
   41 101       0:00 nginx: worker process
  308 root      0:00 /bin/sh
  317 root      0:00 ps
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(You could also just use &lt;code&gt;kubectl exec -it nginx-pod -- /bin/sh&lt;/code&gt;, but of course that's not possible in a distroless container)&lt;/p&gt;

&lt;p&gt;Note that the filesystem is the filesystem of the Alpine debug container, not the nginx container:&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;# cat /etc/os-release&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Alpine Linux"&lt;/span&gt;
&lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;alpine
&lt;span class="nv"&gt;VERSION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.19.1
&lt;span class="nv"&gt;PRETTY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Alpine Linux v3.19"&lt;/span&gt;
&lt;span class="nv"&gt;HOME_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://alpinelinux.org/"&lt;/span&gt;
&lt;span class="nv"&gt;BUG_REPORT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://gitlab.alpinelinux.org/alpine/aports/-/issues"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we can get to the nginx container filesystem via the &lt;code&gt;/proc/1/root&lt;/code&gt; filesystem. To break this down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/proc&lt;/code&gt; is a virtual filesystem created by the kernel that contains various metadata &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; refers to the process id, in this case our running nginx master process; and &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;root&lt;/code&gt; is a link to the root of the filesystem the process is running in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we can access the index.html file inside the nginx container like this:&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;# cat /proc/1/root/usr/share/nginx/html/index.html&lt;/span&gt;
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
…
/ &lt;span class="c"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's try that with the &lt;code&gt;cgr.dev/chainguard/nginx&lt;/code&gt; image, which is one of &lt;a href="https://images.chainguard.dev/"&gt;Chainguard's distroless images&lt;/a&gt;:&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;$ &lt;/span&gt;kubectl run nginx-distroless &lt;span class="nt"&gt;--image&lt;/span&gt; cgr.dev/chainguard/nginx
pod/nginx-distroless created
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl debug &lt;span class="nt"&gt;-it&lt;/span&gt; nginx-distroless &lt;span class="nt"&gt;--image&lt;/span&gt; alpine &lt;span class="nt"&gt;--target&lt;/span&gt; nginx-distroless
Targeting container &lt;span class="s2"&gt;"nginx-distroless"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; If you don&lt;span class="s1"&gt;'t see processes from this container it may be because the container runtime doesn'&lt;/span&gt;t support this feature.
Defaulting debug container name to debugger-bcr26.
If you don&lt;span class="s1"&gt;'t see a command prompt, try pressing enter.
/ # cat /proc/1/root/usr/share/nginx/html/index.html
cat: can'&lt;/span&gt;t open &lt;span class="s1"&gt;'/proc/1/root/usr/share/nginx/html/index.html'&lt;/span&gt;: Permission denied
/ &lt;span class="c"&gt;# whoami&lt;/span&gt;
root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get &lt;code&gt;Permission denied&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It turns out that the problem is that the nginx container is running as the user &lt;code&gt;nonroot&lt;/code&gt; with UID 65532, which we don't have permission to access despite being &lt;code&gt;root&lt;/code&gt; (using &lt;code&gt;--profile&lt;/code&gt; to set a different security profile didn't help either, but I suspect it might in the future). To fix this, we need our debug container to run as the same user as the nginx container. Unfortunately there's no &lt;code&gt;--user&lt;/code&gt; flag for &lt;code&gt;kubectl&lt;/code&gt;, so we need to have an image that runs as this user by default. We could create one e.g with a Dockerfile such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; 65532&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in the case of Chainguard Images there's a much easier solution. Most of our images come with &lt;code&gt;-dev&lt;/code&gt; variants that run as the same user but also include a shell and can be used for debugging, so we can do:&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;$ &lt;/span&gt;kubectl debug &lt;span class="nt"&gt;-it&lt;/span&gt; nginx-distroless &lt;span class="nt"&gt;--image&lt;/span&gt; cgr.dev/chainguard/nginx:latest-dev &lt;span class="nt"&gt;--target&lt;/span&gt; nginx-distroless &lt;span class="nt"&gt;--&lt;/span&gt; /bin/sh
Targeting container &lt;span class="s2"&gt;"nginx-distroless"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; If you don&lt;span class="s1"&gt;'t see processes from this container it may be because the container runtime doesn'&lt;/span&gt;t support this feature.
Defaulting debug container name to debugger-nbvjt.
If you don&lt;span class="s1"&gt;'t see a command prompt, try pressing enter.
/ $
/ $ cat /proc/1/root/usr/share/nginx/html/index.html
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
…
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And everything works as expected.&lt;/p&gt;

&lt;p&gt;There's actually a second wrinkle that is also solved by setting the user – if your pod is running with the &lt;code&gt;runAsNonRoot&lt;/code&gt; policy, you won't be able to start a debug container that runs as root with the default profile.&lt;/p&gt;

&lt;p&gt;This does point to some ways in which &lt;code&gt;kubectl debug&lt;/code&gt; could be improved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;--user&lt;/code&gt; option to set the user in the debug container &lt;/li&gt;
&lt;li&gt;Add a formal way to access the target container filesystem. Going via &lt;code&gt;/proc/1/root&lt;/code&gt; seems a little hacky and non-intuitive&lt;/li&gt;
&lt;li&gt;Add some more docs to explain all of this (which is somewhere I plan to help).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(I do see there are some &lt;a href="https://github.com/kubernetes/enhancements/issues/4292"&gt;proposed enhancements&lt;/a&gt; related to profiles that might help here)&lt;/p&gt;

&lt;p&gt;I should also point out that Ivan addresses these problems directly with his &lt;a href="https://github.com/iximiuz/cdebug"&gt;cdebug&lt;/a&gt; tool. You can use &lt;code&gt;cdebug&lt;/code&gt; to directly debug a pod:&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;$ &lt;/span&gt;cdebug &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt; pod/nginx-distroless/nginx-distroless
Debugger container name: cdebug-20ba5985
Starting debugger container...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;debugger container...
Attaching to debugger container...
If you don&lt;span class="s1"&gt;'t see a command prompt, try pressing enter.
/ # cat /proc/1/root/usr/share/nginx/html/index.html
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
…
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cdebug&lt;/code&gt; also supports a &lt;code&gt;--user&lt;/code&gt; flag if you have the &lt;code&gt;runAsNonRoot&lt;/code&gt; policy e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdebug &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; 65532 pod/nginx-distroless/nginx-distroless
…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's about it. Running production workloads in distroless containers is a big improvement in terms of security. With a little bit of knowledge these containers can still be straightforward to debug.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>containers</category>
      <category>distroless</category>
      <category>devops</category>
    </item>
    <item>
      <title>Securely Containerize a Python Application with Chainguard Images</title>
      <dc:creator>Patrick Smyth</dc:creator>
      <pubDate>Fri, 12 Apr 2024 19:09:57 +0000</pubDate>
      <link>https://dev.to/chainguard/securely-containerize-a-python-application-with-chainguard-images-bn8</link>
      <guid>https://dev.to/chainguard/securely-containerize-a-python-application-with-chainguard-images-bn8</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2D0JULd4E5A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;Containerization technologies such as Docker have revolutionized the development to production pipeline, making it easier than ever to reproduce environments and set up and maintain infrastructure. Unfortunately, containers come with their own risks and overhead. While standard images can be convenient for development, putting large images into production increases the attack surface and can demand additional and unnecessary resources in deployment.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll build on a &lt;a href="https://www.chainguard.dev/chainguard-images?utm_source=cg-devrel"&gt;Chainguard Image&lt;/a&gt; to containerize a Python application in a multi-stage build process. By using a Chainguard Image, our containerized application benefits from a minimalist design that reduces attack surface and from a set of features focused on security and ease of development. In addition, the use of a multistage build process will allow us to develop our application with access to tools such as package managers while removing these potential vulnerabilities later in the build process.&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%2Fg3q8ly36r21wbx75uhd8.gif" 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%2Fg3q8ly36r21wbx75uhd8.gif" alt="A GIF of a little octpus with the caption Hello, I am small image" width="512" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Application
&lt;/h2&gt;

&lt;p&gt;The Python application we will containerize in this tutorial, Chainguard Timeteller, will provide the user with the local time in ten randomly chosen international timezones. It also displays the time in UTC and in the user's local timezone. The application depends on &lt;a href="https://pypi.org/project/pytz/"&gt;pytz&lt;/a&gt;, allowing us to draw from the &lt;a href="https://en.wikipedia.org/wiki/Tz_database"&gt;Olson tz database&lt;/a&gt; in selecting our random timezones.&lt;/p&gt;

&lt;p&gt;Let's start by creating our main application script. &lt;br&gt;
 First, open your terminal and run the below line to create a new folder called &lt;code&gt;timeteller&lt;/code&gt; in your home directory:&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;mkdir&lt;/span&gt; ~/timeteller &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/timeteller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a new file called &lt;code&gt;main.py&lt;/code&gt; in your preferred text editor. We'll use the widely available &lt;a href="https://en.wikipedia.org/wiki/GNU_nano"&gt;Nano&lt;/a&gt; editor in this tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the following Python code into &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tzname&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pytz&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;common_timezones&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tz_aware_now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a timezone-aware datetime for the current time and a provided timezone. Defaults to UTC.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tz&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;now&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pretty_print_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Given a datetime object, return a human-readable and nicely-formatted time string..&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Generate an ordinal suffix, i.e. "th" or "st" based on the day
&lt;/span&gt;    &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;
    &lt;span class="n"&gt;day_ordinal_suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;th&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;st&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dayd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;th&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;date_pretty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%-I:%M %p on %B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;day_ordinal_suffix&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;date_pretty&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_timezone_message&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create the message to display to a user. The message includes a greeting, the current time in UTC, the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s local timezone and time, and ten local times from randomly selected timezones.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;local_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tz_aware_now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tz_aware_now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;astimezone&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;random_timezones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;common_timezones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;timezones_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;pretty_print_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tz_aware_now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;zone&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;random_timezones&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;printable_timezones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;➤&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezones_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;zone&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;timezones_map&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Welcome to Chainguard Timeteller! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🌎 The current time in UTC is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;pretty_print_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tz_aware_now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❓ Your current timezone is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tzname&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⏰ Your local time is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;pretty_print_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Local times from ten randomly chosen timezones around the world:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;printable_timezones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]&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;message&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate_timezone_message&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using Nano, you can save the file by pressing &lt;code&gt;Control-x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;Enter&lt;/code&gt; in sequence.&lt;/p&gt;

&lt;p&gt;In this script, we define functions to return the current time in a specific timezone, generate nicely-formatted lines for each region, and pull together a message to the user. When run directly, the script prints the generated message, including the time in ten randomly selected timezones, to the console. The code depends on a library, pytz, not in the standard library, and we'll have to install it during our build process.&lt;/p&gt;

&lt;p&gt;Because our code depends on &lt;code&gt;pytz&lt;/code&gt;, a package not in Python's standard library, we'll also need to specify our dependencies. Open a &lt;code&gt;requirements.txt&lt;/code&gt; file using your text editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the below into the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytz==2024.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we specify the version of &lt;code&gt;pytz&lt;/code&gt; we want to use. Once you're done, save the file.&lt;/p&gt;

&lt;p&gt;Before we build our container, let's test that our script works. First, install our dependency using the pip package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on your system, you may need to use the &lt;code&gt;pip3&lt;/code&gt; command instead of the &lt;code&gt;pip&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Once &lt;code&gt;pytz&lt;/code&gt; has installed, run the script with the below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on your system, you may need to use the &lt;code&gt;python3&lt;/code&gt; command instead of the &lt;code&gt;python&lt;/code&gt; command. You should receive output similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Welcome to Chainguard Timeteller! 

🌎 The current time in UTC is 6:07 PM on February 18th.
❓ Your current timezone is EST.
⏰ Your local time is 1:07 PM on February 18th.

Local times from ten randomly chosen timezones around the world:

     ➤ Europe/Dublin 6:07 PM on February 18th
     ➤ Africa/Lagos 7:07 PM on February 18th
     ➤ America/Tortola 2:07 PM on February 18th
     [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you see the above output with local times in ten randomly-selected timezones, you'll know the application is working. You're ready to containerize Timeteller using a base image from Chainguard Images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Stage Build Using Chainguard Images
&lt;/h2&gt;

&lt;p&gt;Now that we have our application in place, we're ready to containerize it using a multi-stage build process. This build process works as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We begin the build by pulling a development version of the &lt;code&gt;python-latest&lt;/code&gt; Chainguard Image as our base image.&lt;/li&gt;
&lt;li&gt;We copy the &lt;code&gt;requirements.txt&lt;/code&gt; file to the image, activate our virtual environment, and install our dependency using &lt;code&gt;pip&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We now pull the minimal runtime version of the &lt;code&gt;python-latest&lt;/code&gt; image. This image does not contain pip or an interactive shell.&lt;/li&gt;
&lt;li&gt;We copy our virtual environment (now with access to our dependency) from the dev image to the minimal runtime image.&lt;/li&gt;
&lt;li&gt;We activate our virtual environment on the minimal runtime image and run the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create a file named &lt;code&gt;Dockerfile&lt;/code&gt; in your &lt;code&gt;timeteller&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Copy the following build instructions to the Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;cgr.dev/chainguard/python:latest-dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LANG=C.UTF-8&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/timeteller/venv/bin:$PATH"&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /timeteller&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv /timeteller/venv
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; cgr.dev/chainguard/python:latest&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ="America/Chicago"&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /timeteller&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/venv/bin:$PATH"&lt;/span&gt;


&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; main.py ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /timeteller/venv /venv&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "python", "/timeteller/main.py" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the timezone in the line &lt;code&gt;ENV TZ="America/Chicago"&lt;/code&gt;to your own local timezone. (Without providing this information, the detected timezone in the container would be UTC.)&lt;/p&gt;

&lt;p&gt;Save the file. We should now be ready to perform the build. Run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; timeteller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build the image from the instructions in our Dockerfile and tag it with the name &lt;code&gt;timeteller&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the image build is successful, you're ready to run it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; timeteller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the output from our application as above, including the ten randomly selected local times. Congratulations! You've successfully containerized and run an application using Chainguard Images in a multi-stage build process.&lt;/p&gt;

&lt;p&gt;The final image based on python:latest does not contain either pip or interactive shells such as &lt;code&gt;sh&lt;/code&gt; or &lt;code&gt;bash&lt;/code&gt;. Since package managers and shells are common vectors for attackers, having neither as part of our runtime image decreases the attack surface of our application in production. The multi-stage build process allows us to use tools such as shells and package managers during development while allowing us to keep our production image securely minimal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Chainguard Images
&lt;/h2&gt;

&lt;p&gt;Chainguard Images provide a happy medium between superminimal images such as &lt;a href="https://hub.docker.com/_/scratch/"&gt;scratch&lt;/a&gt; and more complex distribution-based images such as Alpine or Debian. Chainguard Images aim specifically to reduce complexity, intentionally including only those software components necessary for runtime. Further, Chainguard Images are based on a distroless philosophy, meaning that they strip out additional software components traditionally associated with a distribution. Typically, a Chainguard Image contains only an application runtime, root certificates, a minimal file structure, and a small number of core system libraries.&lt;/p&gt;

&lt;p&gt;Each Chainguard Image comes with a comprehensive SBOM (Software Bill of Materials). This allows users of Chainguard Images to check against known vulnerabilities, adhere to the legal terms of software licenses, and ensure software integrity.&lt;/p&gt;

&lt;p&gt;Finally, the focus on minimal builds results in significantly fewer CVEs on your runtime images. Before we end this tutorial, let's scan for CVEs in our Timeteller image using an industry-standard tool, &lt;a href="https://docs.docker.com/scout/"&gt;Docker Scout&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To use Docker Scout, you'll first have to have a &lt;a href="https://hub.docker.com"&gt;Docker Hub&lt;/a&gt; account. Follow the &lt;a href="https://github.com/docker/scout-cli?tab=readme-ov-file#cli-plugin-installation"&gt;installation instructions for Docker Scout on GitHub&lt;/a&gt;. Once Docker Scout is installed, you can sign in to Docker Hub on the command line with the &lt;code&gt;docker login&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Once we have Docker Scout installed, we can use it to scan for vulnerabilities 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;docker scout cves timeteller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running Docker Scout on our Timeteller image (built from a Chainguard Image) on February 18th, 2024 produced the following report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;    ✓ Image stored for indexing
    ✓ Indexed 32 packages
    ✓ No vulnerable package detected&lt;span class="sb"&gt;


&lt;/span&gt;&lt;span class="gu"&gt;## Overview&lt;/span&gt;&lt;span class="sb"&gt;

                    │       Analyzed Image         
&lt;/span&gt;────────────────────┼──────────────────────────────
  Target            │  timeteller:latest           
    digest          │  0cca410be7e4                
    platform        │ linux/amd64                  
    vulnerabilities │    0C     0H     0M     0L   
    size            │ 28 MB                        
    packages        │ 32                           &lt;span class="sb"&gt;


&lt;/span&gt;&lt;span class="gu"&gt;## Packages and Vulnerabilities&lt;/span&gt;

  No vulnerable packages detected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, no CVEs were detected in the Timeteller image at time of writing, a relatively rare outcome in the fast-paced world of container vulnerabilities and exposures. While your results with Chainguard Images won't always be this free of vulnerabilities, using Chainguard Images as your base will reduce your CVE incidence rate by 80% compared to comparable industry alternatives.&lt;/p&gt;

&lt;p&gt;In this tutorial, you containerized a Python application with Chainguard Images in a multi-stage build process. This resulted in a runtime image with only the software components required to run our application. This focus on reducing software complexity resulted in a runtime image with a demonstrably low number of CVEs—zero in this case. Now that you understand the advantages of building your production infrastructure on &lt;a href="https://www.chainguard.dev/chainguard-images?utm_source=cg-devrel"&gt;Chainguard Images&lt;/a&gt;, you're ready to go forth and secure your own production environment.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>security</category>
      <category>python</category>
      <category>wolfi</category>
    </item>
    <item>
      <title>Level up your vulnerability management skills . . . painlessly</title>
      <dc:creator>erin glass</dc:creator>
      <pubDate>Wed, 14 Feb 2024 19:46:39 +0000</pubDate>
      <link>https://dev.to/chainguard/level-up-your-vulnerability-management-skills-painlessly-25c2</link>
      <guid>https://dev.to/chainguard/level-up-your-vulnerability-management-skills-painlessly-25c2</guid>
      <description>&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%2Fhs001nncqgdc9a5tfkhu.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%2Fhs001nncqgdc9a5tfkhu.png" alt="Image description" width="760" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turns out, vulnerabilities don't disappear if you ignore them, and regulation is catching up. If you're working in software in 2024, it's imperative to know how to manage vulnerabilities quickly and effectively. Unfortunately, according to &lt;a href="https://www.cisa.gov/news-events/news/we-must-consider-software-developers-key-part-cybersecurity-workforce"&gt;a shocking statistic from CISA&lt;/a&gt;, 23 of the top 24 computer science programs don't require courses in cybersecurity. &lt;/p&gt;

&lt;p&gt;To help the industry sharpen its vulnerability management skills, we're launching our action-packed, over-the-top course &lt;a href="https://courses.chainguard.dev/"&gt;Painless Vulnerability Management with Chainguard&lt;/a&gt;. Free for a limited time, this course will help developers understand the landscape, tools, and practices of vulnerability management, and learn how to use Chainguard Images to make the process a breeze.   &lt;/p&gt;

&lt;p&gt;If you want to learn why vulnerability management is so critical, impress your boss or prospective employer, or simply avoid enabling the next software supply chain attack, this course is for you! We promise to equip you with all the skills you need to painlessly manage vulnerabilities, and just as many memes!&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>learning</category>
      <category>containers</category>
    </item>
    <item>
      <title>Learning Labs: How to build on Chainguard's Hardened and Minimal Images</title>
      <dc:creator>Adrian Mouat</dc:creator>
      <pubDate>Mon, 29 Jan 2024 16:31:53 +0000</pubDate>
      <link>https://dev.to/chainguard/learning-labs-how-to-build-on-chainguards-hardened-and-minimal-images-5c7e</link>
      <guid>https://dev.to/chainguard/learning-labs-how-to-build-on-chainguards-hardened-and-minimal-images-5c7e</guid>
      <description>&lt;p&gt;At &lt;a href="//chainguard.dev"&gt;Chainguard&lt;/a&gt; we've just launched the first in a series of hands-on &lt;a href="https://www.crowdcast.io/c/learninglabs-feb24"&gt;Learning Labs&lt;/a&gt;. This series will help engineers get started on the path to low-CVE, minimal images using Chainguard base images. We're planning to run labs for various stacks, starting with images for compiled languages such as Go, Rust and C on Feb 13. &lt;/p&gt;

&lt;p&gt;What you will learn:&lt;br&gt;
  💜 Why reducing the size and CVE count of images is important&lt;br&gt;
  💜 How to migrate a Dockerfile build using a compiled language to use Chainguard Images&lt;br&gt;
  💜 Advanced techniques for producing truly minimal images and debugging&lt;/p&gt;

&lt;p&gt;📆 Tuesday, Feb 13, 2024 @ 10 am ET (click through for your TZ)&lt;br&gt;
📝 Register: &lt;a href="https://www.crowdcast.io/c/learninglabs-feb24"&gt;https://www.crowdcast.io/c/learninglabs-feb24&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Get Involved with Chainguard Projects for Hacktoberfest 2023</title>
      <dc:creator>Erika Heidi</dc:creator>
      <pubDate>Wed, 18 Oct 2023 15:58:33 +0000</pubDate>
      <link>https://dev.to/chainguard/get-involved-with-chainguard-projects-for-hacktoberfest-2023-4h7b</link>
      <guid>https://dev.to/chainguard/get-involved-with-chainguard-projects-for-hacktoberfest-2023-4h7b</guid>
      <description>&lt;p&gt;Open source has always been at the core of what Chainguard does, which is evidenced by projects such as &lt;a href="https://sigstore.dev"&gt;Sigstore&lt;/a&gt; and &lt;a href="https://edu.chainguard.dev/open-source/wolfi/overview/"&gt;Wolfi&lt;/a&gt;. From &lt;a href="https://github.com/chainguard-dev/apko"&gt;image building&lt;/a&gt; to automated &lt;a href="https://github.com/chainguard-dev/images-autodocs"&gt;documentation&lt;/a&gt;, the majority of the code we write is open source, from the very beginning.&lt;/p&gt;

&lt;p&gt;In this month of October, the Developer Enablement team at Chainguard would like to invite &lt;a href="https://hacktoberfest.com/"&gt;Hacktoberfest&lt;/a&gt; participants to get involved with our open source projects. We have good first issues and a welcoming team ready to help with any questions you may have. &lt;/p&gt;

&lt;p&gt;If you're interested in containers and Linux, you may be interested in Wolfi, our community Linux &lt;em&gt;undistro&lt;/em&gt; built for containers. We appreciate your help in keeping Wolfi-bot accountable - it doesn't always get things right 😅&lt;/p&gt;

&lt;p&gt;If you'd like to help with documentation, we welcome your help with READMEs in the &lt;a href="https://github.com/chainguard-images/images"&gt;Chainguard Images&lt;/a&gt; repository. &lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing to Wolfi
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://edu.chainguard.dev/open-source/wolfi/overview/"&gt;Wolfi&lt;/a&gt; Linux undistro is an open source project primarily maintained by Chainguard, but with community contributors from all over the world. Wolfi packages live in the &lt;a href="https://github.com/wolfi-dev/os"&gt;github.com/wolfi-dev/os&lt;/a&gt; repository, where we have established a thorough automation process to validate new package builds, check for available package updates, and automatically send PRs when new versions are available. In fact, the Wolfi-bot is a top contributor to the project, but it doesn't always get things right at first.&lt;/p&gt;

&lt;p&gt;This year we invite Hacktoberfest participants to help with broken automated PRs from the Wolfi-bot. &lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying Broken PRs
&lt;/h3&gt;

&lt;p&gt;The broken Wolfi-bot PRs are easy to spot — they are tagged as &lt;code&gt;automated-pr&lt;/code&gt; and &lt;code&gt;request-update&lt;/code&gt; and have an red &lt;code&gt;X&lt;/code&gt; icon next to it, indicating that one or more CI checks didn't pass. This is typically due to a build issue that needs to be manually debugged and fixed - maybe a new package version introduces a new dependency that is not met at build time, or something has changed in the make / build process for that package.&lt;/p&gt;

&lt;p&gt;We have created a number of issues to fix these broken PRs. You can find them in the repo by searching for issues tagged as &lt;code&gt;hacktoberfest&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we expect from contributions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Choose an issue tagged as &lt;code&gt;hacktoberfest&lt;/code&gt; from &lt;a href="https://github.com/wolfi-dev/os/issues/7017"&gt;the list&lt;/a&gt;. Make sure to leave a comment on the issue so that others know you are working on it.&lt;/li&gt;
&lt;li&gt;Create a new PR with your changes, so that we can tag it with the &lt;code&gt;hacktoberfest-accepted&lt;/code&gt; label and make it a valid PR for Hacktoberfest.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check also the &lt;a href="https://github.com/wolfi-dev/os/blob/dfb8f4a553e894db861295be44ef5e4de3072022/CONTRIBUTING.md"&gt;Wolfi contributing guide&lt;/a&gt; for more information on how to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing to Chainguard Images
&lt;/h2&gt;

&lt;p&gt;The public &lt;a href="https://dev.tochainguard-images/images"&gt;https://github.com/chainguard-images/images&lt;/a&gt; repository is an open source repository containing the configurations for all public Chainguard Images. With the number of images growing every day, it is hard to keep up with documentation needs. This year we invite Hacktoberfest participants to test our images and help update their READMEs, which are used for images documentation living in &lt;a href="https://edu.chainguard.dev/chainguard/chainguard-images/reference"&gt;Chainguard Academy&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we expect from contributions:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;a href="https://github.com/chainguard-images/images/issues/1539"&gt;one of the listed issues&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Test the image on your local environment.&lt;/li&gt;
&lt;li&gt;Update the image README with instructions on how to use it and any other important information that you think users should know. You should follow the &lt;a href="https://github.com/chainguard-images/images/blob/main/.github/readme-template.md"&gt;README template&lt;/a&gt; for keeping READMEs consistent.&lt;/li&gt;
&lt;li&gt;Optionally, write a test for the image. Check the "Tests" section of our &lt;a href="https://github.com/chainguard-images/images/blob/main/BEST_PRACTICES.md#tests"&gt;Best Practices doc&lt;/a&gt; for more details on how to go about that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're finished, create a PR to the Chainguard Images repository with your updated README, so that we can tag it with the &lt;code&gt;hacktoberfest-accepted&lt;/code&gt; label and make it a valid PR for Hacktoberfest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing to Chainguard Academy
&lt;/h2&gt;

&lt;p&gt;You are also welcome to send documentation PRs to &lt;a href="https://edu.chainguard.dev"&gt;Chainguard Academy&lt;/a&gt;. Maybe you found a typo or an outdated instruction in one of our tutorials? Send a &lt;a href="https://github.com/chainguard-dev/edu"&gt;PR&lt;/a&gt;. Just be aware that some of those docs are automated and shouldn't be manually altered. When in doubt, create an issue first so that we can discuss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending PRs to Chainguard Projects
&lt;/h2&gt;

&lt;p&gt;In order to get your PRs passing CI scripts to Chainguard repositories, your commits must be signed with either &lt;a href="https://docs.sigstore.dev/signing/gitsign/"&gt;Gitsign&lt;/a&gt; or with regular &lt;a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits"&gt;gpg signing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don't forget to mention the issue ID you're fixing with your PR. For Wolfi PRs, a good title could be something like "Fixing wolfi-bot update: package-name #1234" where &lt;code&gt;1234&lt;/code&gt; is the number of your assigned issue. For Chainguard Images PRs, we recommend using a &lt;code&gt;[Docs]&lt;/code&gt; prefix so that the documentation team can be assigned more quickly to review your PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wolfi Swag
&lt;/h2&gt;

&lt;p&gt;We value all contributors and to show our gratitude, we’ll be giving away a limited number of Wolfi swag packs. If your PR gets accepted, you may be eligible to receive one! Reviewers will leave instructions for you in the eligible PR.&lt;/p&gt;

&lt;p&gt;If you still have any questions, don't hesitate to open an issue on the relevant repository or reach out directly on social: &lt;a href="https://twitter.com/chainguard_dev"&gt;https://twitter.com/chainguard_dev&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>containers</category>
      <category>linux</category>
    </item>
    <item>
      <title>Software Supply Chain Security Leadership Series: Measuring SBOM Quality</title>
      <dc:creator>Samson Goddy</dc:creator>
      <pubDate>Tue, 20 Dec 2022 15:09:05 +0000</pubDate>
      <link>https://dev.to/chainguard/software-supply-chain-security-leadership-series-measuring-sbom-quality-5047</link>
      <guid>https://dev.to/chainguard/software-supply-chain-security-leadership-series-measuring-sbom-quality-5047</guid>
      <description>&lt;p&gt;&lt;a href="https://www.crowdcast.io/c/measuring-sbom-quality"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IV-NMqWZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5crnd8sg23znrf6rectw.png" alt="Image from Chainguard event series " width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How should we think about the things that make a “software bill of materials” (SBOMs) good? In this talk, we look at the results of a recent study on SBOM quality and dig deeper into new tools that can be used to measure SBOM quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.crowdcast.io/c/measuring-sbom-quality"&gt;Join us and save your spot!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>event</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From Build to Prod: Getting Started with Software Signing and Policy Enforcement</title>
      <dc:creator>Samson Goddy</dc:creator>
      <pubDate>Fri, 09 Dec 2022 15:45:26 +0000</pubDate>
      <link>https://dev.to/chainguard/from-build-to-prod-getting-started-with-software-signing-and-policy-enforcement-21n7</link>
      <guid>https://dev.to/chainguard/from-build-to-prod-getting-started-with-software-signing-and-policy-enforcement-21n7</guid>
      <description>&lt;p&gt;&lt;a href="https://www.crowdcast.io/c/software-signing"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CZNR2HlM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f40moq5tybjvk6qr9icp.png" alt="Banner for the Build to Prod event" width="880" height="495"&gt;&lt;/a&gt;Want to know how to get started on signing your images and commits? Secure your software from build to production and join &lt;a href="https://twitter.com/strongjz"&gt;James Strong&lt;/a&gt; to walk through signing images with &lt;a href="https://www.sigstore.dev/how-it-works"&gt;Sigstore&lt;/a&gt; via Tekton chains and commits with &lt;a href="https://dev.to/erikaheidi/enable-gitsign-today-and-start-signing-your-commits-2gda"&gt;Gitsign&lt;/a&gt;, all with &lt;a href="https://www.chainguard.dev/chainguard-enforce"&gt;policy enforced by Chainguard&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.crowdcast.io/c/software-signing"&gt;Be sure to register and save your spot for free!!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>events</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>3 ways to improve your OSS project's resilience for Hacktoberfest</title>
      <dc:creator>Erika Heidi</dc:creator>
      <pubDate>Fri, 30 Sep 2022 17:51:10 +0000</pubDate>
      <link>https://dev.to/chainguard/3-ways-to-improve-your-oss-projects-resilience-for-hacktoberfest-g4</link>
      <guid>https://dev.to/chainguard/3-ways-to-improve-your-oss-projects-resilience-for-hacktoberfest-g4</guid>
      <description>&lt;p&gt;With October just around the corner, &lt;a href="https://hacktoberfest.com" rel="noopener noreferrer"&gt;Hacktoberfest&lt;/a&gt; is about to start. While many developers around the world are getting ready to participate and claim their rewards, open source maintainers who opt in their projects are also busy preparing for the event. Maintainers play a very important role in Hacktoberfest each year, enabling participants to contribute by creating adequate issues, reviewing PRs, and helping out newcomers getting started into their projects.&lt;/p&gt;

&lt;p&gt;Apart from the usual preparation tasks that focus on enabling contributors to find relevant issues they can work on, as a maintainer you can also work on a few improvements to make your project more resilient and better prepared to accept contributions from the wider community. &lt;/p&gt;

&lt;p&gt;In this article, we'll share 3 things you can do today in order to improve your project's reliability and facilitate long-term maintenance, keeping software supply chain security in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Set up Automated Quality Checks with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;GitHub Actions is a powerful feature from GitHub that allows you to set up unlimited automated workflows, given your project is open source. Actions that automatically run the application's test suite and check the code style used are ideal to be integrated into your pull request workflow, facilitating reviews and enabling contributors to get unblocked faster.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/marketplace?type=actions" rel="noopener noreferrer"&gt;GitHub Marketplace&lt;/a&gt; has a large offering of community-contributed actions for all kinds of stacks. &lt;/p&gt;

&lt;p&gt;For instance, searching for "php" will give you quite a few different actions that can be combined in a workflow to be executed whenever a new pull request is open. &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%2F5a10srrl7jtnpz2gkou2.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%2F5a10srrl7jtnpz2gkou2.png" alt="screenshot showing github marketplace search for php actions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the documentation included with each action for more information on how to implement it on a workflow for your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Sign your Commits (and ask contributors to do the same)
&lt;/h2&gt;

&lt;p&gt;Signing your commits is an important step to improve the reliability of your codebase, since it allows users to confirm that the code committed to the repository actually comes from a legitimate source. It is not very hard to forge commit authorship, especially considering typosquat attacks, where bad actors will create an account with a very similar name to your own account and set up git to use similar credentials.&lt;/p&gt;

&lt;p&gt;So why isn't everyone signing their commits already? It has a lot to do with the difficulty in creating and maintaining GPG keypairs, which are used to generate commit signatures. Luckily for us, keyless signing (which in fact still uses GPG keys but make them invisible and ephemeral, only valid at the time of signing) is an alternative that doesn't require dealing with GPG keys.That makes commit signing a lot easier to adopt in the context of open source projects that expect contributions from the wider community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.sigstore.dev/gitsign/overview/" rel="noopener noreferrer"&gt;Gitsign&lt;/a&gt; offers a keyless commit signing implementation based on OIDC, which is an identity layer built on top of the OAuth 2.0 framework. Gitsign supports verifying your identity either through GitHub, Microsoft, or a Google account.&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%2F77gfmla6qqdzdra0ei0q.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%2F77gfmla6qqdzdra0ei0q.png" alt="OIDC authorization with Gitsign"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information on commit signing and detailed instructions on how to install and set up Gitsign, check the following article:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/erikaheidi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F162988%2F80f515eb-5bcf-4813-9bfe-1438eea85889.jpg" alt="erikaheidi"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/erikaheidi/enable-gitsign-today-and-start-signing-your-commits-2gda" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Enable Gitsign Today and Start Signing your Commits&lt;/h2&gt;
      &lt;h3&gt;Erika Heidi ・ Aug 25 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Don't forget to update your CONTRIBUTING docs to include information about signing commits, with links to relevant articles to help users set their local git configuration for signing.  Here is an example text in markdown that you can copy and paste to your CONTRIBUTING.md page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### Commit Signing&lt;/span&gt;

Please sign all your commits using either GPG keys or a keyless mechanism such as &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Gitsign&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.sigstore.dev/gitsign/overview/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;. This is important to make sure all code comes from trusted sources, which will improve the overall security and quality of our project. For more information on how to set up your local Git environment to user commit signing by default, please refer to one of the following guides:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Gitsign Documentation&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.sigstore.dev/gitsign/installation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;GitHub Documentation on signing commits with GPG keys&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Include a Software Bill of Materials (SBOM) within your Releases
&lt;/h2&gt;

&lt;p&gt;A Software Bill of Materials is a document that contains a list of all software dependencies that are incorporated into your application or container image. There are a few different formats you can use, but &lt;a href="https://spdx.dev/" rel="noopener noreferrer"&gt;SPDX&lt;/a&gt; is generally a good choice since it is a standard maintained by the Linux Foundation.&lt;/p&gt;

&lt;p&gt;Having an SBOM allows end-users and project leads to track all software included in the application as well as its versions. It facilitates detecting vulnerabilities within your software supply chain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anchore/syft/" rel="noopener noreferrer"&gt;Syft&lt;/a&gt; is a popular open source tool that generates SBOMs for software applications and also containers. You can execute it manually and include the generated artifacts into your release, but you can also automate the process using a GitHub Action that will be triggered whenever you have a new release on your repository.&lt;/p&gt;

&lt;p&gt;For instance, you can use the following workflow to set up Syft to automatically generate SBOM files for Composer-based projects (PHP) whenever a new release is published in your repository:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate Release SBOM&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;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;published&lt;/span&gt;

  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&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;build&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;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;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;Install Composer dependencies&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;composer instal --prefer-dist --no-progress&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 SBOM&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;anchore/sbom-action@v0&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;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Considerations
&lt;/h2&gt;

&lt;p&gt;The steps listed in this article may often be seen as an afterthought and not a priority for an open source project, but they certainly pay dividends in the long run. In addition to setting up a strong foundation for contributors to do their work, you'll also seize the benefits of having automated processes to help with the project's long term maintenance. By implementing these steps within your open source project, you will also be gathering valuable data that you can use later on to compare and keep track of your project's evolution, how the dependencies changed over time (via SBOMs), who are the legitimate authors of commits (via commit signing), and whether or not new contributions adhere to code standards (via CI Actions).&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Container Images for the Cloud Native Era</title>
      <dc:creator>Erika Heidi</dc:creator>
      <pubDate>Thu, 22 Sep 2022 13:09:07 +0000</pubDate>
      <link>https://dev.to/chainguard/container-images-for-the-cloud-native-era-2mk1</link>
      <guid>https://dev.to/chainguard/container-images-for-the-cloud-native-era-2mk1</guid>
      <description>&lt;p&gt;Today is a very special day for Chainguard, as we release to the public a few projects that will lay the foundation for important improvements in the cloud-native and container ecosystems. The following announcement (video) provides an overview of these projects and how they fit together to enable safer build and runtime environments, with a focus on securing the software supply chain:&lt;/p&gt;

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

&lt;p&gt;During the past few months, I have been working closely with the team that built Wolfi and Chainguard Images, testing and documenting these projects as they evolved into the version that is being released today. Some of this work is already available at &lt;a href="https://edu.chainguard.dev" rel="noopener noreferrer"&gt;Chainguard Academy&lt;/a&gt;, our educational hub for software supply chain resources. More to come, for sure!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Yw0ZpX6d3sQnxsjaQx/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Yw0ZpX6d3sQnxsjaQx/giphy.gif" alt="gif of a purple octopus throwing confetti"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post I'll talk a bit about these projects, how they came to be, and what they represent in the context of container images for the cloud native era.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Wolfi was Born
&lt;/h2&gt;

&lt;p&gt;A long time ago, in a galaxy far far away, some folks realized that the most popular base container images were bloated with stuff that could make sense in bare metal servers, but were totally superfluous in containerized environments. The first distroless images were developed at Google, and Chainguard's founders Matt Moore and Dan Lorenc were directly involved with the initiative.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/dzPDaQLABRZkBfWQFE/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/dzPDaQLABRZkBfWQFE/giphy.gif" alt="gif of kid saying "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fast-forward to May 2022, when the Chainguard Images project was still in its first iterations. It didn't take long for the team to realize that the Linux distributions commonly used as bases for container images were not really designed for what we wanted to build. Some of the main challenges included the lag behind upstream updates, lack of provenance information, and an unnecessary increased attack surface caused by software that didn't need to be there.&lt;/p&gt;

&lt;p&gt;The only way to solve these problems was to build a distribution designed for container/cloud native environments. So, we built Wolfi. You can read more about the design principles and decisions behind Wolfi in &lt;a href="http://www.chainguard.dev/unchained/introducing-wolfi-the-first-linux-un-distro" rel="noopener noreferrer"&gt;this blog post&lt;/a&gt;. The &lt;a href="https://edu.chainguard.dev/open-source/wolfi/overview/" rel="noopener noreferrer"&gt;Wolfi documentation&lt;/a&gt; at Chainguard Academy has more context on why we call it an &lt;em&gt;undistro&lt;/em&gt; and how it is different from other Linux distributions. In a nutshell, Wolfi is a tiny distribution that doesn't have a kernel of its own, it's designed to support both &lt;code&gt;glibc&lt;/code&gt; and &lt;code&gt;musl&lt;/code&gt;, and includes high-quality SBOMs (software bill of materials) covering all packages in the distro.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief Look at the New Chainguard Images
&lt;/h2&gt;

&lt;p&gt;Powered by Wolfi, Chainguard Images are a suite of distroless images that consolidate the base features of the Wolfi undistro into end-user container images that can be integrated into existing workflows. Chainguard Images are fully declarative and reproducible, and include SBOMs that cover all image dependencies. In addition, Chainguard Images are signed via &lt;a href="https://sigstore.dev" rel="noopener noreferrer"&gt;Sigstore&lt;/a&gt;, which attests the provenance of all artifacts. All images and corresponding signatures, as well as their SBOMs, are hosted in Chainguard's OCI registry &lt;code&gt;cgr.dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The easiest way to see the advantages of using a Chainguard Image instead of their traditional (or even official) counterpart is by comparing the results of security scanners analyzing those images. Here's an overview of Nginx, as an example:&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%2F16z56m4puzoec2ca3svh.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%2F16z56m4puzoec2ca3svh.png" alt="trivy scan results: nginx official vs chainguard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left, you have an overview of CVEs detected by &lt;a href="https://github.com/aquasecurity/trivy" rel="noopener noreferrer"&gt;Trivy&lt;/a&gt; on the &lt;code&gt;nginx:latest&lt;/code&gt; container image, from August to September. This includes low, medium, high, and critical CVEs (classified by color). On the right side, you can see the results from a Trivy run on our distroless Nginx image: zero CVEs.&lt;/p&gt;

&lt;p&gt;But don't take my word for it; install Trivy and run the scan on your own terminal, you will be surprised with the results.&lt;/p&gt;

&lt;p&gt;To learn more about how to get started using Chainguard Images, please visit the official documentation at &lt;a href="https://edu.chainguard.dev/chainguard/chainguard-images/overview/" rel="noopener noreferrer"&gt;Chainguard Academy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chainguard Academy: the one-stop resource for software supply chain security
&lt;/h2&gt;

&lt;p&gt;Last but not least, &lt;a href="https://edu.chainguard.dev" rel="noopener noreferrer"&gt;Chainguard Academy&lt;/a&gt; comes as a one-stop educational resource to close gaps in knowledge and elevate our collective understanding about software supply chain security, approaching conceptual subjects and industry frameworks as well as technical guides and tutorials on how to get things done in practice. &lt;/p&gt;

&lt;p&gt;It's worth noting that our knowledge base is open source and we are committed to keep iterating on it in order to provide the best documentation around the software supply chain, and the wider community is invited to propose improvements and new topics. We are still working on contributing guidelines, but they should be available soon (and in time for Hacktoberfest, dare I say!). Yay! You can find us on &lt;a href="https://github.com/chainguard-dev/edu" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, if you have any questions, feel free to join us today in a &lt;a href="https://twitter.com/i/spaces/1lDGLndgWpyxm?s=20" rel="noopener noreferrer"&gt;Twitter Spaces&lt;/a&gt; where we'll discuss all things software supply chain! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/10JDn0oEPKp6da/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/10JDn0oEPKp6da/giphy.gif" alt="gif - mic drop"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>containers</category>
      <category>opensource</category>
      <category>linux</category>
      <category>security</category>
    </item>
  </channel>
</rss>
