Monday, May 4, 2009

Set up a LAN gateway with DHCP, Dynamic DNS and iptables on Debian Etch

Linux is a perfect platform to act as a router/gateway.

In this tutorial, I will explain how to set up a Linux box to operate as a network router. The box will provide the following services:

  • DHCP server to provide the ip addresses to the machines in the LAN
  • DNS server to resolve domain names
  • Gateway with IP tables to give access to the Internet.
  • Firewall with IP tables.

The resulting machine will have quite a small footprint: about 600M, and except if your network is intensively used, a low spec computer can be recycled to do the job.

As the machine is going to operate as a router/firewall

This tutorial is based on a Debian Etch 4.0 r3 minimal network install, i.e that during the install, at the "Software selection" step, I unselected everything.

The box has 2 network interfaces:

  • eth0: ip 192.168.1.9 which is connected to internet (not directly though, but it is the box that is routing the traffic toward internet for this LAN)
  • eth1: ip 192.168.2.1, this is the interface connected to our LAN and that will forward the traffic to and from internet.

Also, we are going to manage the domain name lan.debuntu.local, so each machine will be able to communicate with each others by using their hostname.

So first let's get started with the set up of bind9

1. DNS server

As a DNS server we are going to use bind9, it will be configured to resolve the names of the host for our network lan.debuntu.local.

The DNS server will also accept dynamic DNS update from the local DHCP server.

In this tutorial, I will be using the Dynamic DNS feature of bind.

1.1. Installing the DNS server

Make sure you are installing bind9 as older version of bind do not not support dynamic dns updates.

# apt-get install bind9

1.2. Configuring the DNS server

In order to keep the default install files clean, we are going to only edit /etc/bind/named.conf.local . In this file we are going to allow dns updates from local host using "rndc-key" (which is installed by default with bind9 package)

We are also going to define 2 zones:

  • lan.debuntu.local : our local domain name
  • 2.168.192.in-addr.arpa : our local network ip zone, this will allow us to reverse lookup names.

So let's go and edit /etc/bind/named.conf.local and add:

#allow dns updates from localhost with key "rndc-key"
include "/etc/bind/rndc.key";
controls {
inet 127.0.0.1 allow { localhost; } keys { "rndc-key"; };
};

#defines lan.debuntu.local
zone "lan.debuntu.local" {
type master;
file "db.lan.debuntu.local";
allow-update { key "rndc-key"; };
};

#defines our local subnet 192.168.2.0/24
zone "2.168.192.in-addr.arpa" {
type master;
notify no;
file "db.2.168.192";
allow-update { key "rndc-key"; };
};

Then, we need to create those 2 files : /var/cache/bind/db.lan.debuntu.local and /var/cache/bind/db.2.168.192 .

The first one will be used to resolve names, while the second one to reverse name lookup.

/var/cache/bind/db.lan.debuntu.local will look like:

;
; Zone file for lan.debuntu.local
;
; The full zone file
;
$TTL 3D
@ IN SOA ns.lan.debuntu.local. postmaster.lan.debuntu.local. (
200806281; serial, todays date + todays serial #
8H ; refresh, seconds
2H ; retry, seconds
4W ; expire, seconds
1D ) ; minimum, seconds
;
NS ns ; Inet Address of name server
MX 10 mail ; Primary Mail Exchanger
;
A 192.168.2.1 ; IP address
;
router A 192.168.2.1
ns CNAME router
dhcp CNAME ns.lan.debuntu.local.
* A 192.168.2.1

While /var/cache/bind/db.2.168.192 will look like:

$TTL 3D
@ IN SOA lan.debuntu.local. postmaster.lan.debuntu.local. (
200806281 ; serial, todays date + todays serial #
8H ; refresh, seconds
2H ; retry, seconds
4W ; expire, seconds
1D ) ; minimum, seconds
;
@ IN NS ns.lan.debuntu.locl.
@ IN PTR lan.debuntu.local.

1 IN PTR router.lan.debuntu.local.


2. DHCP server

In order to provide an IP address to the other machines in the network, we need to use a DHCP server.
This DHCP server will provide the host with all the information needed to connect to any other accessible host. i.e, the IP, netmask, gateway, domain name server.
The DHCP server will also update bind with a nt set of hostname and IP when the client is requesting a specific hostname.

2.1. Installing the DHCP server

We are going to install the dhcp server packaged under the name of dhcp3-server. To install it, simply type:

# apt-get install dhcp3-server

Make sure you are installing dhcp3-server and not dhcp as the latter does not support dynamic dns updates.

2.2. Configuring the DHCP server

The configuration is all in /etc/dhcp3/dhcpd.conf.

In our set up, we want to give IPs in the range 192.168.2.0/24 and we want to set up our domain name to be lan.debuntu.local

