nobbit
Posts: 121
Joined: Sun Sep 25, 2011 5:51 pm

Setting up a simple firewall, turned on/off with systemctl

Sat Nov 17, 2018 6:09 pm

My laziness bit back earlier today when I tried to update raspbian. I had set up a very simple firewall that was started by an init script. Apparently that init script didn't act as systemd expected it too; for some reason apt-get dist-upgrade couldn't resolve dependencies because it ended up in a weird loop over this script.

I didn't bother investigating. It was time to do a more proper setup anyway, so I just erased the old one. Now, in case someone here wants the same thing from their firewall as I do I thought I'd share my new setup.

First of all: I just need port 922 to be open to tcp traffic (ssh). Other than that I want to allow ICMP traffic (network discovery, pings and similar), and all traffic that was initiated from the Pi. And, crucially, I want my firewall to start at boot and I want to be able to turn it off or on via systemctl.

For this we need to create three files, and we need to be root while doing it.

Start by creating a new directory for the firewall settings:

Code: Select all

mkdir /etc/simplefw
Now add a file with our rules (/etc/simplefw/enable), expressed as iptables commands:

Code: Select all

## Accepted INPUT connections ##

# Related and established traffic, regardless of interface
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# ssh connections
iptables -A INPUT -p tcp --dport 922 -j ACCEPT

# pings
iptables -A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT

# All traffic on the loopback interface
iptables -A INPUT -i lo -j ACCEPT

## Policy settings ##

iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -P INPUT DROP
Note that I put all the rules before the policies. These rules will start applying as the commands are run. If we put the INPUT policy to DROP without having set any exceptions for connections to accept, any open ssh tunnels will close when that command runs.

Also note the "922" - that's my one open port. If you have a web server you may want to add similar lines for port 80 (http) and 443 (https). Note that I haven't specified which network interface this applies to; therefore it applies to all.

Now create a file with commands that negate these (/etc/simplefw/disable), in an order that doesn't jeopardize open connections:

Code: Select all

iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

iptables -F
Here we set the default policy to ACCEPT, before dropping all rules one by one in the order they were entered.

Now let's make this a systemd service!

Create the file

Code: Select all

/etc/systemd/system/simplefw.service
with the following content:

Code: Select all

[Unit]
Description=SimpleFirewall

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/bash /etc/simplefw/enable
ExecStop=/bin/bash /etc/simplefw/disable

[Install]
WantedBy=multi-user.target
Enable your firewall to make it start at boot, then start it:

Code: Select all

systemctl enable simplefw
systemctl start simplefw
And that's it. If you run "systemctl stop simplefw" the commands in /etc/simplefw/disable will be run, cleaning out your rules.

If you want to do more complicated things I recommend that you read up on iptables. There are many firewall solutions out there, but I've found that they're more or less all hopelessly difficult to grasp if you don't understand the concepts of iptables (most of them are - just like this one - some sort of wrapper around iptables anyway).

mfa298
Posts: 1387
Joined: Tue Apr 22, 2014 11:18 am

Re: Setting up a simple firewall, turned on/off with systemctl

Sun Nov 18, 2018 5:12 pm

nobbit wrote:
Sat Nov 17, 2018 6:09 pm
My laziness bit back earlier today when I tried to update raspbian. I had set up a very simple firewall that was started by an init script. Apparently that init script didn't act as systemd expected it too; for some reason apt-get dist-upgrade couldn't resolve dependencies because it ended up in a weird loop over this script.
You might be interested to know there's a service in Debian/Ubuntu that does this all for you (I've been using it almost as long as the Pi). CentOS/RHEL/Fedora (which I've been using for much longer) have a similar service as well as newer variations.

Code: Select all

sudo apt install iptables-persistent
This uses a file (/etc/iptables/rules.v4) to define the rules in a form iptables-restore can use (you can create it with iptables-save or by hand crafting it)

Based on your setup script I think your rules.v4 would look a bit like:

Code: Select all

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp --dport 922 -j ACCEPT
-A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -i lo -j ACCEPT
COMMIT
For your default flush all rules on stop setup you might need to change the setting in /etc/default/netfilter-persistent

Return to “General discussion”