I recently started using a firewall on all my computers. Prior to this I relied on my hosts being behind NATs and so not needing protection from the internet. However I was uncomfortable with trusting all the devices on my local network. I now run firewalls on all my hosts to block everything except what I whitelist.
One reason it took me so long to do this was my dislike of
years ago I was very into tweaking firewalls (so much so that there's a
running joke with my friends about "ultra-[me]-paranoid" firewalls). I
became enamoured of OpenBSD partly for this reason. Its firewall software,
pf, was (and is) so far beyond
iptables in ease of use that in general
I refused to touch
iptables because of the contrast. However these days I
mainly run Linux (Debian, Raspbian, and Ubuntu), so I decided to get over
Alternatives to restrictive firewalls
An alternative solution I was considering was to have hosts I was particularly concerned about restricted to their own local networks. (There are some hosts I'm more concerned about than others, such as those that run internet exposed services). However, barring running multiple routers I was not confident I could isolate the hosts sufficiently.
My goal was to block all inbound traffic and only allow access to particular ports from particular hosts. To do this:
- I went through each host and listed out what ports needed to be open to which IPs.
- I set up static DHCP leases for all my hosts/devices. This way I can, for example, give access to my phone but nothing else.
Once I knew what I wanted to allow, I needed to figure out how to write and load the rules. In particular:
- How do I load the rules at boot?
- How do I manage the rules from a config file?
I solved both of these by using the
package. When you install it, it offers to save your current rules to
/etc/iptables/rules.v6. At boot it loads the
rules from these files.
When I change the rules, I run
iptables-restore < /etc/iptables/rules.v4
to reload them.
This works well and is not too dissimilar to using
For my desktop, the files look like:
*filter :INPUT DROP [1:32] :FORWARD DROP [0:0] :OUTPUT ACCEPT [898:55500] -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -s 192.168.1.2/32 -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p icmp -j ACCEPT #-A INPUT -j LOG --log-prefix "Blocked packet: " --log-tcp-options COMMIT
*filter :INPUT DROP [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT
- I allow traffic on
- I allow outbound traffic
- I allow SSH from one host
- I allow ICMP (blocking that is silly as it's useful for diagnostics)
- I block IPv6 (I don't use it currently, so just in case)
The line I commented out is useful for debugging. If I enable it,
iptables logs all blocked packets to syslog.
The lines immediately following
*filter show the policies. The stuff
 is not important (packet counts I believe).
I have similar configs on all hosts, but they vary on which ports they
allow. I set up the configs manually on each host. My final step was to
ensure backups for each host includes
In addition to the static rules for each host, I also manage some ports dynamically.
I use iptables-manage to
automatically allow any IP connecting to my IRC network to connect to HTTP
and SSH (on one host). To do this, a bot updates a list of IPs that
iptables-manage monitors. (I discussed this in prior
I have one host acting as a jump host for SSH to the others. Hosts only allow SSH from the jump host. The jump host has a dynamic IP, so I use dnsrule to allow IPs associated with a particular DNS record. When the IP changes, hosts allow it once I update the DNS record to reflect the change.
This means I don't have to open any service to the internet except IRC, but I can still provide access to HTTP and SSH. It's a form of port knocking, so it's security through obscurity, but it's better than allowing the internet. I'm reasonably okay with allowing IRC as I wrote the server I use.
I'm fairly happy with this setup. It was simpler to get going than I
imagined. I should not have let my distaste for
iptables keep me from
doing this for so long. After doing this I appreciate
iptables more, but
I still think
pf is far superior.