iptables
is a powerful and precise firewall; this document is to show how to configure iptables
to conform to a default-deny access strategy: nothing goes through until expressly permitted. This is a substantially simplified version of iptables-hardened; it will do exactly the job it needs to to keep your host secure, but will lack certain features that may be of marginal interest to the sysadmin, such as logging and complex chains.
Depending on the circumstances, it may not always be possible to be at a keyboard and monitor of the host you are administering. It is key to never apply a rule you’re not able to roll-back due to disconnecting yourself. This is what makes default-deny so especially fortuitous! Once set up properly, your only task is opening the minimum ports available to get a service appropriate connectivity–you shouldn’t have to spend time chasing holes in the firewall.
On the assumption one must set up a server strictly remotely, and relying on SSH as the exclusive out-of-band management, we need to defensively write rules–we need to write rules to transition to default-deny without getting disconnected. In the following segment, be sure to modify the subnet to your own network.
# cat << EOF > ~/iptables
*filter
:INPUT DROP
:FORWARD DROP
:OUTPUT DROP
-A INPUT -p tcp -m tcp -s 10.137.0.0/24 --dport 22 -j ACCEPT
-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT
EOF
This rule means nothing goes through except inbound port 22 from a friendly subnet, and any related traffic to sustain this connection.
If available, check if your distro offers iptables-apply. This tool allows you to apply a new rulesfile which will undo itself if the rules prove to break connectivity.
# iptables-apply -t 30 ~/iptables
The onscreen rules will then guide you to start a new ssh
session within 30 seconds, because only a new session will definitively prove the rules file is working as expected. If your new session connects, continue with iptables-apply
on the original terminal. Confirming you can create the new session will commit the new rules rules to memory.
In the absence of iptables-apply
, you can redirect your rules to iptables-restore
. If you have to rely on this method, create a backup of your current-working rules:
# iptables-save > ~/iptables.safe
# iptables-restore < ~/iptables; sleep 30; iptables-restore < ~/iptables.safe
Following the same philosophy, once you execute the compound statement below–if the rules break your connectivity–there will be a 30 second delay and then a revert to the original rules. If connectivity remains, you can CTRL-C
to break out of the sleep, avoiding the iptables.safe restoration.
# iptables -vnL
Chain INPUT (policy DROP 31 packets, 1848 bytes)
pkts bytes target prot opt in out source destination
678 42849 ACCEPT tcp -- * * 10.137.0.0/24 0.0.0.0/0 tcp dpt:22
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy DROP 3 packets, 214 bytes)
pkts bytes target prot opt in out source destination
400 46066 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
# iptables-save > ~/iptables.safe
Finally, save your new rules with the iptables-save > ~/iptables.safe
command.
Many activies a server will perform will likely require DNS lookups. DNS operates on outbound UDP port 53.
# ping minecraft.codeemo.com
ping: minecraft.codeemo.com: Temporary failure in name resolution
# iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
# iptables -I INPUT 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# ping minecraft.codeemo.com
PING minecraft.codeemo.com (167.71.248.91) 56(84) bytes of data.
[snip]
Having DNS figured out means other common utilities for downloading applications will now be possible, easily.
If you need to download an online file, it’s easy to get files via HTTP
and HTTPS
# iptables -A OUTPUT -p tcp -m tcp --dport 80 -m comment --comment "allow outbound http" -j ACCEPT
# iptables -A OUTPUT -p tcp -m tcp --dport 443 -m comment --comment "allow outbound https" -j ACCEPT
Let’s let ICMP
through. For now, friendly-inbound only, and any outgoing.
# iptables -A INPUT -p icmp -j ACCEPT
# iptables -A OUTPUT -p icmp -j ACCEPT
# iptables -A OUTPUT -o lo -m comment --comment "Permit loopback traffic" -j ACCEPT
Let’s look at the current rules so far. We use the parameters “-vnL” which gives us [v]erbose, [n]umeric ports, [L]ist rules. This also gives us packet/byte counters.
# iptables -vnL
Chain INPUT (policy DROP 2 packets, 104 bytes)
pkts bytes target prot opt in out source destination
110 7468 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
3239 206K ACCEPT tcp -- * * 10.137.0.0/24 0.0.0.0/0 tcp dpt:22
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1886 196K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 /* allow outbound http */
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 /* allow outbound https */
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 /* Permit loopback traffic */
# watch -n .5 iptables -vnL
You can open a new terminal session that provides a real-time view of packets hitting your server. If you are trying to let a new service though, you’ll see the packet show up first on the INPUT
chain. Follow where the numbers increment to see where the packet ends up–trace the packet flow to the expected rule (and not incrementing DROP).
If you want to remove a rule, do so with --line-numbers
:
# iptables --line-numbers --list
Chain INPUT (policy DROP)
num target prot opt source destination
1 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
2 ACCEPT tcp -- 10.137.0.0/24 anywhere tcp dpt:ssh
3 ACCEPT icmp -- anywhere anywhere
Chain FORWARD (policy DROP)
num target prot opt source destination
Chain OUTPUT (policy DROP)
num target prot opt source destination
1 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
2 ACCEPT udp -- anywhere anywhere udp dpt:domain
3 ACCEPT tcp -- anywhere anywhere tcp dpt:http /* allow outbound http */
4 ACCEPT tcp -- anywhere anywhere tcp dpt:https /* allow outbound https */
5 ACCEPT icmp -- anywhere anywhere
6 ACCEPT all -- anywhere anywhere /* Permit loopback traffic */
You can delete a rule with the syntax: iptables -D [POLICY] [RULENO]
, for example iptables -D OUTPUT 5
to remove the ICMP rule.
Writing a rule to allow inbound arbitrary port traffic is simple; the reverse traffic is already handled with the OUTPUT
rule RELATED/ESTABLISHED
.
# iptables -A INPUT -p tcp -m tcp --dport 8443 -m comment --comment "mineos webui" -j ACCEPT
Let’s find some packets that just don’t make sense to ever honor, and drop them immediately.
# iptables -I INPUT 2 -m conntrack --ctstate INVALID -j DROP
# iptables -I INPUT 2 -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -m comment --comment "[malicious packet patterns]" -j DROP
# iptables -I INPUT 2 -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -m comment --comment "[malicious packet patterns]" -j DROP
# iptables-save > ~/iptables.v4
# iptables-restore < ~/iptables.v4
Different distributions apply iptables in different ways, some use iptables-persistent
, some put iptables-restore
in /etc/rc.local
, some expect the rules at /etc/sysconfig/iptables
. Check your distribution manual for further details.
iptables
provides an immense amount of control of packet flow. Creating good rules from the outset will lower the effort required to maintain a secured system. Be safe!