<?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: Hugo Prudente</title>
    <description>The latest articles on DEV Community by Hugo Prudente (@hugoprudente).</description>
    <link>https://dev.to/hugoprudente</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F397550%2F63e80a04-c283-46fb-953b-96fb4d9794e8.jpeg</url>
      <title>DEV Community: Hugo Prudente</title>
      <link>https://dev.to/hugoprudente</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hugoprudente"/>
    <language>en</language>
    <item>
      <title>Managing Secrets During Docker Build</title>
      <dc:creator>Hugo Prudente</dc:creator>
      <pubDate>Sat, 06 Mar 2021 01:01:52 +0000</pubDate>
      <link>https://dev.to/hugoprudente/managing-secrets-during-docker-build-3682</link>
      <guid>https://dev.to/hugoprudente/managing-secrets-during-docker-build-3682</guid>
      <description>&lt;p&gt;Original post from: &lt;a href="https://hugoprudente.github.io" rel="noopener noreferrer"&gt;https://hugoprudente.github.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What would be the best way to manage my secrets during a docker build?&lt;/p&gt;

&lt;p&gt;Checking official and unofficial projects available in &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;hub.docker.com&lt;/a&gt;,&lt;br&gt;
I have collected the 4 (four) most common cases on how users are storing and managing their secrets.&lt;/p&gt;

&lt;p&gt;There are cases that during the build you would use a token or secret file for fetch information from a &lt;br&gt;
repo or other application to setup a configuration that will not be possible during runtime.&lt;/p&gt;

&lt;p&gt;Some of those cases also doesn't fit the multistage building as fetching a package from pip.&lt;/p&gt;
&lt;h2&gt;
  
  
  Scenarios
&lt;/h2&gt;

&lt;p&gt;I need to install a &lt;code&gt;python&lt;/code&gt; using a private pip that I created for this lab.&lt;/p&gt;

&lt;p&gt;To achieve that you only need to add &lt;code&gt;pip.conf&lt;/code&gt; file as below to &lt;code&gt;/root/.pip/pip.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[global]&lt;/span&gt;
&lt;span class="py"&gt;index-url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://hugo.prudente:My$3cr3tP4$$@private.pip/playlist&lt;/span&gt;
&lt;span class="py"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;60&lt;/span&gt;
&lt;span class="py"&gt;extra-index-url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://pypi.python.org/simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks simple, let's see how we manage it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 1
&lt;/h3&gt;

&lt;p&gt;Here we copy the &lt;code&gt;pip.conf&lt;/code&gt; to the container and don't remove it on the end.&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; python:latest&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pip.conf /root/.pip/pip.conf&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker build &lt;span class="nt"&gt;-t&lt;/span&gt; secret:v1 &lt;span class="nb"&gt;.&lt;/span&gt;

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker &lt;span class="nb"&gt;history &lt;/span&gt;secret:v1
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
0d6589d4b95f   About a minute ago   RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist &lt;span class="c"&gt;# bui…   14.1MB    buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      4 minutes ago        COPY pip.conf /root/.pip/pip.conf &lt;span class="c"&gt;# buildkit    200B      buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      10 days ago          /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["python3"]              0B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check if the file on the end of the build is present and it was leaked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
❯ docker run -it secret:v1 cat /root/.pip/pip.conf
[global]
index-url = https://hugo.prudente:My$3cr3tP4$$@private.pip/playlist
timeout=60
extra-index-url = https://pypi.python.org/simple
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 2
&lt;/h3&gt;

