Stateful firewalling allows for a quick and secure way to restrict access to incoming network connections on a GNU/Linux machine with iptables.
It works by only reviewing the defined policy on the initial connection handshake, then relates subsequent packets to that initial connection. This will allow the firewall to be very efficient, only looking up rules for initial packets.
There are 4 states which iptables can use: New, Established, Related, and Invalid.
New connections are TCP SYN handshakes that have no prior association.
Established are those packets which have already been sent through New.
Related state are for possible New connections, which have been created from an already existing connection (such as ftp in non-passive mode).
The Invalid state is used for anything else.Using these rules, we can concentrate all our resources into only filtering New and Invalid connections, as anything Established or Related can likely be trusted fully.
The following script will allow only new connections on the $EXT interface from an allowed list of ports, both TCP and UDP.
It also has commands to setup port-forwarding and masquerade support. It could be used as an all-in-one GNU/Linux router/firewall.
#!/bin/bash
PATH=/sbin
LAN=172.16.254.0/24 # internal subnet
EXT=eth1 # external interface
INT=eth0 # internal interface
ALLOWUDP=1194,4569,5000 # comma separated lists
ALLOWTCP=22,25,5060 # same as above
FORWARD=("172.16.254.253:33891" "172.16.254.253:3389" "172.16.254.245:33894") # port forward rules, must follow this example syntax
start() {
# masquerade
iptables -t nat -A POSTROUTING -s 172.16.254.0/24 -j MASQUERADE
# port forwards
for i in ${FORWARD[@]}
do
iptables -A INPUT -p tcp --dport ${i#*:} -j ACCEPT
iptables -A INPUT -p udp --dport ${i#*:} -j ACCEPT
iptables -t nat -A PREROUTING -s ! $LAN -p tcp --dport ${i#*:} -j DNAT --to-destination ${i}
iptables -t nat -A PREROUTING -s ! $LAN -p udp --dport ${i#*:} -j DNAT --to-destination ${i}
done
# prevent more than 1 ssh attempt per minute per host
iptables -N ssh
iptables -A ssh -m recent --update --name ssh --seconds 60 --rttl -j DROP
iptables -A ssh -m hashlimit --hashlimit-name ssh --hashlimit-mode srcip --hashlimit-burst 10 --hashlimit 1/minute -j ACCEPT
iptables -A ssh -m recent --set --name ssh -j DROP
iptables -A INPUT -i $EXT -s ! $LAN -p tcp --dport 22 -m state --state NEW -j ssh
iptables -A INPUT -i $EXT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i $EXT -m state --state INVALID -j LOG --log-prefix "INVALID: " --log-level warning
iptables -A INPUT -i $EXT -p icmp --icmp-type echo-request -m limit --limit 1/minute -j LOG --log-prefix "PING: " --log-level notice
iptables -A INPUT -i $EXT -p icmp --icmp-type echo-request -m limit --limit 2/second -j ACCEPT
iptables -A INPUT -i $EXT -p udp -m multiport --dports $ALLOWUDP -m state --state NEW -j ACCEPT
iptables -A INPUT -i $EXT -p tcp -m multiport --dports $ALLOWTCP -m state --state NEW -j ACCEPT
iptables -A INPUT -i $EXT -p tcp -j REJECT --reject-with tcp-reset
iptables -A INPUT -i $EXT -j REJECT --reject-with icmp-port-unreachable
iptables -A INPUT -i $EXT -j DROP
echo iptables started
return 0
}
stop() {
iptables -F
iptables -t nat -F
iptables -X ssh
echo iptables stopped
return 0
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "usage: $0 [start|stop|restart]"
exit 1
;;
esac
exit 0
Stateful Firewalls using iptables in Linux
It works by only reviewing the defined policy on the initial connection handshake, then relates subsequent packets to that initial connection. This will allow the firewall to be very efficient, only looking up rules for initial packets.
There are 4 states which iptables can use: New, Established, Related, and Invalid.
New connections are TCP SYN handshakes that have no prior association.
Established are those packets which have already been sent through New.
Related state are for possible New connections, which have been created from an already existing connection (such as ftp in non-passive mode).
The Invalid state is used for anything else.Using these rules, we can concentrate all our resources into only filtering New and Invalid connections, as anything Established or Related can likely be trusted fully.
The following script will allow only new connections on the $EXT interface from an allowed list of ports, both TCP and UDP.
It also has commands to setup port-forwarding and masquerade support. It could be used as an all-in-one GNU/Linux router/firewall.
#!/bin/bash PATH=/sbin LAN=172.16.254.0/24 # internal subnet EXT=eth1 # external interface INT=eth0 # internal interface ALLOWUDP=1194,4569,5000 # comma separated lists ALLOWTCP=22,25,5060 # same as above FORWARD=("172.16.254.253:33891" "172.16.254.253:3389" "172.16.254.245:33894") # port forward rules, must follow this example syntax start() { # masquerade iptables -t nat -A POSTROUTING -s 172.16.254.0/24 -j MASQUERADE # port forwards for i in ${FORWARD[@]} do iptables -A INPUT -p tcp --dport ${i#*:} -j ACCEPT iptables -A INPUT -p udp --dport ${i#*:} -j ACCEPT iptables -t nat -A PREROUTING -s ! $LAN -p tcp --dport ${i#*:} -j DNAT --to-destination ${i} iptables -t nat -A PREROUTING -s ! $LAN -p udp --dport ${i#*:} -j DNAT --to-destination ${i} done # prevent more than 1 ssh attempt per minute per host iptables -N ssh iptables -A ssh -m recent --update --name ssh --seconds 60 --rttl -j DROP iptables -A ssh -m hashlimit --hashlimit-name ssh --hashlimit-mode srcip --hashlimit-burst 10 --hashlimit 1/minute -j ACCEPT iptables -A ssh -m recent --set --name ssh -j DROP iptables -A INPUT -i $EXT -s ! $LAN -p tcp --dport 22 -m state --state NEW -j ssh iptables -A INPUT -i $EXT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -i $EXT -m state --state INVALID -j LOG --log-prefix "INVALID: " --log-level warning iptables -A INPUT -i $EXT -p icmp --icmp-type echo-request -m limit --limit 1/minute -j LOG --log-prefix "PING: " --log-level notice iptables -A INPUT -i $EXT -p icmp --icmp-type echo-request -m limit --limit 2/second -j ACCEPT iptables -A INPUT -i $EXT -p udp -m multiport --dports $ALLOWUDP -m state --state NEW -j ACCEPT iptables -A INPUT -i $EXT -p tcp -m multiport --dports $ALLOWTCP -m state --state NEW -j ACCEPT iptables -A INPUT -i $EXT -p tcp -j REJECT --reject-with tcp-reset iptables -A INPUT -i $EXT -j REJECT --reject-with icmp-port-unreachable iptables -A INPUT -i $EXT -j DROP echo iptables started return 0 } stop() { iptables -F iptables -t nat -F iptables -X ssh echo iptables stopped return 0 } case $1 in start) start ;; stop) stop ;; restart) stop start ;; *) echo "usage: $0 [start|stop|restart]" exit 1 ;; esac exit 0