Snippets:Simple router (Linux)

#!/bin/sh

# License: CC0

set -eu
iptables-nft-restore <<\EOF
*filter
:FORWARD DROP
-A FORWARD -i eth0 ! -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i eth0 -j DROP
-A FORWARD ! -i eth0 -o eth0 -m state ! --state UNTRACKED -j ACCEPT
COMMIT
*nat
-A POSTROUTING -o eth0 -j MASQUERADE
COMMIT
EOF

ip6tables-nft-restore <<\EOF
*filter
:FORWARD DROP
-A FORWARD -i eth0 ! -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i eth0 -j DROP
-A FORWARD ! -i eth0 -o eth0 -m state ! --state UNTRACKED -j ACCEPT
COMMIT
*nat
-A POSTROUTING -o eth0 -j MASQUERADE
COMMIT
EOF
sysctl -w net.ipv6.conf.all.forwarding=1 net.ipv4.ip_forward=1
  • Replace "eth0" (in all instances where it exists in the above script) with the name of the outbound interface that you have Internet access on.
  • In order for this script to actually be useful, your system must have at least one subordinate network relative to the network interface where you have Internet access on; see static route mesh. This script will still run correctly even if there is no subordinate network, but it won't be very useful except in cases where the subordinate network interfaces are subject to hot plugging.
  • The filter is configured such that the subordinate networks are allowed to access the Internet-facing interface, but the subordinate networks cannot communicate with each other, nor can the Internet-facing interface access any of the subordinate networks. Access to servers on the local system with the firewall is not restricted.
  • This script uses Network Address Translation (NAT) to allow the subordinate networks to connect to the greater Internet without static routes or dynamic routing protocols. If you have static routes, routing protocols, or other circumstances where you are able to access the subordinate networks directly, then you can remove the NAT by omitting the "-j MASQUERADE" line.
  • If you are using Docker in the same network namespace as the firewall rules[1], then you should set --iptables=false on the ExecStart= command or iptables: false in /etc/docker/daemon.json. The -p option in docker run for publishing ports will cease to work, but you can still use Socketbox or other similar tools.
  • This script should be executed on system startup (e.g. in /etc/rc.local).
  1. Refers to the network namespace in which the dockerd command is run, and can be modified by prepending nsenter --net=[path to network namespace] (also ip netns exec [name of network namespace]) to the command specification or by adding NetworkNamespacePath= or JoinsNamespaceOf= to the systemd unit file. Not to be confused with --net=host.