&lt;p&gt;Here we copy the &lt;code&gt;pip.conf&lt;/code&gt; to the container and remove it with a &lt;code&gt;RUN&lt;/code&gt; statement on the end.&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; python:latest&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pip.conf /root/.pip/pip.conf&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; /root/.pip/pip.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker build &lt;span class="nt"&gt;-t&lt;/span&gt; secret:v2 &lt;span class="nb"&gt;.&lt;/span&gt;

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker &lt;span class="nb"&gt;history &lt;/span&gt;secret:v2
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
42f04cdc6577   6 seconds ago   RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /root/.pip/pip.conf &lt;span class="c"&gt;# buil…   0B        buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      4 minutes ago   RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist &lt;span class="c"&gt;# bui…   14.1MB    buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      7 minutes ago   COPY pip.conf /root/.pip/pip.conf &lt;span class="c"&gt;# buildkit    200B      buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      10 days ago     /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["python3"]              0B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check again if the file was present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker run &lt;span class="nt"&gt;-it&lt;/span&gt; secret:v2 &lt;span class="nb"&gt;cat&lt;/span&gt; /root/.pip/pip.conf
&lt;span class="nb"&gt;cat&lt;/span&gt;: /root/.pip/pip.conf: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 3
&lt;/h3&gt;

&lt;p&gt;Here we copy the &lt;code&gt;pip.conf&lt;/code&gt; to the container and remove it in the same &lt;code&gt;RUN&lt;/code&gt; statement as the &lt;code&gt;pip install&lt;/code&gt;&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; python:latest&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pip.conf /root/.pip/pip.conf&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /root/.pip/pip.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker build &lt;span class="nt"&gt;-t&lt;/span&gt; secret:v3 &lt;span class="nb"&gt;.&lt;/span&gt;

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker &lt;span class="nb"&gt;history &lt;/span&gt;secret:v3
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
a2bf2672abaf   About a minute ago   RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt;…   14.1MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      17 minutes ago       COPY pip.conf /root/.pip/pip.conf &lt;span class="c"&gt;# buildkit    200B      buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      10 days ago          /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["python3"]              0B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check once more if the file was present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker run &lt;span class="nt"&gt;-it&lt;/span&gt; secret:v3 &lt;span class="nb"&gt;cat&lt;/span&gt; /root/.pip/pip.conf
&lt;span class="nb"&gt;cat&lt;/span&gt;: /root/.pip/pip.conf: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 4
&lt;/h3&gt;

&lt;p&gt;Here we create the &lt;code&gt;pip.conf&lt;/code&gt; using the &lt;code&gt;generate.sh&lt;/code&gt; script that receive the&lt;br&gt;
&lt;code&gt;SECRET&lt;/code&gt; as &lt;code&gt;ARG&lt;/code&gt; with the &lt;code&gt;--build-arg&lt;/code&gt; options and we remove it on the same &lt;code&gt;RUN&lt;/code&gt; statement.&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;#!/bin/sh&lt;/span&gt;

&lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /root/.pip
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /root/.pip/pip.conf &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;
[global]
index-url = https://hugo.prudente:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;@private.pip/playlist
timeout=60
extra-index-url = https://pypi.python.org/simple
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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; python:latest&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; SECRET&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; generate.sh /generate.sh&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;/generate.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /root/.pip/pip.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker build &lt;span class="nt"&gt;-t&lt;/span&gt; secret:v4 &lt;span class="nt"&gt;--progress&lt;/span&gt; plain &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;My&lt;span class="nv"&gt;$3cr3tP4$$&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

➜ on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; secret:v4
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
d2ca3623139f   2 minutes ago   RUN |1 &lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;My&lt;span class="nv"&gt;$3cr3tP4$$&lt;/span&gt; /b…   14.1MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      2 minutes ago   COPY generate.sh /generate.sh &lt;span class="c"&gt;# buildkit        261B      buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      2 minutes ago   ARG SECRET                                      0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      10 days ago     /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["python3"]              0B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last but not least, let's check the presece of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
❯ docker run &lt;span class="nt"&gt;-it&lt;/span&gt; secret:v4 &lt;span class="nb"&gt;cat&lt;/span&gt; /root/.pip/pip.conf
&lt;span class="nb"&gt;cat&lt;/span&gt;: /root/.pip/pip.conf: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The credential is not on the &lt;code&gt;pip.conf&lt;/code&gt; file but it's visible during the &lt;code&gt;docker history&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preliminary Results
&lt;/h3&gt;