We are only going to listen for DHCP queries on eth1 and thus will need to bind the service for only this specific address. To achieve this, go and edit /etc/default/dhcp3-server and make sure INTERFACES is set as follow:

... ... # On what interfaces should the DHCP server (dhcpd) serve DHCP requests? # Separate multiple interfaces with spaces, e.g. "eth0 eth1". INTERFACES="eth1"

Then, go and edit /etc/dhcp3/dhcpd.conf and make it look as follows:

#naming the server # and enabling ddns server-identifier router; authoritative; ddns-update-style interim; include "/etc/bind/rndc.key"; # Use what key in what zone zone lan.debuntu.local. { primary 127.0.0.1; key "rndc-key"; } #Standard DHCP info option domain-name "lan.debuntu.local"; option domain-name-servers ns.lan.debuntu.org; default-lease-time 600; max-lease-time 7200; log-facility local7; subnet 192.168.2.0 netmask 255.255.255.0 { range 192.168.2.5 192.168.2.200; option routers router.lan.debuntu.local; zone 2.168.192.in-addr.arpa. { primary ns.lan.debuntu.local; key "rndc-key"; } zone lan.debuntu.local. { primary ns.lan.debuntu.local; key "rndc-key"; } }

Which says that we provide IP addresses on the range 192.168.2.5 to 192.168.2.200, and the traffic for this network will be routed by router.lan.debuntu.local

On the top of this, the domain name to be used for dns ueries is lan.debuntu.local and the DNS server is machine ns.lan.debuntu.local

The host names where defined earlier in the DNS section, dhcp3-server will query his DNS server to find there IP. Only the IP will be sent back to the host clients.

Now our DHCP server should be ready, it is time to restart it.

# /etc/init.d/dhcp3-server restart

Same here, if anything goes wrong, /var/log/messages, /var/log/syslog and /var/log/daemon.log will be your friends.

At this stage, you should normally be able to provide IPs to all the host in the network, provide them domain name resolution service and all the host should be able to communicate with each others using hostnames.

BUT, except for the gateway, none of the host can connect to the internet.

3. Forwarding Internet traffic with IPtables

IPtables is both used to act as a firewall, but it is also the one passing packets from one network to another.

3.1. Enabling IP forwarding

IP forwarding is enabled at the kernel level. The way to enable it is to set /proc/sys/net/ipv4/ip_forward to 1.
This can be done during runtime by typing:

# echo 1 > /proc/sys/net/ipv4/ip_forward

To make those changes permanent upon reboots, edit /etc/sysctl.conf and make sure there is the following values:

net.ipv4.conf.default.forwarding=1
net.ipv4.conf.all.forwarding=1

3.2. Setting iptables rules

The next step is to set up iptables rules, the ones that will tell the kernel what to do with the packet depending on their states.

This firewall will only accept ssh connection from the WAN, anything from the LAN and will forward port 2222 from the WAN to machine 192.168.2.2 on port 22.

Here is the script used:

#!/bin/sh
#
# this script requires iptables package to be
# installed on your machine


# Where to find iptables binary
IPT="/sbin/iptables"
# The network interface you will use
# WAN is the one connected to the internet
# LAN the one connected to your local network
WAN="eth0"
LAN="eth1"
# First we need to clear up any existing firewall rules
# and chain which might have been created
$IPT -F
$IPT -F INPUT
$IPT -F OUTPUT
$IPT -F FORWARD
$IPT -F -t mangle
$IPT -F -t nat
$IPT -X

# Default policies: Drop any incoming packets
# accept the rest.
$IPT -P INPUT DROP
$IPT -P OUTPUT ACCEPT
$IPT -P FORWARD ACCEPT

# To be able to forward traffic from your LAN
# to the Internet, we need to tell the kernel
# to allow ip forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# Masquerading will make machines from the LAN
# look like if they were the router
$IPT -t nat -A POSTROUTING -o $WAN -j MASQUERADE


$IPT -t nat -A PREROUTING -i $WAN -p tcp --dport 2222 -j DNAT --to-destination 192.168.2.2:22
$IPT -A FORWARD -i $WAN -p tcp --dport 22 -m state --state NEW -j ACCEPT

# Do not allow other new or invalid connections to reach your internal network
$IPT -A FORWARD -i $WAN -m state --state NEW,INVALID -j DROP

# Accept any connections from the local machine
$IPT -A INPUT -i lo -j ACCEPT
# plus from your local network
$IPT -A INPUT -i $LAN -j ACCEPT

# log those packets and inform the sender that the packet was rejected
$IPT -N Rejectwall
$IPT -A Rejectwall -m limit --limit 10/minute -j LOG --log-prefix "Rejectwall: "
$IPT -A Rejectwall -j REJECT
# use the following instead if you want to simulate that the host is not reachable
# for fun though
#$IPT -A Rejectwall -j REJECT --reject-with icmp-host-unreachable

