Firewall Setup
The default Armbian OS does not ship with a firewall. So we need to set up our own firewall. For Ubuntu there are multiple options like UFW and iptables.
But iptables are more preferred because they are much more compatible with the docker.
Step 1: Install iptables
- Start by updating your package repositories to ensure you get the latest version of iptables:
sudo apt update
- Now, you can install iptables and iptables-persistent.Install the iptables-persistent package to ensure that your rules are automatically loaded at boot
sudo apt install iptables
sudo apt install iptables-persistent
- Verify the Installation. This command will display the installed version of iptables, confirming that it’s ready for use.
sudo iptables --version
Step 2: Setting up firewall rules
One approach is that you start by blocking everything on your IPV 4 and IPV 6 tables. These file are all rules can be added to the separate files, namely rules.V4 and rules.V6.
sudo nano /etc/iptables/rules.v4
An example rules.v4 files will look like this
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
-A INPUT -i tailscale0 -j ACCEPT
-A INPUT -s 123.123.123.123 -p tcp -m tcp --dport 9090 -j ACCEPT
-A INPUT -s 123.123.123.123 -p tcp -m tcp --dport 8888 -j ACCEPT
-A INPUT -s 195.165.165.0/24 -p tcp --dport 9090 -j ACCEPT
-A INPUT -s 195.165.165.0/24 -p tcp --dport 8888 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT
Default Policies
- INPUT:
DROP→ All incoming traffic is blocked unless explicitly allowed. - FORWARD:
DROP→ No packet forwarding between interfaces. - OUTPUT:
ACCEPT→ All outgoing traffic is allowed.
Allowed Incoming Traffic
| Rule | Protocol | Port(s) | Source | Description |
|---|---|---|---|---|
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT | Any | Any | Established connections | Allows replies to outgoing connections. |
-A INPUT -i lo -j ACCEPT | Any | Any | Localhost | Permits loopback traffic. |
-A INPUT -p icmp -j ACCEPT | ICMP | N/A | Any | Allows ping and ICMP messages. |
-A INPUT -p tcp --dport 22 -j ACCEPT | TCP | 22 | Any | SSH access. (you can also allow specific IP for better protection see example below) |
-A INPUT -p tcp --dport 80 -j ACCEPT | TCP | 80 | Any | HTTP web traffic. |
-A INPUT -p tcp --dport 443 -j ACCEPT | TCP | 443 | Any | HTTPS web traffic. |
-A INPUT -i tailscale0 -j ACCEPT | Any | Any | tailscale0 interface | Allows traffic from Tailscale VPN. |
-A INPUT -s 123.123.123.123 -p tcp --dport 9090 -j ACCEPT | TCP | 9090 | 100.100.1.1 | Allows custom service from specific IP (tailscale). |
-A INPUT -s 123.123.123.123 -p tcp --dport 8888 -j ACCEPT | TCP | 8888 | 100.100.1.1 | Allows custom service from specific IP (tailscale). |
-A INPUT -s 195.165.165.0/24 -p tcp --dport 9090 -j ACCEPT | TCP | 9090 | Local LAN (195.165.165.0/24) | Allows service access from LAN. |
-A INPUT -s 195.165.165.0/24 -p tcp --dport 8888 -j ACCEPT | TCP | 8888 | Local LAN (195.165.165.0/24) | Allows service access from LAN. |
Rejected Traffic
-A INPUT -j REJECT --reject-with icmp-host-prohibited
→ Any traffic not matching the above rules is rejected with an ICMP "host prohibited" message.
Summary
- Secure default: Blocks all inbound traffic unless explicitly allowed.
- Access granted: SSH (22), HTTP (80), HTTPS (443), ICMP, Tailscale, and specific services (9090, 8888) from trusted sources.
- LAN & specific IPs: Only trusted networks/IPs can access sensitive ports.
- Output unrestricted: Outbound traffic is fully allowed.
IPV6 Firewall Rules
Internally running service mostly use IPV4 like (localhost / 127.0.0.1) so for this reason we dont need to cofigure IPV6 rules for them. So we only apply the rules for outbount / web facing ports and keep everything else blocked.
An example rules.v6 files will look like this
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Accept established/related connections
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# Accept loopback
-A INPUT -i lo -j ACCEPT
# Accept ICMPv6
-A INPUT -p icmpv6 -j ACCEPT
# Allow SSH
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
# Allow HTTP
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
# Allow HTTPS
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
# Default reject
-A INPUT -j REJECT --reject-with icmp6-port-unreachable
COMMIT
Step 3: Enabling the rules
To implement this rule, we can run the following command.
sudo su -c 'iptables-restore < /etc/iptables/rules.v4'
sudo su -c 'iptables-restore < /etc/iptables/rules.v6'