&lt;p&gt;Here is a matrix on where our secrets have been leaked.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Inspection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;secret:v1&lt;/td&gt;
&lt;td&gt;&lt;span&gt;Yes&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret:v2&lt;/td&gt;
&lt;td&gt;&lt;span&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret:v3&lt;/td&gt;
&lt;td&gt;&lt;span&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret:v4&lt;/td&gt;
&lt;td&gt;&lt;span&gt;No&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;Yes&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With the preliminary results we can already exclude methods 1 and 4 as we can&lt;br&gt;
consider them insecure due to the credentials being visible at some point. &lt;/p&gt;

&lt;p&gt;The method 4, I also used &lt;code&gt;--build-args SECRET=${SECRET}&lt;/code&gt; and the secrets&lt;br&gt;
leaks on the same way.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dive Deep
&lt;/h2&gt;
&lt;h3&gt;
  
  
  OverlayFS
&lt;/h3&gt;

&lt;p&gt;Is the kernel implementation for a union-filesystem, an overlay-filesystem tries&lt;br&gt;
to present a filesystem which is the result over overlaying one filesystem on&lt;br&gt;
top of the other.&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%2F5x20qntgvs66fgia350v.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%2F5x20qntgvs66fgia350v.png" alt="overlayfs.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short taking the example of the image above imagine you have 2 directories&lt;br&gt;
the &lt;strong&gt;lower&lt;/strong&gt; and &lt;strong&gt;upper&lt;/strong&gt; where the &lt;strong&gt;lower&lt;/strong&gt; is a read-only directory for the&lt;br&gt;
consumer, but they are still read-write from the Linux Operational system.&lt;/p&gt;

&lt;p&gt;When a file is modified on the &lt;strong&gt;upper&lt;/strong&gt; the change happens normally but if a&lt;br&gt;
change is made on a file of the &lt;strong&gt;lower&lt;/strong&gt; directory a copy of it is created on the&lt;br&gt;
&lt;strong&gt;upper&lt;/strong&gt; to become accessible, once the modification is complete another&lt;br&gt;
process is responsible to fetch the modification and write on the &lt;strong&gt;lower&lt;/strong&gt;&lt;br&gt;
directory.&lt;/p&gt;

&lt;p&gt;So this union of directories merged together as one unique block limited by the&lt;br&gt;
cgroups is what docker and it's storage driver uses on our example.&lt;/p&gt;

&lt;p&gt;The AUFS that's also a union-filesystem also presents the same behaviour,&lt;br&gt;
although some of locations may be slightly different.&lt;/p&gt;
&lt;h3&gt;
  
  
  Checking the Filesystem
&lt;/h3&gt;

&lt;p&gt;Now that I know how OverlayFS works let's isolate the directories (layers) used&lt;br&gt;
by the &lt;code&gt;python:latest&lt;/code&gt; so we can filter out only the ones that we are interested&lt;br&gt;
on, the ones that have &lt;code&gt;pip.conf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Inspect the containers I can find the directories used on the OverlayFS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker inspect python:latest | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt;
9e7c768dda91c4fa7ed6a57c7cb784834033bff92bd11ff6d062d4de11c0f898
17bf53c98685ae36487eb55f0d2256d168f210a688ef51deef760de1a699cbdf
4cb1dbbf58a2b1ca8df6d9d977a66fe918aee21434fcd656f1a68f1f412d75ff
358dd0944f115e2a273c5259dd1432b44e36908cf223f8ce0d9f74550430f577
c034592b1a26552525742ed81e7fbce2139817b634d48db8349dbebf15a45914
19d471e0407c0f1ca14eb1cb8c46aaef9357037cad5dc170cb6a4af3c1feab40
e005796f193e62e9db78de1df20999daca1a96a0bebed19c1dd906b1b4da8542
badc6aa65b2d3f10b0cdff3fc04bf3a64b551af1dd9e01b6ecd38ed71abdc3da
8d6ff96b718838005288a94cdc9fd408d1f70d7e9cbab678ebeb4521d11b366d

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker inspect python:latest | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; layers.python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the layers saved in the file &lt;code&gt;layers.python&lt;/code&gt; I can use a similar command to&lt;br&gt;
exclude the know python layers and get only the ones added by our build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker inspect secret:v1 | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; layers.python  | &lt;span class="nb"&gt;uniq
&lt;/span&gt;e1kq2j71b7clcwtn0lbmqa1g9
v6zy2xgzrow2mgpyq9d0vch6l

