My Linux Router

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

Leave a Comment

Your email address will not be published. Required fields are marked *