Quick and dirty, my Debian router virtual-machine, my requirements were simple: DDNS, NAT-Loopback, NAT-PMP (A.K.A. UPnP Port Mapping), and some bandwidth usage monitoring
apt-get update && apt-get dist-upgrade apt-get install bind9 isc-dhcp-server iptables
Here my external interface will be eth0, internal will be eth1. I’ll assume that you have internet access available on eth0 already, and you either have a static ip, or use dhcp to get an address from your provider.
Normally I would add a bridge interface for the internal interfaces, however I’m already doing this for eth1 on the hypervisor, so won’t be necessary on the vm.
Configure eth1 to have a static ip, this needs to be on the same subnet as addresses you plan on handing out via DHCP, as the server won’t give addresses for subnets it itself is not part of
vim /etc/network/interfaces
auto lo iface lo inet loopback allow-hotplug eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet static address 10.0.0.1 network 10.0.0.0 netmask 255.255.255.0 broadcast 10.0.0.255
DHCP Configuration
There are easier options out there, such as dnsmasq, which some people swear by. I prefer the industry standard ISC-DHCP-Server. Firstly edit your listening interfaces to only listen over the LAN.
vim /etc/default/isc-dhcp-server
INTERFACES="eth1"
Now edit the actual configuration files for your DHCP server. Start off with a subnet declaration block and some sane defaults
vim /etc/dhcp/dhcpd.conf
include "/etc/bind/rndc.key"; ddns-updates on; ddns-updates interim; update-static-leases on; default lease time 86400; max-lease-time 86400; authoritative; allow booting; allow bootp; allow client-updates; #DNS Zone Updating, those trailing spaces are important zone yourdomain.com. { primary localhost; key rndc-key; } #Reverse DNS, your subnet in reverse, EG 1.168.192.in-addr.arpa. zone 0.0.10.in-addr.arpa. { primary localhost; key rndc-key; } # Actual Subnet declaration block # This is where subnet specific settings go subnet 10.0.0.0 netmask 255.255.255.0 { range 10.0.0.100 10.0.0.200; option subnet-mask 255.255.255.0; option routers 10.0.0.1; option domain-name-servers 10.0.0.1; option domain-name "yourdomain.com"; ddns-domainname "yourdomain.com."; ddns-rev-domainname "in-addr.arpa."; }
You’ll need to create that rndc key before you go much further
rndc-confgen
Check your config for syntax errors
dhcpd -t -cf /etc/dhcp/dhcpd.conf
DNS
First add your ISP’s (or googles) DNS server to your BIND config, so that it can forward requests.
vim /etc/bind/named.conf.options
options { directory "/var/cache/bind"; forwarders { 192.168.1.26; }; dnssec-validation auto; auth-nxdomain no; # conform to RFC1035 listen-on-v6 { any; }; };
Now to declare your DNS Zone and Reverse DNS Zone
vim /etc/bind/named.conf.local
include "/etc/bind/rndc.key"; controls { inet 127.0.0.1 allow { localhost; } keys { "rndc-key"; }; }; zone "vm.adamparsons.id.au" { type master; file "/etc/bind/db.parsons.lan"; allow-update { key rndc-key; }; }; zone "0.0.10.in-addr.arpa" { type master; file "/etc/bind/0.0.10.in-addr.arpa"; allow-update { key rndc-key; }; };
Your domain db file should look like this
vim /etc/bind/db.vm.adamparsons.id.au
$TTL 86400 @ IN SOA vm.adamparsons.id.au. root.localhost. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 86400 ) ; Negative Cache TTL ; @ IN NS ns.vm.adamparsons.id.au. @ IN A 10.0.0.1 ns IN A 10.0.0.1 webserver IN A 10.0.0.100
And your reverse DNS file should look like this
vim /etc/bind/0.0.10.in-addr.arpa
$TTL 604800 @ IN SOA 0.0.10.in-addr.arpa. root.localhost. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS localhost. 1.0.0 IN PTR localhost.
Permissions!
BIND will attempt to write to these files, so will need R/W Access. I’m gonna leave that one up to you, because I suck at this.
Time for a rELOAD
After changing 10 thousand config files, lets make sure everything still works.
systemctl stop bind9 systemctl start bind9 systemctl force-reload isc-dhcp-server.service
Routing with iptables
Firstly ensure that your kernel has routing enabled
sysctl -w net.ipv4.ip_forward=1
echo 1 > /proc/sys/net/ipv4/ip_forward
Remember that eth0 is the external network, and eth1 is the internal
First lets edit some base rules in bash, then when we’re happy, save to an iptables-restore file.
#!/bin/sh ## Clear all rules iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT ## By Default, block everything that isn't outgoing from the host iptables -P INPUT DROP iptables -P FORWARD DROP ## Allow responses iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT ## Allow ICMP Messages iptables -A INPUT -p icmp -j ACCEPT iptables -A OUTPUT -p icmp -j ACCEPT ## Allow internal TCP and UDP traffic (this is very permissive) iptables -A INPUT -p tcp -s 10.0.0.0/24 -j ACCEPT iptables -A INPUT -p udp -s 10.0.0.0/24 -j ACCEPT iptables -A INPUT -p icmp -s 10.0.0.0/24 -j ACCEPT ## Allow loopback traffic iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT ## Allow ssh from every interface iptables -A INPUT -p tcp --dport 22 -j ACCEPT ## NAT outgoing connections iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE iptables -A FORWARD -i eth1 -j ACCEPT iptables -A FORWARD -i eth0 -j ACCEPT
Now try to ping the outside world from one of the machines behind the NAT.
Port Forwarding
Here goes an exercise in futility. Lets port forward port a web server, port 80,443 on 10.0.0.100, the IP following the ‘-d’ is your WAN address, this shouldn’t be substituted for anything else such as interfaces.
iptables -t nat -A PREROUTING -d 192.168.1.186/32 -p tcp -m multiport --dports 80,443 -j DNAT --to-destination 10.0.0.100
The tricky part: NAT Reflection, or NAT Loopback, depending who you talk to. NAT Loopback in its most simple terms is being able to access your DNAT’d ports while inside the network, by accessing your WAN address. This has taken me forever to get working
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -d 10.0.0.100/32 -p tcp -m multiport --dports 80,443 -j SNAT --to-source 192.168.1.186
Bandwidth Monitoring
Still a difficult one to get right, all the different solutions out there have varying degrees of uselessness. I use a combination of bandwidthd and ntop-ng, as bandwidthd does the one thing I wanted ntop-ng to do (total usage per device) but ntop-ng is pretty cool regardless
apt-get install ntop-ng bandwidthd
Extras to make it ™JustWork™
Some extra stuff you might wanna install if you have like an actual household with real people that have lives and don’t wanna configure your routers shell scripts to make skype work or get onto xbox live.
..Webmin, decent iptables, dhcpd and bind front-end
.. natpmp, automatic upnp port forwarding