➜ docker inspect secret:v2 | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; layers.python  | &lt;span class="nb"&gt;uniq
&lt;/span&gt;v6zy2xgzrow2mgpyq9d0vch6l
e1kq2j71b7clcwtn0lbmqa1g9
oudofb9c0iaqog9sff81f8053

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker inspect secret:v3 | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; layers.python  | &lt;span class="nb"&gt;uniq
&lt;/span&gt;e1kq2j71b7clcwtn0lbmqa1g9
hqsrze873a2uz7tjsgbqdo3sd

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker inspect secret:v4 | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; layers.python  | &lt;span class="nb"&gt;uniq
&lt;/span&gt;78gz619okusgrq4jo4ek863ug
oq22x88p26hzqmr1w1qpf8mm4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the layers I have created a list just to make it simpler to&lt;br&gt;
find it when we check the directory. &lt;/p&gt;
&lt;h4&gt;
  
  
  Accessing the OverlayFS directories
&lt;/h4&gt;

&lt;p&gt;Using one of the commands below as root you will find the entry point where&lt;br&gt;
Docker Storage driver creates the file-system hierarchy used by the whole&lt;br&gt;
eco-system. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&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="nb"&gt;cd&lt;/span&gt; /var/lib/docker/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MacOS&lt;/strong&gt;&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;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host justincormack/nsenter1
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/lib/docker/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once in the &lt;code&gt;/var/lib/docker&lt;/code&gt; directory using a simple &lt;code&gt;ls&lt;/code&gt; and filtering the&lt;br&gt;
layers previous stored in the temporary file and expanding it I could find the&lt;br&gt;
specific layers that have the &lt;code&gt;pip.conf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/var/lib/docker/overlay2 
&lt;span class="c"&gt;# ls | grep -f /tmp/layers | xargs find | grep pip.conf&lt;/span&gt;
e1kq2j71b7clcwtn0lbmqa1g9/diff/root/.pip/pip.conf
hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf
oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I accessed each of of those to confirm if the file was present or if was just&lt;br&gt;
its shadow left by the directory union.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/var/lib/docker/overlay2 
&lt;span class="c"&gt;# cat e1kq2j71b7clcwtn0lbmqa1g9/diff/root/.pip/pip.conf&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;global]
index-url &lt;span class="o"&gt;=&lt;/span&gt; https://hugo.prudente:My&lt;span class="nv"&gt;$3cr3tP4$$&lt;/span&gt;@private.pip/playlist
&lt;span class="nb"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60
extra-index-url &lt;span class="o"&gt;=&lt;/span&gt; https://pypi.python.org/simpl

/var/lib/docker/overlay2 
&lt;span class="c"&gt;# cat hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt;: can&lt;span class="se"&gt;\'&lt;/span&gt;t open &lt;span class="s1"&gt;'hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf'&lt;/span&gt;: No such device or address

/var/lib/docker/overlay2 
&lt;span class="c"&gt;# cat oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt;: can&lt;span class="se"&gt;\'&lt;/span&gt;t open &lt;span class="s1"&gt;'oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf'&lt;/span&gt;: No such device or address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So 1 of 3 layers have the file present so I have checked from which container&lt;br&gt;
that layer belong to and here's the surprise.&lt;/p&gt;

