Requirements:
- Debian Linux
- Multiple network interfaces connected to WAN (for example using a switch, might also be possible using bridge and dummy interfaces in Linux)
- ISP that gives multiple IPs via DHCP
Configure the interfaces
File: /etc/network/interfaces , change the interface names to match yours.
# The loopback network interface auto lo iface lo inet loopback # LAN Interface allow-hotplug ens3 iface ens3 inet static address 172.16.1.1 netmask 255.255.255.0 up /sbin/ip rule add 172.16.1.0/24 dev $IFACE table 10 up /sbin/ip rule add 172.16.1.0/24 dev $IFACE table 11 # WAN Interface 1 allow-hotplug ens5 iface ens5 inet manual up /sbin/dhclient -4 -v -pf /run/dhclient.$IFACE.pid -lf /var/lib/dhcp/dhclient.$IFACE.leases -I -df /var/lib/dhcp/dhclient6.$IFACE.leases $IFACE down /bin/kill $(cat /run/dhclient.$IFACE.pid) # WAN Inetrface 2 allow-hotplug ens9 iface ens9 inet manual up /sbin/dhclient -4 -v -pf /run/dhclient.$IFACE.pid -lf /var/lib/dhcp/dhclient.$IFACE.leases -I -df /var/lib/dhcp/dhclient6.$IFACE.leases $IFACE down /bin/kill $(cat /run/dhclient.$IFACE.pid) # WAN Interface 3 allow-hotplug ens10 iface ens10 inet manual up /sbin/dhclient -4 -v -pf /run/dhclient.$IFACE.pid -lf /var/lib/dhcp/dhclient.$IFACE.leases -I -df /var/lib/dhcp/dhclient6.$IFACE.leases $IFACE down /bin/kill $(cat /run/dhclient.$IFACE.pid)
Configure dhclient enter hooks to use multiple routing tables
File: /etc/dhcp/dhclient-enter-hooks.d/routing-tables , change the interface names to match yours.
RUN="yes" if [ "$RUN" = "yes" ]; then if [ "$interface" = "ens5" ]; then echo "Main wan interface, not doing anything special" >> /tmp/dhclient-routing-tables.log else table="" if [ "$interface" = "ens9" ]; then table="10" elif [ "$interface" = "ens10" ]; then table="11" fi echo "Routing table $table" >> /tmp/dhclient-script.debug if [ -n $table ]; then if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ]; then for router in $old_routers; do echo "Removing default $router table $table" >> /tmp/dhclient-routing-tables.log /sbin/ip route delete $router dev $interface table $table /sbin/ip route delete default via $router dev $interface table $table done for router in $new_routers; do echo "Adding default $router table $table" >> /tmp/dhclient-routing-tables.log /sbin/ip route add $router dev $interface table $table /sbin/ip route add default via $router dev $interface table $table done fi if [ x$new_ip_address != x$old_ip_address ]; then echo "ip rule add from $new_ip_address table $table" >> /tmp/dhclient-routing-tables.log /sbin/ip rule del from $old_ip_address table $table /sbin/ip rule add from $new_ip_address table $table fi fi fi new_routers="" old_routers="" fi fi
Automatically update firewall when dhclient runs
File: /etc/dhcp/dhclient-exit-hooks.d/firewall
# Reload firewall in case ip has changed or such RUN="yes" if [ "$RUN" = "yes" ]; then /etc/firewall.sh fi
Configure the NAT and firewall
File: /etc/firewall.sh
#!/bin/bash LAN_IF="ens3" WAN_INTERFACE="ens5" WAN2_INTERFACE="ens9" WAN2_IP=$(ip addr show dev $WAN2_INTERFACE | grep 'inet ' |awk '{print $2}' | cut -f1 -d'/') WAN3_INTERFACE="ens10" WAN3_IP=$(ip addr show dev $WAN3_INTERFACE | grep 'inet ' |awk '{print $2}' | cut -f1 -d'/') # Configure routing tables ip rule delete fwmark 10 table 10 ip rule add fwmark 10 table 10 ip rule delete fwmark 11 table 11 ip rule add fwmark 11 table 11 ip route add 172.16.1.0/24 dev ens3 table 10 ip route add 172.16.1.0/24 dev ens3 table 11 # Configure firewall iptables -F iptables -F -t nat iptables -F -t mangle ip6tables -F # Allow ICMP iptables -A INPUT -p icmp -j ACCEPT iptables -A FORWARD -p icmp -j ACCEPT ip6tables -t filter -A INPUT -p icmpv6 -j ACCEPT ip6tables -t filter -A FORWARD -p icmpv6 -j ACCEPT # Allow SSH iptables -A INPUT -p tcp --dport 22 -j ACCEPT ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT # Allow localhost iptables -I INPUT -i lo -s 127.0.0.0/8 -d 127.0.0.0/8 -j ACCEPT ip6tables -I INPUT -i lo -s ::1 -d ::1 -j ACCEPT # Allow state iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT # NAT Postrouting # Allow to all from lan iptables -A FORWARD -i $LAN_IF -o $WAN_INTERFACE -s 172.16.1.0/24 -j ACCEPT iptables -A FORWARD -i $LAN_IF -o $WAN2_INTERFACE -s 172.16.1.10 -j ACCEPT iptables -A FORWARD -i $LAN_IF -o $WAN3_INTERFACE -s 172.16.1.20 -j ACCEPT # Special mangle for multiple routing tables iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j RETURN # return if already set iptables -t mangle -A PREROUTING -i $WAN2_INTERFACE -j MARK --set-mark 10 iptables -t mangle -A PREROUTING -i $WAN3_INTERFACE -j MARK --set-mark 11 iptables -t mangle -A POSTROUTING -o $WAN2_INTERFACE -j MARK --set-mark 10 iptables -t mangle -A POSTROUTING -o $WAN3_INTERFACE -j MARK --set-mark 11 iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark # HTTP/HTTPS on WAN2 to webserver iptables -t nat -A PREROUTING -i $WAN2_INTERFACE -p tcp --dport 80 -j DNAT --to 172.16.1.11:80 iptables -t nat -A PREROUTING -i $WAN2_INTERFACE -p tcp --dport 443 -j DNAT --to 172.16.1.11:443 iptables -A FORWARD -p tcp --dport 80 -d 172.16.1.11 -j ACCEPT iptables -A FORWARD -p tcp --dport 443 -d 172.16.1.11 -j ACCEPT # Rest of WAN2 to 172.16.1.10 iptables -t nat -A POSTROUTING -o $WAN2_INTERFACE -s 172.16.1.10 -j SNAT --to-source $WAN2_IP iptables -t nat -A PREROUTING -i $WAN2_INTERFACE -j DNAT --to-destination 172.16.1.10 iptables -t mangle -A PREROUTING -s 172.16.1.10 -j MARK --set-mark 10 # 1:1 NAT for WAN3 iptables -t nat -A POSTROUTING -o $WAN3_INTERFACE -s 172.16.1.20 -j SNAT --to-source $UWAN3_IP iptables -t nat -A PREROUTING -i $WAN3_INTERFACE -j DNAT --to-destination 172.16.1.20 iptables -t mangle -A PREROUTING -s 172.16.1.20 -j MARK --set-mark 11 iptables -A FORWARD -p tcp --dport 22 -d 172.16.1.20 -j ACCEPT # Masquerading iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -j MASQUERADE # Log the rest iptables -t filter -A INPUT -j LOG ip6tables -t filter -A INPUT -j LOG iptables -A FORWARD -j LOG ip6tables -A FORWARD -j LOG # Default drop iptables -P INPUT DROP iptables -P FORWARD DROP ip6tables -P INPUT DROP ip6tables -P FORWARD DROP
Make script executable:
# chmod +x /etc/firewall.sh
Create systemd service for firewall
File: /etc/systemd/system/firewall.service
[Unit] Description=Firewall ConditionFileIsExecutable=/etc/firewall.sh [Service] Type=oneshot ExecStart=/etc/firewall.sh [Install] WantedBy=basic.target
Enable service:
# systemctl enable firewall
Enable forwarding
File: /etc/sysctl.conf
net.ipv4.ip_forward=1 net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2
Completion
Reboot the system and verify functionality.