Isawan Millicanisawan.net/2019-05-17T11:00:00+01:00Atomic iptables scripts with network namespaces2019-05-17T11:00:00+01:002019-05-17T11:00:00+01:00Isawan Millicantag:None,2019-05-17:isawan.net/posts/atomic-iptables-script<p class="first last">Using network namespaces for atomic iptables firewall changes</p>
<p>An issue faced by shell script based firewalls calling iptables is that
it can take several minutes to rebuild the firewall, during this time the
firewall may drop/accept packets incorrectly.
To get around this issue, the <tt class="docutils literal"><span class="pre">iptables-restore</span></tt> command can be used which
provides <a class="reference external" href="https://wiki.nftables.org/wiki-nftables/index.php/Atomic_rule_replacement">atomic</a> firewall rule changes.
The sysadmin then simply writes/generates the firewall rules in the
iptables-restore format and uses iptables-restore to apply the updates.
Alternatively, <a class="reference external" href="https://wiki.nftables.org/wiki-nftables/index.php/Main_Page">nftables</a> can be used which provides atomic transactions
built-in.
However, significant work may need to be done to convert existing firewall
scripts.
In this article, I'll show a quick way of making the execution of
iptables-based firewall scripts an atomic operation by using a network
namespace as a staging area.</p>
<p>Here is an example of the usage.</p>
<pre class="code literal-block">
isawan@icebox:~/test/iptables-atomic
$ cat big_firewall_script
#!/bin/sh
iptables -N TEST
for i in `seq 5000`;
do
iptables -A TEST --dest 0.0.0.0
done
isawan@icebox:~/test/iptables-atomic
$ sudo ./iptables-atom ./big_firewall_script
</pre>
<p>By wrapping the <tt class="docutils literal">big_firewall_script</tt> with <tt class="docutils literal"><span class="pre">./iptables-atom</span></tt>, the firewall
firewall update is now atomic without any modifications to the firewall
script.</p>
<p>Here is the source for iptables-atom. <a class="footnote-reference" href="#cleanup" id="id1">[1]</a></p>
<pre class="code literal-block">
isawan@icebox:~/test/iptables-atomic
$ cat iptables-atom
#!/bin/sh
ip netns add staging
ip netns exec staging $@
ip netns exec staging iptables-save >/tmp/iptables_dump
iptables-restore /tmp/iptables_dump
ip netns exec staging ip6tables-save >/tmp/ip6tables_dump
ip6tables-restore /tmp/ip6tables_dump
ip netns delete staging
</pre>
<p>A network namespace is created; a network namespace
allows the creation of network objects such as chains and rules in an
isolated environment from the rest of the system.
This provides a clean environment for the creation of firewall rules.
The firewall script is then executed inside the network namespace which
produces all the network objects inside the namespace.
The IPv4 and IPv6 tables are then dumped to a file in a format that can
be read by iptables-restore.
Rules for each IPv4 and IPv6 tables are then each restored atomically
to the system's firewall.</p>
<p>Current limitations with this technique is the interaction with ipsets.
To the best of my understanding, there is no way of atomically reloading
ipsets in this way.
The closest thing that ipsets provides is the <tt class="docutils literal">ipset swap</tt> command which is
atomic but need to be applied to each set individually.</p>
<p>We can extend this approach by running the staging part on a separate,
more powerful machine as part of your CI/CD pipeline; obviously ensuring that
the same kernel and iptables userspace version is used.
In addition, this technique should also work for arptables and ebtables,
although you may need to add additional save-restore pairs.</p>
<table class="docutils footnote" frame="void" id="cleanup" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td>Just a note, you probably shouldn't use this directly.
Consider error handling around failure cases to ensure the namespace is
always deleted on completion.
I have avoided this here to keep the core logic nice and clean.</td></tr>
</tbody>
</table>
Enable AppArmor with Debian on Linode2018-03-31T19:00:00+01:002018-03-31T19:00:00+01:00Isawan Millicantag:None,2018-03-31:isawan.net/posts/apparmor-debian-linode<p class="first last">Instructions on how to enable AppArmor when working with Debian on a Linode VPS.</p>
<p>I ran into a small problem changing the boot parameters get AppArmor working on Debian with Linode. I am writing this in case anyone runs into same issue.</p>
<p>The command-line instructions given by the <a class="reference external" href="https://wiki.debian.org/AppArmor/HowToUse">debian</a> wiki shows the following kernel parameters need to be set using grub:</p>
<pre class="code literal-block">
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=1 security=apparmor"
</pre>
<p>Following a reboot, a read of <tt class="docutils literal">/proc/cmdline</tt> revealed that the kernel parameters were not used.
This is because Linode defaults to using their own kernel, you will need to explicitly set GRUB 2 to boot in the Linode Manager.</p>
<p>Login to the Linode Manager, click the node relevant node and locate the
'Configuration Profile'. Change the kernel to 'GRUB 2'.</p>
<p>Reboot and see what happens. Good luck!</p>
First!2017-11-27T23:00:00+00:002017-11-27T23:00:00+00:00Isawan Millicantag:None,2017-11-27:isawan.net/posts/first<p class="first last">First!</p>
<p>First!</p>
<p>I'm still setting up the website. In the mean time, for work related things you can contact me at:</p>
<p><a class="reference external" href="mailto:work@isawan.net">work@isawan.net</a></p>