&lt;p&gt;That 1 layer is shared with 3 of 4 builds that we have created, meaning that&lt;br&gt;
during a &lt;code&gt;docker pull&lt;/code&gt; three diferente containers could leak my &lt;code&gt;pip.conf&lt;/code&gt;&lt;br&gt;
secret.&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The updated matrix consolidating the results on where our secrets have leaked.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Inspection&lt;/th&gt;
&lt;th&gt;OverlayFS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;secret:v1&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret:v2&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret:v3&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret:v4&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So even knowing that the file is not acessible from the container directly if&lt;br&gt;
you have access to pull the container on a full read-write system you would be&lt;br&gt;
able to retreive the secrets.&lt;/p&gt;

&lt;p&gt;But now what's the best way to build the container and do not have such issue?&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;From 18.09 or newer Docker have introduced the Docker BuildKit that brings some&lt;br&gt;
extra funcionality to the Docker builds. &lt;/p&gt;

&lt;p&gt;The builds using BuildKit different from the legacy allows the usage of the&lt;br&gt;
&lt;code&gt;--secret&lt;/code&gt; that allows the capacity of binding a file during build runtime&lt;br&gt;
similar to the tradicional runtime that we achieve with &lt;code&gt;-v&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;It's usage is quite simple let's build a container and run our tests again.&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; python:latest&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret,id&lt;span class="o"&gt;=&lt;/span&gt;pip.conf,dst&lt;span class="o"&gt;=&lt;/span&gt;/root/.pip/pip.conf &lt;span class="se"&gt;\
&lt;/span&gt;      pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker build &lt;span class="nt"&gt;--file&lt;/span&gt; Dockerfile  &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pip.conf,src&lt;span class="o"&gt;=&lt;/span&gt;pip.conf &lt;span class="nt"&gt;-t&lt;/span&gt; secret:v5 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the &lt;code&gt;secret:v5&lt;/code&gt; build lets confirm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
➜ docker &lt;span class="nb"&gt;history &lt;/span&gt;secret:v5
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
266a21bb36ae   36 seconds ago   RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;playlist &lt;span class="c"&gt;# bui…   14.1MB    buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;      11 days ago      /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["python3"]              0Bi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The history in this case is clean, not even mention the mount for &lt;code&gt;pip.conf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
❯ docker run &lt;span class="nt"&gt;-it&lt;/span&gt; secret:v5 &lt;span class="nb"&gt;cat&lt;/span&gt; /root/.pip/pip.conf
&lt;span class="nb"&gt;cat&lt;/span&gt;: /root/.pip/pip.conf: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file is also not present on the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
❯ docker inspect secret:v5 | &lt;span class="nb"&gt;grep &lt;/span&gt;Dir | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s2"&gt;"([a-z0-9]{25,64})"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; layers.python  | &lt;span class="nb"&gt;uniq
&lt;/span&gt;cnpw0dw9o05lmdz3j9j62jzpt

on ⛵ k3s &lt;span class="o"&gt;(&lt;/span&gt;nerdweek&lt;span class="o"&gt;)&lt;/span&gt; ~/post via 🐍 v3.9.1 &lt;span class="o"&gt;(&lt;/span&gt;osx&lt;span class="o"&gt;)&lt;/span&gt;
/var/lib/docker/overlay2 &lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="nb"&gt;ls &lt;/span&gt;cnpw0dw9o05lmdz3j9j62jzpt | xargs find | &lt;span class="nb"&gt;grep &lt;/span&gt;pip.conf
find: committed: No such file or directory
find: diff: No such file or directory
find: &lt;span class="nb"&gt;link&lt;/span&gt;: No such file or directory
find: lower: No such file or directory
find: work: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the most important one the file doesn't exist on the layer/directory that we just&lt;br&gt;
created meaning that if we use in a base image our scretes are safe. &lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/develop/develop-images/build_enhancements/" rel="noopener noreferrer"&gt;https://docs.docker.com/develop/develop-images/build_enhancements/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html" rel="noopener noreferrer"&gt;https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/storage/storagedriver/overlayfs-driver/" rel="noopener noreferrer"&gt;https://docs.docker.com/storage/storagedriver/overlayfs-driver/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>secrets</category>
      <category>credentials</category>
      <category>security</category>
    </item>
    <item>
      <title>Using AWS Network ACLs With NAT Gateway</title>
      <dc:creator>Hugo Prudente</dc:creator>
      <pubDate>Sun, 29 Nov 2020 23:34:33 +0000</pubDate>
      <link>https://dev.to/hugoprudente/using-aws-network-acls-with-nat-gateway-3a5c</link>
      <guid>https://dev.to/hugoprudente/using-aws-network-acls-with-nat-gateway-3a5c</guid>
      <description>&lt;p&gt;It’s quite common the mistakes made when using the AWS Network ACLs for adding that extra layer of security in your VPC.&lt;/p&gt;