$IPT -A INPUT -p icmp -j ACCEPT

# Accept ssh connections from the Internet
$IPT -A INPUT -i $WAN -p tcp --dport 22 -j ACCEPT

# or only accept from a certain ip
#$IPT -A INPUT -i $WAN -s 125.124.123.122 -p tcp --dport 22 -j ACCEPT

# Accept related and established connections
$IPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Drop netbios from the outside, no log, just drop
$IPT -A INPUT -p udp --sport 137 --dport 137 -j DROP

# Finally, anything which was not allowed yet
# is going to go through our Rejectwall rule
$IPT -A INPUT -j Rejectwall

Now, run the script:

# sh iptables.sh

All host within the LAN should now be able to access the internet!

now we have to make sure that the changes are permanent upon reboot.

3.3. Making iptables rules persistent

I like to be able to easily start and stop iptables using an init.d script. Unfortunately, this is gone from debian packages.

Anyhow, here is a script that you can copy to /etc/init.d/iptables :

#!/bin/sh

IPTABLES="/sbin/iptables"

# load options
if [ -f /etc/default/iptables ] ; then
. /etc/default/iptables
else
exit 1
fi

# Check for daemon presence
test -x ${IPTABLES} || exit 0

# Get lsb functions
. /lib/lsb/init-functions
. /etc/default/rcS

# Check for saved state
if [ x$1 != "xsave" ] && [ x$1 = "xstart" ] && ! test -r ${IPTABLES_SAVE}; then
log_warning_msg "Skipping iptables configuration..."
exit 0
fi

flush() {
if [ -f /proc/net/ip_tables_names ] ; then
for table in `cat /proc/net/ip_tables_names`; do
${IPTABLES} -F -t $table
${IPTABLES} -X -t $table
if [ $table = nat ]; then
${IPTABLES} -t nat -P PREROUTING ACCEPT
${IPTABLES} -t nat -P POSTROUTING ACCEPT
${IPTABLES} -t nat -P OUTPUT ACCEPT
elif [ $table = mangle ]; then
${IPTABLES} -t mangle -P PREROUTING ACCEPT
${IPTABLES} -t mangle -P INPUT ACCEPT
${IPTABLES} -t mangle -P FORWARD ACCEPT
${IPTABLES} -t mangle -P OUTPUT ACCEPT
${IPTABLES} -t mangle -P POSTROUTING ACCEPT
elif [ $table = filter ]; then
${IPTABLES} -t filter -P INPUT ACCEPT
${IPTABLES} -t filter -P FORWARD ACCEPT
${IPTABLES} -t filter -P OUTPUT ACCEPT
fi
done
fi
return 0
}

case "$1" in
start)
log_begin_msg "Loading iptables settings..."
${IPTABLES}-restore ${SAVE_RESTORE_OPTIONS} ${IPTABLES_SAVE}
log_end_msg $?
;;
save)
log_begin_msg "Saving iptables settings..."
${IPTABLES}-save ${SAVE_RESTORE_OPTIONS} > ${IPTABLES_SAVE}
log_end_msg $?
;;
stop)
log_begin_msg "Clearing iptables settings..."
flush
log_end_msg $?
;;
restart)
$0 stop
$0 start
;;
status)
${IPTABLES} -L
;;
*)
log_success_msg "Usage: $0 {start|stop|restart|status|save}"
exit 1
esac

and another one to /etc/default/iptables :

IPTABLES_SAVE="/etc/iptables-rules"
SAVE_RESTORE_OPTIONS="-c"

finally, once you are satisfied with your actual rules, save them by typing the following:

# /etc/init.d/iptables save

We also need to make sure that the service will be started upon boot up by typing:

# update-rc.d iptables defaults 20

And that's it, here we are with a box that provide full access t internet!

4. Troubleshooting

Even though this tutorial is made in such a way that you could just copy and paste, issues may arise.

You might find useful to install the packages dnsutils, telnet, tcpdump and nmap to get a better idea of what is going on.

In /var/log , action is going to be mailly in messages, daemon.log and syslog.





In zone lan.debuntu.local, we define the standard bind headers and finally, some static hosts in our network: router, ns, dhcp and any other host to point to 192.168.2.1.

In zone 2.168.192.in-addr.arpa, we define our reverse lookup name for IP 192.168.2.1.

You need to make sure that the directory holding those db files is writable as bind will need to create journal files to get DDNS to work.

Finally, we just have to restart bind. If there is anything wrong, /var/log/syslog is your best friend, along with goolge :).

/etc/init.d/bind9 restart

now that our DNS server is up and running, we need to handle DHCP request.



No comments: