<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Tag: Docker - Ryan Daniels</title>
	<atom:link href="https://ryandaniels.ca/blog/tag/docker/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Sat, 25 Jan 2025 20:08:10 +0000</lastBuildDate>
	<language>en-CA</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://ryandaniels.ca/wp-content/uploads/2019/07/img_5907-small-blur-square-100x100.jpg</url>
	<title>Tag: Docker - Ryan Daniels</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">22628916</site>	<item>
		<title>heredoc in Dockerfile</title>
		<link>https://ryandaniels.ca/blog/heredoc-in-dockerfile/</link>
		
		<dc:creator><![CDATA[Ryan Daniels]]></dc:creator>
		<pubDate>Sat, 25 Jan 2025 19:56:51 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<guid isPermaLink="false">https://ryandaniels.ca/?p=4421</guid>

					<description><![CDATA[<figure class="alignleft wp-block-image size-thumbnail"><img src="https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker-300x124.jpg" alt="" class="wp-image-4428"/></figure>
<p>Writing a Dockerfile with long scripts inside can be very annoying. Instead use a heredoc. Please no "&#038;& \" over and over.<br />
&#160;</p>
<p>The post <a href="https://ryandaniels.ca/blog/heredoc-in-dockerfile/">heredoc in Dockerfile</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Writing a Dockerfile with long scripts inside can be very annoying. Instead use a heredoc. <br>Please no &#8220;&amp;&amp; \&#8221; over and over.</p>



<p>Please, no:</p>



<pre class="wp-block-code"><code>RUN dnf upgrade -y &amp;&amp; \
    dnf install -y package123 &amp;&amp; \
    rm -rf /var/cache/dnf/* &amp;&amp; \
    dnf clean all</code></pre>



<p>All those &#8220;&amp;&amp; \&#8221; over and over.. But the bigger issue for me is figuring out where something broke. When there&#8217;s an error the whole block of code is shown and no indication where the problem actually is!</p>



<p>Heredoc inside a Dockerfile / Containerfile to save the day:</p>



<pre class="wp-block-code"><code>RUN &lt;&lt;EOF
    set -ex
    dnf upgrade -y
    dnf install -y package123
    rm -rf /var/cache/dnf/*
    dnf clean all
EOF</code></pre>



<p>Or even better, specify bash shell if the image has it, with pipefail:</p>



<pre class="wp-block-code"><code>SHELL &#91;"/bin/bash", "-o", "pipefail", "-c"]
RUN &lt;&lt;EOF bash
    set -ex
    # Do stuff
EOF</code></pre>



<h2 class="wp-block-heading">What is a heredoc?</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>As described by <a href="https://en.wikipedia.org/wiki/Here_document" target="_blank" rel="noreferrer noopener">Wikipedia</a>:<br>here document (here-document, here-text, heredoc, hereis, here-string or here-script) is a file literal or input stream literal: it is a section of a source code file that is treated as if it were a separate file. The term is also used for a form of multiline string literals that use similar syntax, preserving line breaks and other whitespace (including indentation) in the text.<br>The most common syntax for here documents, originating in Unix shells, is &lt;&lt; followed by a delimiting identifier (often the word EOF or END), followed, starting on the next line, by the text to be quoted, and then closed by the same delimiting identifier on its own line. This syntax is because here documents are formally stream literals, and the content of the here document is often redirected to stdin (standard input) of the preceding command or current shell script/executable.<br>The here document syntax is analogous to the shell syntax for input redirection, which is &lt; followed by the name of the file to be used as input.</p>
</blockquote>



<p>More information about heredocs is in <a href="https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/" target="_blank" rel="noreferrer noopener">Docker&#8217;s announcement</a>. Podman also supports heredocs.</p>


<div class="wp-block-image is-style-default">
<figure class="alignright size-thumbnail"><a href="https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker.jpg" rel="lightbox[4421]"><img decoding="async" width="300" height="124" src="https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker-300x124.jpg" alt="Neo from The Matrix saying: &quot;I know docker&quot;. Maybe he also knows heredoc" class="wp-image-4428" srcset="https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker-300x124.jpg 300w, https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker-800x331.jpg 800w, https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker-768x317.jpg 768w, https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker-1200x496.jpg 1200w, https://ryandaniels.ca/wp-content/uploads/2025/01/i-know-docker.jpg 1280w" sizes="(max-width: 300px) 100vw, 300px" /></a></figure></div>


<h2 class="wp-block-heading">Conclusion</h2>



<p>Writing long scripts in a Dockerfile / Containerfile is painful. Use heredocs instead to make life easier.</p>



<p>This will also help if you start using <a href="https://ryandaniels.ca/blog/bootc-bootable-containers-one-container-image-to-rule-them-all/">bootc</a>!</p>



<p></p>
<p>The post <a href="https://ryandaniels.ca/blog/heredoc-in-dockerfile/">heredoc in Dockerfile</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4421</post-id>	</item>
		<item>
		<title>Docker and Trouble with Red Hat Enterprise Linux 9: iptables</title>
		<link>https://ryandaniels.ca/blog/docker-and-trouble-with-red-hat-enterprise-linux-9-iptables/</link>
		
		<dc:creator><![CDATA[Ryan Daniels]]></dc:creator>
		<pubDate>Sun, 19 Mar 2023 19:18:17 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[RHEL]]></category>
		<guid isPermaLink="false">https://ryandaniels.ca/?p=2846</guid>

					<description><![CDATA[<p>Red Hat Enterprise Linux 9 (RHEL 9) and Docker don't get along very well. Running a container that requires older iptables (and not nftables) can be a problem.</p>
<p>The post <a href="https://ryandaniels.ca/blog/docker-and-trouble-with-red-hat-enterprise-linux-9-iptables/">Docker and Trouble with Red Hat Enterprise Linux 9: iptables</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Red Hat Enterprise Linux 9 (RHEL 9) and Docker don&#8217;t get along very well sometimes. It turns out, running a container that requires older iptables (and not nftables) can be a problem.<br>This is a problem for other Operating Systems too that use nftables, but let&#8217;s focus on RHEL 9 today. </p>



<h2 class="wp-block-heading">Problem with running a newer OS (like RHEL 9)</h2>



<p>Wow, RHEL 9 is using a modern kernel and toolchain. Ok, now I can continue.</p>



<p>Some newer Linux Operating Systems are moving away from <a href="https://en.wikipedia.org/wiki/Iptables" target="_blank" rel="noreferrer noopener">iptables</a> and changing to <a href="https://en.wikipedia.org/wiki/Nftables" target="_blank" rel="noreferrer noopener">nftables</a>. This can be a problem when an app is expecting iptables. Normally the OS can load the older iptables, but you can run into problems when running containers. </p>



<h3 class="wp-block-heading">Error from GitLab Runner</h3>



<p>It&#8217;s even more fun trying to figure out what&#8217;s going on when running on a GitLab Runner.</p>



<p>This is the error from a GitLab Runner job:</p>



<pre class="wp-block-code"><code>ERROR: error during connect: Get https://docker:2376/v1.40/info: dial tcp: lookup docker on 8.8.8.8:53: server misbehaving</code></pre>



<h3 class="wp-block-heading">Error when running Docker container</h3>



<p>This is the error when running a container using Docker:</p>



<pre class="wp-block-code"><code># docker run --rm -it --privileged --name dind docker:19.03-dind

...
INFO&#91;2023-03-18T21:03:46.764203869Z] Loading containers: start.                   
WARN&#91;2023-03-18T21:03:46.774269934Z] Running iptables --wait -t nat -L -n failed with message: `iptables v1.8.4 (legacy): can't initialize iptables table `nat': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.`, error: exit status 3 
INFO&#91;2023-03-18T21:03:46.801647539Z] stopping event stream following graceful shutdown  error="&lt;nil&gt;" module=libcontainerd namespace=moby
...
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N DOCKER: iptables v1.8.4 (legacy): can't initialize iptables table `nat': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.
 (exit status 3)</code></pre>



<p>So here we can see the problem. Our container is trying to use iptables and it&#8217;s not working.</p>



<p>But, if you run a newer Docker in Docker (DinD) container, it works! And then the old container also starts to work (this is, until a reboot of the host). The culprit? iptables, and a clever trick in newer DinD containers to workaround this issue..</p>



<h2 class="wp-block-heading">Enable iptables from inside a Container</h2>



<p>If you run a container as privileged, you can actually trigger the host OS to load the iptables kernel modules! </p>



<p>This was found from Docker in Docker (DinD) adding a <a href="https://github.com/docker-library/docker/blob/849b56e6c81dc509da780121352f844e8f26bb7a/23.0/cli/modprobe.sh" target="_blank" rel="noreferrer noopener">custom modprobe script</a> (called from the <a href="https://github.com/docker-library/docker/blob/94129ecd12de7acbc9d5a15d25d535ee091770b1/23.0/dind/dockerd-entrypoint.sh#L152" target="_blank" rel="noreferrer noopener">entrypoint script</a>), which is essentially just running this command:</p>



<pre class="wp-block-code"><code># ip link show ip_tables</code></pre>



<p>The Docker in Docker modprobe script gives credit to:</p>


<div class="wp-block-image">
<figure class="aligncenter"><img fetchpriority="high" decoding="async" width="550" height="273" src="https://ryandaniels.ca/wp-content/uploads/2023/02/Luca_Bruno-ip_link_show_module-dark.png" alt="Loading Linux kernel modules the 'alternative' way: 'ip link show $module'. No need for modprobe binary, kernel object files. This fixes the problem with RHEL 9 and iptables with Docker" class="wp-image-2847" srcset="https://ryandaniels.ca/wp-content/uploads/2023/02/Luca_Bruno-ip_link_show_module-dark.png 550w, https://ryandaniels.ca/wp-content/uploads/2023/02/Luca_Bruno-ip_link_show_module-dark-300x149.png 300w" sizes="(max-width: 550px) 100vw, 550px" /></figure></div>


<h2 class="wp-block-heading">Enable iptables on Red Hat Enterprise Linux 9</h2>



<p>But if your container isn&#8217;t using this &#8216;ip link show ip_tables&#8217; trick, the container will have problems. You need to enable the iptables legacy module on your host OS.</p>



<p>To have iptables loaded and ready to go, you can also run the above trick directly on the host. But the &#8220;proper&#8221; way is to use modprobe when the OS boots.</p>



<pre class="wp-block-code"><code># modprobe ip_tables</code></pre>



<p>That will dynamically enable the older iptables. But after a reboot the change is gone, so to make a persistent change:</p>



<pre class="wp-block-code"><code>echo ip_tables &gt; /etc/modules-load.d/ip_tables.conf</code></pre>



<p>Reboot and check:</p>



<pre class="wp-block-code"><code># lsmod|grep -E "^ip_tables|^iptable_filter|^iptable_nat"
ip_tables              28672  0</code></pre>



<p>Now the older containers will also work (that need iptables (legacy).</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Red Hat doesn&#8217;t recommend running Docker (instead they recommend Podman). Probably for these reasons. And I wonder if this problem also has something to do with the underlying and undocumented way that <a href="https://ryandaniels.ca/blog/docker-iptables-input-chain/">Docker uses iptables INPUT chain</a>.</p>
<p>The post <a href="https://ryandaniels.ca/blog/docker-and-trouble-with-red-hat-enterprise-linux-9-iptables/">Docker and Trouble with Red Hat Enterprise Linux 9: iptables</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2846</post-id>	</item>
		<item>
		<title>Secure Docker with iptables firewall and Ansible</title>
		<link>https://ryandaniels.ca/blog/secure-docker-with-iptables-firewall-and-ansible/</link>
		
		<dc:creator><![CDATA[Ryan Daniels]]></dc:creator>
		<pubDate>Sun, 24 May 2020 19:11:41 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[CentOS]]></category>
		<category><![CDATA[Guide]]></category>
		<category><![CDATA[IT Automation]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<guid isPermaLink="false">https://ryandaniels.ca/?p=2290</guid>

					<description><![CDATA[<figure class="alignleft wp-block-image size-thumbnail"><img src="https://ryandaniels.ca/wp-content/uploads/2020/05/punch_hole_eye-200x300.jpg" alt="" class="wp-image-2489"/></figure>
<p>Out of the box, security with Docker (and Docker Swarm) over the network is bad. Okay, that's not entirely true. Out of the box when you have no containers started, it's fine. But after you start a container, and if you publish a port, they are exposed to the outside world by default. And it's not easy to fix. You need to create a custom Docker firewall with iptables.<br />
&#160;</p>
<p>The post <a href="https://ryandaniels.ca/blog/secure-docker-with-iptables-firewall-and-ansible/">Secure Docker with iptables firewall and Ansible</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Out of the box, security with Docker (and Docker Swarm) over the network is bad. Okay, that&#8217;s not entirely true. Out of the box when you have no containers started, it&#8217;s fine. But after you start a container, and if you publish a port, they are exposed to the outside world by default. And it&#8217;s not easy to fix. You need to create a custom Docker firewall with iptables.</p>



<p>Let&#8217;s discuss the background of firewall issues with Docker, and a working solution for my use case (either setup manually or using Ansible). By the end we will use a firewall on the server to lock down everything by default, only allowing my trusted IPs! With the option to open specified ports publicly (like SSH).</p>



<p>Note: This solution works with CentOS 7, RHEL 7, Ubuntu 18.04, and Ubuntu 20.04.</p>



<span id="more-2290"></span>


				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-5708b17e-d0f5-4528-8221-706c39a8a865      "
					data-scroll= ""
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							Table Of Contents						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#background" class="uagb-toc-link__trigger">Background</a><li class="uagb-toc__list"><a href="#roll-your-own-solution" class="uagb-toc-link__trigger">Roll your own solution</a><li class="uagb-toc__list"><a href="#problems-i-need-to-solve" class="uagb-toc-link__trigger">Problems I need to solve</a><li class="uagb-toc__list"><a href="#solution-docker-firewall-with-iptables-and-ipset" class="uagb-toc-link__trigger">Solution &#8211; Docker firewall with iptables and ipset</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#high-level-summary" class="uagb-toc-link__trigger">High level summary</a></li></ul></li><li class="uagb-toc__list"><a href="#warnings" class="uagb-toc-link__trigger">Warnings</a><li class="uagb-toc__list"><a href="#the-manual-way" class="uagb-toc-link__trigger">The manual way</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#prep" class="uagb-toc-link__trigger">Prep</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#configure-ipset" class="uagb-toc-link__trigger">Configure ipset</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#configure-iptables" class="uagb-toc-link__trigger">Configure iptables</a></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#the-automatic-way-with-ansible" class="uagb-toc-link__trigger">The Automatic way with Ansible</a><li class="uagb-toc__list"><a href="#conclusion" class="uagb-toc-link__trigger">Conclusion</a><li class="uagb-toc__list"><a href="#references" class="uagb-toc-link__trigger">References</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#background-for-dockers-undocumented-use-of-iptables-input-chain" class="uagb-toc-link__trigger">Background for Docker&#039;s undocumented use of iptables INPUT chain</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#references-and-links" class="uagb-toc-link__trigger">References and links</a></ul></ul></ul></ol>					</div>
									</div>
				</div>
			


<h2 class="wp-block-heading">Background</h2>



<div class="wp-block-image"><figure class="alignright size-medium"><img decoding="async" width="400" height="600" src="https://ryandaniels.ca/wp-content/uploads/2020/05/punch_hole_eye-400x600.jpg" alt="Docker punches a hole through firewall" class="wp-image-2489" srcset="https://ryandaniels.ca/wp-content/uploads/2020/05/punch_hole_eye-400x600.jpg 400w, https://ryandaniels.ca/wp-content/uploads/2020/05/punch_hole_eye-480x720.jpg 480w, https://ryandaniels.ca/wp-content/uploads/2020/05/punch_hole_eye-200x300.jpg 200w, https://ryandaniels.ca/wp-content/uploads/2020/05/punch_hole_eye.jpg 533w" sizes="(max-width: 400px) 100vw, 400px" /><figcaption><sup>Image credit: <a rel="noreferrer noopener" href="https://www.pexels.com/photo/brown-human-eye-2873058/" target="_blank">Jonathan Borba</a></sup></figcaption></figure></div>



<p>Firstly, even if you were using a firewall like iptables, Docker makes that useless. Docker punches a whole right through your firewall!</p>



<p>And if you <a href="https://ryandaniels.ca/blog/ansible-manage-firewalld/">try another firewall, like firewalld</a>? Docker Swarm (and even regular Docker) with firewalld is a complete mess. Restart the firewalld service, or change the firewalld config, and you lost all the config that Docker needed. Now you have to restart Docker! What happens if the firewalld service failed and restarted? That Docker Swarm node is out of service.</p>



<p>Keep in mind, when I mention Docker, it means the regular Docker Engine. Docker Swarm means SwarmKit, which is the newer way of using Swarm. (The old way is the standalone solution which is old and not referenced at all here).</p>



<p>There are many attempts to solve this problem by users of Docker. Unfortunately the Docker team has been pretty quiet about these issues. They recommend a manual user solution, and to disable Docker&#8217;s use of iptables. I&#8217;m speculating here, but it seems like any future change from Docker will likely be a breaking change since this is a complicated issue to fix.</p>



<p>The attempted solutions (from users) are not very straight forward for a normal Docker user. And that&#8217;s the real issue. On top of that, out of the box (after you start one service with a published port), there is no security at the network layer. Anyone can connect to an exposed container&#8217;s port. Most importantly, even if you think you have a firewall protecting you.. Wrong, you don&#8217;t! With normal Docker, you can bind your service to your localhost which helps. But what about Docker Swarm? Nope, that doesn&#8217;t work.</p>



<p>There&#8217;s a great article about &#8220;<a rel="noreferrer noopener" href="https://utcc.utoronto.ca/~cks/space/blog/tech/SecurityChoiceProblem" data-type="URL" data-id="https://utcc.utoronto.ca/~cks/space/blog/tech/SecurityChoiceProblem" target="_blank">The problem of forcing users to make choices (in security)</a>.&#8221; Definitely worth the read!</p>



<h2 class="wp-block-heading">Roll your own solution</h2>



<p>Until this is actually addressed in Docker, our only hope is to find the simplest solution possible. And turning off iptables integration in Docker is unacceptable (which is constantly recommended by the Docker developers). The other option is to move on from Docker and/or Docker Swarm. I hear this thing called Kubernetes is pretty great. Anyways, back to Docker. <br>Many people have spent hours trying to learn, and figure out iptables as a solution to this. You need to roll your own solution apparently! So iptables is probably the best approach, since that is what Docker needs to use to do it&#8217;s magic (and most other firewalls are just a wrapper for iptables anyway depending on your OS).</p>



<p>Something <s>fun</s> I found out while testing this, <a href="https://ryandaniels.ca/blog/docker-iptables-input-chain/">Docker Swarm uses iptables in an undocumented way</a>. Docker Swarm uses the iptables INPUT chain! It&#8217;s only for encrypted overlay networks. But it&#8217;s not very fun realizing that! All of a sudden rules are being appended to the INPUT chain.</p>



<p>Okay, enough backstory. On with my futile attempt to roll my own solution. This took way longer than I thought it would! But it does work! Currently it works at least.. (That&#8217;s why I use the word futile!)</p>



<h2 class="wp-block-heading">Problems I need to solve</h2>



<ol class="wp-block-list"><li>Only allow traffic from multiple &#8220;trusted&#8221; IP addresses to my servers. Not all of these IPs will be in the same &#8220;IP block/range&#8221; either. This will be to all services running directly on the server, and also all of the Docker containers.</li><li>Let only specific ports be publicly accessible, like SSH.</li><li>I&#8217;m not managing which containers are accessible through the firewall. Meaning, I&#8217;m not manually adding ports into my firewall solution. That kind of manual work is not happening. I need a dynamic, and flexible solution that blocks by default except to my trusted IPs.</li><li>The firewall solution must be simple. More complex means more room for error.</li><li>The firewall solution must not impact performance significantly.</li><li>Restarting the firewall won&#8217;t break Docker. </li><li>Restarting Docker won&#8217;t break the firewall.</li><li>No impact to running server processes or Docker services when making a change. Things need to keep working! <br>Firewall changes need to happen online and not impact Docker. Meaning I can&#8217;t be restarting Docker because I made a firewall change.<br>Docker &#8220;changes&#8221; need to happen online. Meaning I can&#8217;t be restarting the firewall because I made a Docker change. (A Docker &#8220;change&#8221; means starting/stopping a container).</li></ol>



<p>That sounds very simple! Unfortunately, it is not with Docker (and Docker Swarm).</p>



<h2 class="wp-block-heading">Solution &#8211; Docker firewall with iptables and ipset</h2>



<p>If you don&#8217;t know much about iptables, or ipset, that&#8217;s okay. You don&#8217;t really need to know. You should have some basic understandings though, so you don&#8217;t break your servers! The <a rel="noreferrer noopener" href="https://wiki.archlinux.org/index.php/Iptables#Chains" target="_blank">Arch Linux wiki</a> has great information about iptables. Including this helpful <a rel="noreferrer noopener" href="https://www.frozentux.net/iptables-tutorial/chunkyhtml/images/tables_traverse.jpg" target="_blank" rel="lightbox[2290]">visual</a> about the iptables flow.</p>



<p>Note: This solution works with CentOS 7, RHEL 7, Ubuntu 18.04, and Ubuntu 20.04.</p>



<h3 class="wp-block-heading">High level summary</h3>



<p>iptables with ipset will handle all of this for us. And keep our servers, and Docker locked down from the network level. </p>



<p>In this solution, we will use the iptables INPUT chain to jump to another chain (let&#8217;s call our custom chain FILTERS), but return if there&#8217;s some legitimate looking traffic, so the Swarm overlay can do whatever it wants in INPUT with IPSEC, or whatever it is appending to INPUT.<br>Inside our custom chain FILTERS, we drop everything that doesn&#8217;t match our trusted list of IPs. We also allow our SSH port and the basic default iptables stuff.. You can also add any OS port to be publicly accessible.<br>The DOCKER-USER chain only needs a few entries. Any internal Docker traffic is returned, and it will drop any other traffic that&#8217;s not in our allowed IP list. You can also add any container port to be publicly accessible.</p>



<p>One of the dangers with this approach is if Docker changes it&#8217;s behaviour our firewall could break, or our Docker services could stop working. Since Docker doesn&#8217;t offer any solution for their users, we need our own solution. So keep in mind that you need to test this when upgrading to a new version of Docker. That is the trade-off with a &#8220;roll your own&#8221; solution. But what choice do we have?</p>



<p>I&#8217;ve created an <a rel="noreferrer noopener" href="https://galaxy.ansible.com/ryandaniels/iptables_docker" target="_blank">Ansible Role: iptables for Docker</a>, on <a rel="noreferrer noopener" href="https://github.com/ryandaniels/ansible-role-iptables-docker" target="_blank">GitHub</a> and <a rel="noreferrer noopener" href="https://galaxy.ansible.com/ryandaniels/iptables_docker" target="_blank">Ansible Galaxy</a>.</p>



<h2 class="wp-block-heading">Warnings</h2>



<p><strong>Warning: Be sure you have everything needed in your configuration. Once the iptables firewall is started it blocks anything that wasn’t added! Don&#8217;t lock yourself out of your server. Be sure to have another way to connect, like a console.</strong></p>



<p><strong>Disclaimer: Keep in mind, you should test all of this in your lab or staging environments. I can’t guarantee this will be 100% safe and can’t be held responsible for anything going wrong!</strong></p>



<p><strong>SELinux Bug</strong>: If using SELinux, currently there&#8217;s a bug with SELinux which prevents saving the iptables rules to the iptables.save file.<br><strong>Impact</strong>: Saving the iptables rules a 2nd time will silently fail. Workaround has been added so SELinux allows chmod to interact with the iptables.save file. <a rel="noreferrer noopener" href="https://github.com/ryandaniels/ansible-role-iptables-docker/blob/master/README.md#selinux-manual-workaround-for-iptables-and-chmod" target="_blank">See notes on GitHub for SELinux workaround steps</a>. Alternatively you could disable SELinux, but that&#8217;s not recommended. Bug report: <a rel="noreferrer noopener" href="https://bugs.centos.org/view.php?id=12648" target="_blank">https://bugs.centos.org/view.php?id=12648</a></p>



<h2 class="wp-block-heading">The manual way</h2>



<p>Run the commands below. These commands are only for CentOS/RHEL 7. If you don&#8217;t want to do this manually, jump to the <a href="#the-automatic-way-with-ansible">Automatic section, using Ansible</a> (which also works with Ubuntu).</p>



<h3 class="wp-block-heading">Prep</h3>



<p>Make note of what you already have in iptables (if you are already using it). Be sure you have some background with iptables, since you could break things!</p>



<pre class="wp-block-code"><code># iptables -nvL --line-numbers</code></pre>



<p>Install the required packages for CentOS / RHEL:</p>



<pre class="wp-block-code"><code># yum install iptables iptables-services ipset ipset-service</code></pre>



<h3 class="wp-block-heading">Configure ipset</h3>



<p>ipset allows you to add a list of IPs that you can use with iptables. In our case, we will add a list of IPs we want to be able to connect to our servers.</p>



<p>Configure ipset with a setname of <code>ip_allow</code>.<br>Add IPs you want to allow. Change the IPs below to your actual trusted/allowed IP ranges. Be sure to include your Docker server IPs here, because if you don&#8217;t they can&#8217;t communicate with eachother:</p>



<pre class="wp-block-code"><code># mkdir -p /etc/sysconfig/ipset.d
# vi /etc/sysconfig/ipset.d/ip_allow.set

create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536
add ip_allow 192.168.1.123
add ip_allow 192.168.100.0/24
add ip_allow 192.168.101.0/24
add ip_allow 192.168.102.0/24</code></pre>



<p>Start, and Enable the ipset service:</p>



<pre class="wp-block-code"><code># systemctl status ipset
# systemctl start ipset
# systemctl enable ipset</code></pre>



<p>See what ipset has in it&#8217;s loaded configuration:</p>



<pre class="wp-block-code"><code># ipset list | head</code></pre>



<p>Important: Make note of the size of &#8220;Number of entries&#8221;. If that number is close to the maxelem size (65536), then you need to delete the ipset and re-create it with a larger max size. If you only use a few IP ranges like above, you don&#8217;t need to worry and will be well below the limit.</p>



<h3 class="wp-block-heading">Configure iptables</h3>



<p>Next up, iptables. iptables is our solution for a firewall. We will create a file with our rules and then add those rules into iptables. The important part is to not flush the existing rules if you are already using Docker  (or Docker Swarm) on your server.</p>



<p>Create an iptables file to use with iptables-restore, to add the rules into iptables:</p>



<pre class="wp-block-code"><code># vi iptables-rules.txt</code></pre>



<p>Add below to the file. There is a lot going on here..</p>



<pre class="wp-block-code"><code>*filter
:DOCKER-USER - &#91;0:0]
:FILTERS - &#91;0:0]
#Can't flush INPUT. wipes out docker swarm encrypted overlay rules
#-F INPUT
#Use ansible or run manually once instead to add -I INPUT -j FILTERS
#-I INPUT -j FILTERS
-F DOCKER-USER
-A DOCKER-USER -m state --state RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -i docker_gwbridge -j RETURN
-A DOCKER-USER -s 172.18.0.0/16 -j RETURN
-A DOCKER-USER -i docker0 -j RETURN
-A DOCKER-USER -s 172.17.0.0/16 -j RETURN
#Below Docker ports open to everyone if uncommented
#-A DOCKER-USER -p tcp -m tcp -m multiport --dports 8000,8001 -j RETURN
#-A DOCKER-USER -p udp -m udp -m multiport --dports 9000,9001 -j RETURN
-A DOCKER-USER -m set ! --match-set ip_allow src -j DROP
-A DOCKER-USER -j RETURN
-F FILTERS
#Because Docker Swarm encrypted overlay network just appends rules to INPUT. Has to be at top unfortunately
-A FILTERS -p udp -m policy --dir in --pol ipsec -m udp -m set --match-set ip_allow src --dport 4789 -j RETURN
-A FILTERS -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FILTERS -p icmp -j ACCEPT
-A FILTERS -i lo -j ACCEPT
#Below OS ports open to everyone if uncommented
-A FILTERS -p tcp -m state --state NEW -m tcp -m multiport --dports 22 -j ACCEPT
#-A FILTERS -p udp -m udp -m multiport --dports 53,123 -j ACCEPT
-A FILTERS -m set ! --match-set ip_allow src -j DROP
-A FILTERS -j RETURN
COMMIT</code></pre>



<p>Use iptables-restore to add the above rules into iptables. The very important flag is <code>-n</code>. That makes sure we don&#8217;t flush the iptables rules if we have rules already in Docker (or Docker Swarm).</p>



<pre class="wp-block-code"><code># iptables-restore -n &lt; iptables-rules.txt</code></pre>



<p>Next, add a rule to the INPUT chain, so we start using the new rules in FILTERS. It has to be at the top, and only needs to be added once:</p>



<pre class="wp-block-code"><code># iptables -I INPUT 1 -j FILTERS</code></pre>



<p>Save the iptables rules:</p>



<pre class="wp-block-code"><code># /usr/libexec/iptables/iptables.init save</code></pre>



<p>That will save any existing and our new iptables rules to the iptables configuration file so it will be persistent after a reboot.</p>



<p>In addition, it was needed to run the above iptables command manually since we want to ensure it&#8217;s only inserted once. And we can&#8217;t flush the INPUT chain to ensure that since Docker Swarm could have rules there already.</p>



<p>Start and Enable the iptables service:</p>



<pre class="wp-block-code"><code># systemctl status iptables
# systemctl start iptables
# systemctl enable iptables</code></pre>



<p>If you want to customize the iptables rules to allow more ports to be open to everyone, just add the port to the appropriate rule in the iptables file (tcp or udp), then re-run the same commands from above:</p>



<pre class="wp-block-code"><code># iptables-restore -n &lt; iptables-rules.txt
# /usr/libexec/iptables/iptables.init save</code></pre>



<p><strong>Don&#8217;t miss the <a href="#warnings">Warnings</a> from above! Especially about SELinux.</strong></p>



<p>If you don&#8217;t want to do all of that manually, and you use Ansible, then do this instead..</p>



<h2 class="wp-block-heading">The Automatic way with Ansible</h2>



<p>Manually run all of the above, on every Docker server is not ideal. Let&#8217;s use Ansible instead!</p>



<p>I&#8217;ve created an <a rel="noreferrer noopener" href="https://galaxy.ansible.com/ryandaniels/iptables_docker" target="_blank">Ansible Role: iptables for Docker</a>, on <a rel="noreferrer noopener" href="https://github.com/ryandaniels/ansible-role-iptables-docker" target="_blank">GitHub</a> and <a rel="noreferrer noopener" href="https://galaxy.ansible.com/ryandaniels/iptables_docker" target="_blank">Ansible Galaxy</a>. </p>



<p>This works on CentOS 7, RHEL 7, Ubuntu 18.04, and Ubuntu 20.04.</p>



<p>Install the Ansible Role using Ansible Galaxy:</p>



<pre class="wp-block-code"><code>$ ansible-galaxy install ryandaniels.iptables_docker</code></pre>



<p>Or, clone the GitHub project:</p>



<pre class="wp-block-code"><code>$ git clone https://github.com/ryandaniels/ansible-role-iptables-docker.git roles/ryandaniels.iptables_docker</code></pre>



<p>Create the Ansible Playbook, called iptables_docker.yml:</p>



<pre class="wp-block-code"><code>---
- hosts: '{{ inventory }}'
  become: yes
  vars:
    # Use this role
    iptables_docker_managed: true
  roles:
  - ryandaniels.iptables_docker</code></pre>



<p>Make configuration changes to add desired IP addresses and ports as needed.</p>



<p><strong>Don&#8217;t miss the <a href="#warnings">Warnings</a> from above!</strong></p>



<p>Then run the playbook:</p>



<pre class="wp-block-code"><code>$ ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7" -i hosts-dev</code></pre>



<h2 class="wp-block-heading">Conclusion</h2>



<p>In conclusion, now we have secured our Docker (and Docker Swarm) environments using Ansible to perform the installation and configuration of iptables! None of our Docker published ports are exposed to the world, unless we want them to be! We have created a custom Docker firewall with iptables. Hopefully, some day this will be the default behaviour and shipped with Docker out of the box! Dare to dream. Security is hard.</p>



<h2 class="wp-block-heading">References</h2>



<h3 class="wp-block-heading">Background for Docker&#8217;s undocumented use of iptables INPUT chain</h3>



<p><a href="https://ryandaniels.ca/blog/docker-iptables-input-chain/">See my previous post about this</a>.</p>



<h3 class="wp-block-heading">References and links</h3>



<p>References, notes, and links about the Docker firewall discussion:</p>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" href="https://docs.docker.com/network/overlay/#encrypt-traffic-on-an-overlay-network" target="_blank">Docker Documentation &#8211; Overlay Networks</a></li><li><a rel="noreferrer noopener" href="https://github.com/docker/for-linux/issues/690" target="_blank">Docker bypasses ufw firewall rules</a></li><li><a rel="noreferrer noopener" href="https://unrouted.io/2017/08/15/docker-firewall/" target="_blank">unrouted</a> &#8211; Solution using iptables. Not for Swarm. It clobbers the INPUT chain, which is used by encrypted overlay with Docker Swarm</li><li><a rel="noreferrer noopener" href="https://github.com/moby/moby/issues/22054" target="_blank">The big thread about Docker and a firewall</a></li><li><a rel="noreferrer noopener" href="https://wiki.archlinux.org/index.php/Iptables#Chains" target="_blank">Arch Linux wiki to the rescue to show iptables</a> flow which links to a <a rel="noreferrer noopener" href="https://www.frozentux.net/iptables-tutorial/chunkyhtml/images/tables_traverse.jpg" target="_blank" rel="lightbox[2290]">great visual</a><br><br></li></ul>
<p>The post <a href="https://ryandaniels.ca/blog/secure-docker-with-iptables-firewall-and-ansible/">Secure Docker with iptables firewall and Ansible</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2290</post-id>	</item>
		<item>
		<title>Docker and the iptables INPUT chain</title>
		<link>https://ryandaniels.ca/blog/docker-iptables-input-chain/</link>
		
		<dc:creator><![CDATA[Ryan Daniels]]></dc:creator>
		<pubDate>Tue, 19 May 2020 01:02:55 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://ryandaniels.ca/?p=2393</guid>

					<description><![CDATA[<p>When testing Docker and iptables I stumbled upon something interesting. It appears Docker uses the iptables INPUT chain in an undocumented way. Well that's interesting..</p>
<p>The post <a href="https://ryandaniels.ca/blog/docker-iptables-input-chain/">Docker and the iptables INPUT chain</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>When testing Docker and iptables I stumbled upon something interesting. It appears Docker uses the iptables INPUT chain in an undocumented way. Well that&#8217;s interesting.. Why Docker, Why?</p>



<p>I could find pretty much nothing about this, anywhere.</p>



<p>Wait, let&#8217;s take a step back. What am I talking about?</p>



<p>I was testing iptables with Docker. What a nightmare. Anyways, I noticed something strange with my three node Docker Swarm test VMs. Some very strange rules at the top of the iptables INPUT chain.</p>



<pre class="wp-block-code"><code># iptables -nvL INPUT
Chain INPUT (policy ACCEPT 33477 packets, 8115K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            policy match dir in pol ipsec udp dpt:4789 u32 "0x0>>0x16&amp;0x3c@0xc&amp;0xffffff00=0x100600"
    0     0 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:4789 u32 "0x0>>0x16&amp;0x3c@0xc&amp;0xffffff00=0x100600"
   21  3669 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            policy match dir in pol ipsec udp dpt:4789 u32 "0x0>>0x16&amp;0x3c@0xc&amp;0xffffff00=0x100100"
    0     0 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:4789 u32 "0x0>>0x16&amp;0x3c@0xc&amp;0xffffff00=0x100100"
    1   101 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            policy match dir in pol ipsec udp dpt:4789 u32 "0x0>>0x16&amp;0x3c@0xc&amp;0xffffff00=0x100200"
    0     0 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:4789 u32 "0x0>>0x16&amp;0x3c@0xc&amp;0xffffff00=0x100200"</code></pre>



<p>What is that? Some kind of rules using a Docker port, and something about IPSEC.</p>



<p>Searching the internet has no information about this, that I could find at least. Only some random Docker issues on GitHub. It does have some similar details in the INPUT chain though.</p>



<p>Also found were two references to the Docker documentation.</p>



<p>The Docker documentation does have a pretty good section about <a rel="noreferrer noopener" href="https://docs.docker.com/network/iptables/" target="_blank">iptables</a>. But no mention of the <code>INPUT</code> chain. They very specifically say they only modify the <code>DOCKER-USER</code> and <code>DOCKER</code> chains in iptables.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="907" height="292" src="https://ryandaniels.ca/wp-content/uploads/2020/05/image.png" alt="docker iptables input chain" class="wp-image-2399" srcset="https://ryandaniels.ca/wp-content/uploads/2020/05/image.png 907w, https://ryandaniels.ca/wp-content/uploads/2020/05/image-800x258.png 800w, https://ryandaniels.ca/wp-content/uploads/2020/05/image-300x97.png 300w, https://ryandaniels.ca/wp-content/uploads/2020/05/image-768x247.png 768w" sizes="auto, (max-width: 907px) 100vw, 907px" /><figcaption>Source: <a href="https://docs.docker.com/network/iptables/#add-iptables-policies-before-dockers-rules" target="_blank" rel="noreferrer noopener">Docker documentation for iptables</a></figcaption></figure>



<p>There&#8217;s also some information about the <a rel="noreferrer noopener" href="https://docs.docker.com/network/overlay/#encrypt-traffic-on-an-overlay-network" target="_blank">overlay network in the Docker documentation</a>, but unfortunately it&#8217;s also very high level. No details about the Docker magic that makes all the networking work seamlessly! However, it does mention using IPSEC. Time to do some testing.</p>



<p>When testing in Docker Swarm, by starting containers which use an encrypted overlay I get those iptables rules in the INPUT chain. <s>Fun!</s></p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Not a very exciting mystery. But very unexpected that Docker is modifying the INPUT chain in iptables! That changes a few thing when trying to secure Docker.. Hopefully other people weren&#8217;t relying on having the last rule in the INPUT chain. </p>



<p>Only Docker knows how their next version of Docker will behave. Anything a user does to try and secure Docker at the network layer is futile.</p>



<p>That being said, I&#8217;ve attempted to <a href="https://ryandaniels.ca/blog/secure-docker-with-iptables-firewall-and-ansible/">secure Docker with an iptables firewall</a>. Check it out!</p>
<p>The post <a href="https://ryandaniels.ca/blog/docker-iptables-input-chain/">Docker and the iptables INPUT chain</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2393</post-id>	</item>
		<item>
		<title>Find Version Tag for Latest Docker Image</title>
		<link>https://ryandaniels.ca/blog/find-version-tag-latest-docker-image/</link>
		
		<dc:creator><![CDATA[Ryan Daniels]]></dc:creator>
		<pubDate>Thu, 05 Mar 2020 02:17:00 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Guide]]></category>
		<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://ryandaniels.ca/?p=2196</guid>

					<description><![CDATA[<p>If you've ever used Docker, you've probably used the latest Docker image tag. This is bad. Do not do this! You will be in a situation where you need to find what version you were actually using. This is how you can find the version of that "latest" image you have running.</p>
<p>The post <a href="https://ryandaniels.ca/blog/find-version-tag-latest-docker-image/">Find Version Tag for Latest Docker Image</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>If you&#8217;ve ever used Docker, you&#8217;ve probably used the latest Docker image tag. This is bad. Do not do this! You will be in a situation where you need to find what version you were actually using. This is how you can find the version of that &#8220;latest&#8221; image you have running.</p>



<span id="more-2196"></span>



<p><strong>Every time</strong> you build a new image, use a <strong>new version</strong> for your image tags. <br><strong>Every time</strong> you pull an image, use a specific image tag <strong>version</strong>.</p>



<p>This <a rel="noreferrer noopener" href="https://vsupalov.com/docker-latest-tag/" target="_blank">article</a> describes why latest is bad.</p>



<p>For me, in this theoretical example.. An image I was using introduced breaking changes. This is fine, since they tagged the new releases as version 2. But, only if I was actually using a specific image version and not latest would this be okay. Latest is latest. So the latest that was version 1, now turned into version 2. And everything broke.</p>



<p>Now that you will never use latest again, you could still have a problem. All those docker containers you have running are using images with the :latest tag. How do you find what version latest was?</p>



<h2 class="wp-block-heading" id="i-hope-you-re-lucky-trick-1">I hope you&#8217;re Lucky.. Trick #1</h2>



<p>Check the labels on your image. If you&#8217;re lucky, the developer added a label to the image with the version. I&#8217;m using the utility <code>jq</code> below. It&#8217;s very helpful, be sure to install it.</p>



<pre class="wp-block-code"><code>$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
traefik             latest              96c63a7d3e50        2 months ago        85.7MB

$ IMAGE_ID=96c63a7d3e50

$ docker image inspect --format '{{json .}}' "$IMAGE_ID" | jq -r '. | {Id: .Id, Digest: .Digest, RepoDigests: .RepoDigests, Labels: .Config.Labels}'</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>{
  "Id": "sha256:96c63a7d3e502fcbbd8937a2523368c22d0edd1788b8389af095f64038318834",
  "Digest": null,
  "RepoDigests": &#91;
    "traefik@sha256:5ec34caf19d114f8f0ed76f9bc3dad6ba8cf6d13a1575c4294b59b77709def39"
  ],
  "Labels": {
    "org.opencontainers.image.description": "A modern reverse-proxy",
    "org.opencontainers.image.documentation": "https://docs.traefik.io/",
    "org.opencontainers.image.title": "Traefik",
    "org.opencontainers.image.url": "https://traefik.io/",
    "org.opencontainers.image.vendor": "Containous",
    "org.opencontainers.image.version": "v1.7.20"
  }
}</code></pre>



<p>Bingo! That&#8217;s version 1.7.20 of Traefik!</p>



<h2 class="wp-block-heading" id="lucky-trick-2">Lucky Trick #2</h2>



<p>Pull a bunch of images and hope the Image ID matches. Not much to this trick..</p>



<h2 class="wp-block-heading" id="lucky-trick-3-1">Lucky Trick #3</h2>



<p>If your image is from Docker Hub, they have a new experimental tool you can use called &#8220;<a href="https://github.com/docker/hub-tool" target="_blank" rel="noreferrer noopener">Docker Hub Tool</a>&#8220;.</p>



<p>But what if you are unlucky? What if there&#8217;s a way to check all version tags of an image?</p>



<h2 class="wp-block-heading" id="find-version-tag-for-latest-docker-image">Find Version Tag for Latest Docker image</h2>



<p>There&#8217;s a way to check all version tags on Docker Hub (for example), against the local docker image&#8217;s &#8220;Image ID&#8221;.</p>



<p>You can get every tag from a Docker Registry (like Docker Hub), then use every tag you found, to get the image ID information from the manifest of every image. </p>



<p>Docker Hub has some quirks compared to a proper Docker Registry, and the API isn&#8217;t well documented. But let&#8217;s focus on Docker Hub, since that&#8217;s a huge public image repository.</p>



<p>If you don&#8217;t want to do all of this manually, you can use my script (on GitHub). Here&#8217;s the script in action:</p>



<pre class="wp-block-code"><code># ./docker_image_find_tag.sh -n traefik -i 96c63a7d3e50 -f 1.7. -l 10 -v

Using IMAGE_NAME: traefik
Using REGISTRY: https://index.docker.io/v2
Found Image ID Source: sha256:96c63a7d3e502fcbbd8937a2523368c22d0edd1788b8389af095f64038318834
Found Total Tags: 610
Found Tags (after filtering): 178
Limiting Tags to: 10
Limit reached, consider increasing limit (-l &#91;number]) or use more specific filter (-f &#91;text])

Found Tags:
v1.7.21-alpine
v1.7.21
v1.7.20-alpine
v1.7.20
v1.7.19-alpine
v1.7.19
v1.7.18-alpine
v1.7.18
v1.7.17-alpine
v1.7.17

Checking for image match..
Found match. tag: v1.7.20
Image ID Target: sha256:96c63a7d3e502fcbbd8937a2523368c22d0edd1788b8389af095f64038318834
Image ID Source: sha256:96c63a7d3e502fcbbd8937a2523368c22d0edd1788b8389af095f64038318834</code></pre>



<p>This example is searching for an image named traefik, with an image ID of  96c63a7d3e50 (which we got from running <code>docker images</code>. </p>



<p>The total number of tags for traefik images is 610! You could use this script to check every one, but that will take a minute or two. Instead, you can filter the results a bit. I *think* I&#8217;m running something in version 1.7.x. I&#8217;m also limiting the search to 10 tags in this example to keep the output small.</p>



<p>And the find version tag script found it! The local image ID matches an image ID on the Docker Registry with a tag of 1.7.20!</p>



<p>You can find the script on GitHub: <a rel="noreferrer noopener" href="https://github.com/ryandaniels/docker-script-find-latest-image-tag" target="_blank">https://github.com/ryandaniels/docker-script-find-latest-image-tag</a></p>



<p>Some issues to note:<br>Issue #1: If the image no longer exists on the Docker Registry. You obviously won&#8217;t find it. <br>Issue #2: The script doesn&#8217;t work for Windows images. There are no manifests available for those.</p>



<p>Side note: In this example I&#8217;m using the traefik image. What is <a rel="noreferrer noopener" href="https://containo.us/traefik/" target="_blank">traefik</a>? It&#8217;s &#8220;The Cloud Native Edge Router&#8221; which means it&#8217;s a reverse proxy and load balancer for HTTP and TCP-based applications. It works really well from my experience! Just be careful when upgrading from version 1 to version 2.</p>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p>In conclusion, we ran the find docker image version script to match all image ID&#8217;s on Docker Hub against the local docker image that&#8217;s only tagged as latest. And we found a match, so we know what version we are running!</p>



<p>Now that we have a versioned image tag, we will update our running containers to use that specific version. And never use latest again!</p>



<p>Check out other interesting articles about <a href="https://ryandaniels.ca/blog/category/docker/">Docker</a>.</p>
<p>The post <a href="https://ryandaniels.ca/blog/find-version-tag-latest-docker-image/">Find Version Tag for Latest Docker Image</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2196</post-id>	</item>
		<item>
		<title>Dockerfile ARG FROM ARG trouble with Docker</title>
		<link>https://ryandaniels.ca/blog/docker-dockerfile-arg-from-arg-trouble/</link>
		
		<dc:creator><![CDATA[Ryan Daniels]]></dc:creator>
		<pubDate>Mon, 30 Dec 2019 15:24:00 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[ci/cd]]></category>
		<category><![CDATA[Guide]]></category>
		<guid isPermaLink="false">https://ryandaniels.ca/?p=2017</guid>

					<description><![CDATA[<figure class="alignleft size-thumbnail"><a href="https://ryandaniels.ca/blog/docker-dockerfile-arg-from-arg-trouble"><img src="https://ryandaniels.ca/wp-content/uploads/2019/12/Dockerfile_ARG-300x266.jpg" alt="Dockerfile ARG" class="wp-image-2033"/></a></figure>
<p>Using a dynamic Dockerfile can have great benefits when used in your CI/CD pipeline. You can use the ARG statement in your Dockerfile to pass in a variable at build time. Even use a variable in the FROM statement!<br />
&#160;</p>
<p>The post <a href="https://ryandaniels.ca/blog/docker-dockerfile-arg-from-arg-trouble/">Dockerfile ARG FROM ARG trouble with Docker</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="564" height="500" src="https://ryandaniels.ca/wp-content/uploads/2019/12/Dockerfile_ARG.jpg" alt="Dockerfile ARG" class="wp-image-2033" srcset="https://ryandaniels.ca/wp-content/uploads/2019/12/Dockerfile_ARG.jpg 564w, https://ryandaniels.ca/wp-content/uploads/2019/12/Dockerfile_ARG-300x266.jpg 300w" sizes="auto, (max-width: 564px) 100vw, 564px" /></figure></div>



<p>Using a dynamic <a rel="noreferrer noopener" aria-label="Dockerfile (opens in a new tab)" href="https://docs.docker.com/engine/reference/builder/" target="_blank">Dockerfile</a> can have great benefits when used in your CI/CD pipeline. You can use the <code>ARG</code> statement in your <code>Dockerfile</code> to pass in a variable at build time. Even use a variable in the <code>FROM</code> statement! Dockerfile ARG FROM ARG. That will make more sense later.</p>



<span id="more-2017"></span>



<p>This post is about <a rel="noreferrer noopener" aria-label="Container virtualization technology (opens in a new tab)" href="https://rancher.com/learning-paths/what-are-containers/" target="_blank">Container virtualization technology</a>, and problems when building a container image using <a rel="noreferrer noopener" aria-label="Docker Engine (opens in a new tab)" href="https://www.docker.com/products/container-runtime" target="_blank">Docker Engine</a>. <br>For some background on a dynamic Dockerfile, check out <a rel="noreferrer noopener" aria-label="Jeff Geerling's post (opens in a new tab)" href="https://www.jeffgeerling.com/blog/2017/use-arg-dockerfile-dynamic-image-specification" target="_blank">Jeff Geerling&#8217;s post</a>.</p>



<p>One problem I&#8217;ve come across with this, is the <code>Dockerfile</code> <code>ARG</code> variable usage isn&#8217;t intuitive. It is actually kind of weird. And I&#8217;m not alone on this! There&#8217;s lots of feedback in <a rel="noreferrer noopener" aria-label="GitHub Issue #34129 (opens in a new tab)" href="https://github.com/moby/moby/issues/34129" target="_blank">GitHub Issue #34129</a>. </p>



<h2 class="wp-block-heading">What&#8217;s the Problem?</h2>



<p>A variable declared at the top of the <code>Dockerfile</code> using <code>ARG</code> doesn&#8217;t work if you try to use it after the <code>FROM</code> statement. Not by default at least. It only works if used in the <code>FROM</code> statement. </p>



<pre class="wp-block-code"><code>ARG TAG=latest
FROM myimage:$TAG # &lt;------ This works!
LABEL BASE_IMAGE="myimage:$TAG" # &lt;------ Does not work!</code></pre>



<p>The <code>FROM</code> above will use the <code>TAG</code> passed in from a build-arg, or default to use the string &#8220;latest&#8221;.<br>But the <code>LABEL</code> won&#8217;t work. The <code>TAG</code> portion will be empty!</p>



<p>This was tested with the most recent version of Docker Engine (version: 19.03.5).</p>



<h2 class="wp-block-heading">How Does it Work?</h2>



<p>To use a variable in the <code>FROM</code> section, you need to put the <code>ARG</code> before the <code>FROM</code>. So far so good. But, now let&#8217;s use that variable again after the <code>FROM</code>? No you can&#8217;t. It&#8217;s not there. Unless you use another <code>ARG</code> statement after the <code>FROM</code>.<br>Wait, it gets more weird. Any <code>ARG</code> before the FIRST <code>FROM</code>, can be used in any <code>FROM</code> (for multi-stage builds). In fact the <code>ARG</code> must be above the first <code>FROM</code> even if you only want to use it in a later <code>FROM</code>. Wow, that&#8217;s a lot of <code>ARG</code>s and <code>FROM</code>s. The <code>ARG</code> is kind of &#8220;global&#8221; but only if you declare it again inside the build stage.</p>



<h2 class="wp-block-heading">Dockerfile ARG FROM ARG</h2>



<p>Let&#8217;s look at an example to make it easier. Below is not a useful Dockerfile. It&#8217;s only an example to illustrate this.</p>



<pre class="wp-block-code"><code>#These ARGs are used in the FROM statements
#Or as global variables if declared again (without the default value)
ARG BUILDER_TAG=latest
ARG BASE_TAG=latest
ARG APP="app.go"

#First build stage
FROM mybuildapp:$BUILDER_TAG AS builder
ARG APP
RUN compile_my_app $APP

#Second build stage
FROM registry.access.redhat.com/ubi8/ubi-minimal:$BASE_TAG
ARG BASE_TAG
ARG APP
LABEL BASE_IMAGE="registry.access.redhat.com/ubi8/ubi-minimal:$BASE_TAG"
COPY --from=builder $APP .</code></pre>



<p>Now, let&#8217;s run the example build:</p>



<pre class="wp-block-code"><code>docker build . \
  --pull
  --build-arg BUILDER_TAG="2.0" \
  --build-arg BASE_TAG="8.1" \
  --build-arg APP="the_best_app.go" \
  -t image_tag</code></pre>



<p>Once the Docker build finishes, this is the result of your command line argument usage inside the docker image:</p>



<pre class="wp-block-code"><code>BUILDER_TAG="2.0"
BASE_TAG="8.1"
APP="the_best_app.go"</code></pre>



<h2 class="wp-block-heading">Results</h2>



<p>The <code>BUILDER_TAG</code> and <code>BASE_TAG</code> will be used in the <code>FROM</code> statements. <code>BASE_TAG</code> is also used inside the second build stage. And <code>APP</code> will be used in both build stages. <code>ARG APP="app.go"</code> only needs to be declared if you want to set a default value. In my example we don&#8217;t really need that since we are passing in a build argument.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Remember that <code>ARG</code>s at the top of the <code>Dockerfile</code> can be used in the <code>FROM</code> statements. They are &#8220;global&#8221; only if you declare the <code>ARG</code> again in each build stage (after <code>FROM</code> (and before the next <code>FROM</code> if using a multi-stage build)).<br>You can remember it this way: <code>Dockerfile</code> <code>ARG</code> <code>FROM</code> <code>ARG</code><br>But, after wasting an hour on this, it was more like: Dockerfile <em>Arrggh!</em> FROM <em>Arrggh!</em></p>



<p>You can use something like this in your CI/CD, for automatic docker image building. </p>



<p><strong>Side note</strong>: Building an image using <a rel="noreferrer noopener" aria-label="Podman (opens in a new tab)" href="https://podman.io/" target="_blank">Podman</a> does not have the same issue as Docker Engine (tested with Podman version 1.4.4, 1.6.3, and 1.7.0). You do not need to specify the <code>ARG</code> again inside the build stage when Podman creates the container image!</p>



<p>Further reading:<br><a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact" target="_blank">Dockerfile Reference: Understand how ARG and FROM interact</a><br><a rel="noreferrer noopener" aria-label="Dockerfile Reference: Scope (opens in a new tab)" href="https://docs.docker.com/engine/reference/builder/#scope" target="_blank">Dockerfile Reference: Scope</a><br><a href="https://docs.docker.com/develop/develop-images/multistage-build" target="_blank" rel="noreferrer noopener" aria-label="Multi-Stage Builds (opens in a new tab)">Multi-Stage Builds</a></p>
<p>The post <a href="https://ryandaniels.ca/blog/docker-dockerfile-arg-from-arg-trouble/">Dockerfile ARG FROM ARG trouble with Docker</a> appeared first on <a href="https://ryandaniels.ca/">Ryan Daniels</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2017</post-id>	</item>
	</channel>
</rss>