&lt;p&gt;Given the fact that Network ACLs are &lt;strong&gt;stateless&lt;/strong&gt;, meaning that the Inbound (Ingress) should have a matching rule for Outbound (Egress).&lt;/p&gt;

&lt;p&gt;With this post, you will learn how to identify such particularities using an AWS ElasticBeanstalk environment as an example, due to its sensitivity regarding network access requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fail to access any HTTPS/TLS endpoint resulting in timeout&lt;/li&gt;
&lt;li&gt;Fail to sync NTP servers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Difference between Security Group and Network ACL (NACL)
&lt;/h2&gt;

&lt;p&gt;The main difference between the Security Group and the Network ACL (NACL) is the&lt;br&gt;
the context where they are applied and the type of rules they provided.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A Security Group:&lt;br&gt;
Stateful: Therefore you don't need a rule that allows response traffic for inbound requests.&lt;br&gt;
Local: Therefore it applies only to the instance or service to which the security group is attached to. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Network ACL:&lt;br&gt;
Stateless: Therefore this rule is required to allow response traffic for inbound requests on the outbound rules.&lt;br&gt;
Global: Therefore it applies to all services that are placed on the subnet that is attached to.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8XL0r8kn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/t6723jas1nqkp8ifpgaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8XL0r8kn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/t6723jas1nqkp8ifpgaa.png" alt="AWS Network NACL &amp;amp; Security Group"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Scenario 1 - (Web Tier) Public Subnet with Network ACL without AWS NAT Gateway.
&lt;/h2&gt;

&lt;p&gt;Using the diagram below as an example to configure the Network ACL's for an ElasticBeanstalk environment.&lt;/p&gt;

&lt;p&gt;Diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------+
|                  |
|   +----------+   |
|   | INSTANCE |   |
|   +----------+   |
|   |  SG-001  |   |
|   +----------+   |
+------------------+
|    Subnet Pub    |
+------------------+
         +   +--------------+
         |---| ACL-001      |
         v   +--------------+
+------------------+
|       IGW        |
+------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As presented on the common issues, the simplified flow for UDP and HTTPS TCPDump packet below displays the return of 2 (two) actions from the EC2 belonging to the ElasticBeanstalk test environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;UDP 172.31.0.31:123 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 0.amazon.pool.ntp.org
UDP 0.amazon.pool.ntp.org &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 172.31.0.31:2000

TCP 172.31.0.31:443 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; aws.amazon.com
TCP aws.amazon.com &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 172.31.0.31:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we notice from the packet analysis the returning port is ephemeral and as we already know that Network ACL are stateless we need to account for that during the rules creation. Due to the Security Group being stateful, the above behavior will work properly. &lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;The solution, in this case, is a mix between the Security Group (SG-001) and the Network ACLs (ACL-001) where we can see:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Security Group&lt;/em&gt;&lt;/strong&gt; presents inbound traffic for TCP/HTTP, TCP/HTTPS for standard ports 80 and 443 from everywhere and outbound traffic from the instance to everywhere.&lt;/p&gt;

&lt;h4&gt;
  
  
  Security Group Inbound Rule (SG-001)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type  |Protocol |Port Range |Source
--------|-----------|-----------|------
HTTP  |TCP  |80 |0.0.0.0/0
HTTPS |TCP  |443  |0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Security Group Outbound Rule (SG-001)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type  |Protocol |Port Range |Source
--------|-----------|-----------|------
All |Traffic  |ALL  |ALL  |0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network ACL Inbound (ACL-001)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule #  Type  Protocol  Port Range  Destination Allow / Deny
102 HTTP (80) TCP (6) 80  0.0.0.0/0 ALLOW
103 HTTPS (443) TCP (6) 443 0.0.0.0/0 ALLOW
201 Custom TCP Rule TCP (6) 1024-65535  0.0.0.0/0 ALLOW
202 Custom UDP Rule UDP (17)  123 0.0.0.0/0 ALLOW
* ALL Traffic ALL ALL 0.0.0.0/0 DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network ACL Outbond (ACL-001)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule #  Type  Protocol  Port Range  Destination Allow / Deny
101 HTTP (80) TCP (6) 80  0.0.0.0/0 ALLOW
202 HTTPS (443) TCP (6) 443 0.0.0.0/0 ALLOW
102 Custom TCP Rule TCP (6) 1024-65535  0.0.0.0/0 ALLOW
103 Custom UDP Rule)  UDP (17)  1024-65535  0.0.0.0/0 ALLOW
* ALL Traffic ALL ALL 0.0.0.0/0 DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we know that ElasticBeanstalk does more than serve traffic but also has agents that connect to AWS API's we notice two different sets of configurations in the Network ACL's.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rules 102 and 103 allow inbound traffic for ports 80 and 443 following by its outbound pair, the 102 and 103 that are responsible to allow ephemeral port out to answer the requests.&lt;/li&gt;
&lt;li&gt;Rules 202 and 203 are using in the reverse order, an agent will post from the EC2 to AWS API, we analyze it inverting the tables. In this case, outbound requires 80 and 443 for the request, and the response will return in an ephemeral port on the inbound rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scenario 2 - (Worker Tier) Private Subnet with Network ACL and AWS NAT Gateway.
&lt;/h2&gt;

&lt;p&gt;Again using the diagram below as an example to configure the Network ACL's for an ElasticBeanstalk environment.&lt;/p&gt;

&lt;p&gt;Diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------+
|                  |
|   +----------+   |
|   | INSTANCE |   |
|   +----------+   |
|   |  SG-002  |   |
|   +----------+   |
+------------------+
|  Subnet Private  |
+------------------+
          +
          |   +--------------+
          |---| ACL-002      |
          v   +--------------+
+------------------+
|                  |
|   +----------+   |
|   |  NAT GW  |   |
|   |  SG-002  |   |
|   +----------+   |
+------------------+
|    Subnet Pub    |
+------------------+
         +   +--------------+
         |---| ACL-003      |
         v   +--------------+
+------------------+
|       IGW        |
+------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same as before as presented on the common issues, the simplified flow for UDP and HTTPS TCPDump packet below displays the return of 2 (two) actions from the EC2 belonging to the ElasticBeanstalk test environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UDP 172.31.0.31:123 &amp;gt; 172.31.0.34:2000
UDP 172.31.0.34:2000 &amp;gt; 0.amazon.pool.ntp.org
UDP 0.amazon.pool.ntp.org &amp;gt; 172.31.0.34:2000
UDP 172.31.0.34:2000 &amp;gt; 172.31.0.31:3000

TCP 172.31.0.31:443 &amp;gt; 172.31.0.33:2000
TCP 172.31.0.33:2000 &amp;gt; aws.amazon.com
TCP aws.amazon.com &amp;gt; 172.31.0.33:3000
TCP 172.31.0.33:3000 &amp;gt; 172.31.0.31:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that this is a little bit different than before, now 4 (four) pairs of packets with a extra jump returning different ports for each pair.  &lt;/p&gt;

&lt;p&gt;It happens due to a particular behavior of the AWS NAT Gateway. AWS Nat Gateway encapsulates packets to achieve higher performance, causing the change on the packet header during its life cycle. &lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;The solution, in this case, is a also a mix between the Security Group (SG-002) and the Network ACLs but this case ACL-002 and ACL-003 where we can see:&lt;/p&gt;

&lt;h4&gt;
  
  
  Security Group Inbound Rule (SG-002)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type  Protocol  Port Range  Source
HTTP  TCP 80  0.0.0.0/0
Custom UDP  UDP 123 0.0.0.0/0
Custom UDP  UDP 1024-65535  0.0.0.0/0
Security Group Outbond Rule (SG-002)
Type  Protocol  Port Range  Source
All Traffic ALL ALL 0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network ACL Inbound - Private Subnet (ACL-002)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule #  Type  Protocol  Port Range  Destination Allow / Deny
102 HTTP (80) TCP (6) 80  0.0.0.0/0 ALLOW
103 HTTPS (443) TCP (6) 443 0.0.0.0/0 ALLOW
202 Custom TCP Rule TCP (6) 1024-65535  0.0.0.0/0 ALLOW
203 Custom UDP Rule UDP (17)  123 0.0.0.0/0 ALLOW
* ALL Traffic ALL ALL 0.0.0.0/0 DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network ACL Outbond - Private Subnet (ACL-002)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule #  Type  Protocol  Port Range  Destination Allow / Deny
202 HTTP (80) TCP (6) 80  0.0.0.0/0 ALLOW
203 HTTPS (443) TCP (6) 443 0.0.0.0/0 ALLOW
102 Custom TCP Rule TCP (6) 1024-65535  0.0.0.0/0 ALLOW
103 Custom UDP Rule)  UDP (17)  1024-65535  0.0.0.0/0 ALLOW
* ALL Traffic ALL ALL 0.0.0.0/0 DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network ACL Inbound - Public Subnet (ACL-003)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule #  Type  Protocol  Port Range  Destination Allow / Deny
102 HTTP (80) TCP (6) 80  0.0.0.0/0 ALLOW
103 HTTPS (443) TCP (6) 443 0.0.0.0/0 ALLOW
202 Custom TCP Rule TCP (6) 1024-65535  0.0.0.0/0 ALLOW
203 Custom UDP Rule UDP (17)  123 0.0.0.0/0 ALLOW
* ALL Traffic ALL ALL 0.0.0.0/0 DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network ACL Outbond - Public Subnet (ACL-003)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule #  Type  Protocol  Port Range  Destination Allow / Deny
202 HTTP (80) TCP (6) 80  0.0.0.0/0 ALLOW
203 HTTPS (443) TCP (6) 443 0.0.0.0/0 ALLOW
102 Custom TCP Rule TCP (6) 1024-65535  0.0.0.0/0 ALLOW
103 Custom UDP Rule)  UDP (17)  1024-65535  0.0.0.0/0 ALLOW
* ALL Traffic ALL ALL 0.0.0.0/0 DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again we have the rule set for the Network ACL where.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ACL-002 Rules 102 and 103 allow inbound traffic for ports 80 and 443 following by its outbound pair, the 102 and 103 that are responsible to allow ephemeral port out to answer the requests.&lt;/li&gt;
&lt;li&gt;ACL-002 Rules 202 and 203 are using in the reverse order, an agent will post from the EC2 to AWS API, we analyze it inverting the tables. In this case, outbound requires 80 and 443 for the request, and the response will return in an ephemeral port on the inbound rules.&lt;/li&gt;
&lt;li&gt;We would expect only outbound traffic for the NAT Gateway, but due to the encapsulation, NAT Gateway is opening a new TCP connection with the backend using the new port requiring the same set as before.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These problems only happen if you tight the security on the Network ACL used by the AWS NAT Gateway, as the AWS default NACL that allows all the traffic inbound and outbound.&lt;/p&gt;

&lt;p&gt;Originally posted on: hugoprudente.github.io/en/posts/&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html"&gt;AWS User Guide for VPC ACLs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-nat-gateway.html"&gt;AWS User Guide for VPC NAT Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>natgateway</category>
      <category>nacl</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
