Overview
Any basic home router is a combination firewall/switch/wifi device. If you don't need wifi, you can get much finer-grained control of your home network by buying a low-power computer with 2 ethernet ports to use as your firewall, and then a cheap 4 or 8 port switch to do your switching. This gives you far better control and flexibility.
Here's how you configure Ubuntu 8.04 on a low-power firewall machine.
Be warned that this is based on a first experience with Ubuntu, so it's a bit meandering and stream-of-consciousness and has some cursewords.
Install Ubuntu 8.04 server edition on your firewall computer.
Not covered here. As a general rule, use i386 version (on the assumption that you are using smaller; low-power hardware).
Kernel Settings
Do NOT skip this step or your internal computers will not be able to reach the internet.
Ubuntu 8.04 by default does not allow packet forwarding, which is a fantastically sane default setting. However, this needs to be changed for your 2-ethernet-port computer to act as a firewall. Therefore, ensure /etc/sysctl.conf looks like this:
I think I want low-level messages on console, so comment out printk settings to restore them to Fedora-like defaults:
#kernel.printk = 4 4 1 7
Syncookies are enabled in Fedora, so enable them in Ubuntu too, by uncommenting:
net.ipv4.tcp_syncookies=1
And obviously enable packet forwarding so you can use a firewall:
net.ipv4.ip_forward=1
(ipv6 packet forwarding is disabled; leave it that way for now.)
Ignore ping requests by uncommenting this:
net/ipv4/icmp_echo_ignore_broadcasts = 1
Now, reboot! (Only way for these settings to take effect.)
A nice change from Fedora
Unlike Fedora, there is no SELinux, so we don't have to disable it. Yay!
There's no NetworkManager bullshit to disable, unlike Fedora. Yay!
Now, where are the network settings kept? How to I set the hostname? Ah. It is kept in /etc/hostname. Right now, it is listed as ubunturouter, like I asked it to be. I wonder if ubunturouter.lan is OK? I guess I'll leave it alone for now.
dnsmasq
Now let's install dnsmasq
This is something Fedora's Yum does automatically when you run any other command; update the repository db:
root@ubunturouter:~# apt-get update
This seems to be equivalent of yum update, but where's the option to check first?
This seems to be the only way to check for updates without doing anything else (-s puts it in 'dry run' mode):
root@ubunturouter:~# apt-get -s upgrade
OK, but let's update:
root@ubunturouter:~# apt-get upgrade
Ugly output compared to yum, but seems to have worked. Updated the kernel this time, so, rebooting!
OK, rebooted. Seems to have worked OK.
Let's see if dnsmasq and logwatch are installed.
root@ubunturouter:~# which dnsmasq root@ubunturouter:~# which logwatch root@ubunturouter:~#
Nope. apt-get time!
root@ubunturouter:~# apt-get search dnsmasq E: Invalid operation search
OK, that blows. Yum can do search.
Let's try this:
root@ubunturouter:~# apt-cache search dnsmasq dnsmasq-base - A small caching DNS proxy and DHCP/TFTP server dnsmasq - A small caching DNS proxy and DHCP/TFTP server
OK. Now let's try to install:
root@ubunturouter:~# apt-get install dnsmasq
That worked, but a little too well: at the end of the install messages, it said:
* Starting DNS forwarder and DHCP server dnsmasq
Erm, thanks, but I didn't ask you to start dnsmasq. Now I have to find out how to shut it down until I'm ready to use it.
root@ubunturouter:~# /etc/init.d/dnsmasq stop * Stopping DNS forwarder and DHCP server dnsmasq [ OK ] root@ubunturouter:~#
Holy shit, that worked very similarly to Fedora.
It looks like update-rc.d is Deb/untu's flavour of chkconfig.
Let's see if we can show the runlevels for dnsmasq:
OK, there seem to be two command-line tools for this: update-rc.d, which seems workable but very low-level, and sysv-rc-conf, which seems to be much more like Fedora's chkconfig, but which isn't installed by default. Let's see if we can find it:
root@ubunturouter:~# apt-cache search sysv-rc-conf sysv-rc-conf - SysV init runlevel configuration tool for the terminal root@ubunturouter:~#
Looks promising.
root@ubunturouter:~# apt-get install sysv-rc-conf
Seems to have worked.
root@ubunturouter:~# sysv-rc-conf --list
Yup. Woot!
root@ubunturouter:~# sysv-rc-conf --level 2 dnsmasq off root@ubunturouter:~# sysv-rc-conf --list dnsmasq dnsmasq 1:off 2:off 3:on 4:on 5:on root@ubunturouter:~#
That seems to have turned it off for runlevel 2 (the silly default in Ubuntu.)
Install logwatch
OK, let's see if logwatch exists for Ubuntu.
root@ubunturouter:~# apt-cache search logwatch logwatch - log analyser with nice output written in Perl fwlogwatch - Firewall log analyzer root@ubunturouter:~#
Yup. And, oooh, fwlogwatch definitely merits my attention too! I could turn on firewall logging and then get e-mail updates!
root@ubunturouter:~# apt-get install logwatch
That worked. It installed a lot of fucked up dependancies (like postfix) but whatever.
Oh: except it looks like postfix is started with the server. That's a no-no. Fix now:
root@ubunturouter:~# sysv-rc-conf --level 2 postfix off root@ubunturouter:~# /etc/init.d/postfix stop root@ubunturouter:~# ls /etc/cron.daily/ 00logwatch apt aptitude bsdmainutils logrotate man-db mlocate standard sysklogd
Seems to have already added logwatch to cron, so that's nice.
Install libgmail
I know I'll need libgmail for my daily updates. Let's see if that's available:
root@ubunturouter:~# apt-cache search libgmail gmailfs - Use your GMail account as a filesystem python-libgmail - Python bindings to access Gmail accounts root@ubunturouter:~#
Yup. Time to install it.
root@ubunturouter:~# apt-get install python-libgmail
That was easy.
Configure networking
May as well start configuring stuff now. Are there any iptables rules currently running after booting?
root@ubunturouter:~# iptables --list Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Nope. So, unlike Fedora, I guess there's no firewall script to disable or override on startup. Handy. But, scary.
One thing we need to do is tie our ethernet ports to their respective MAC addresses. On Fedora, this is done in /etc/sysconfig/network-scripts, but not on Ubuntu, you create the file /etc/iftab, like so:
eth0 mac 00:08:9b:b2:cb:a1 eth1 mac 00:08:9b:b2:cb:a0
And that should keep our ethernet ports from "drifting".
In fact, let's reboot and ensure that is so.
Yes it is.
Configure firewall
Let's copy the firewall from our current router so that we will have it ready to go when we need it. But where should our firewall live? It looks like the equivalent of Fedora's /etc/rc.d/rc.local is /etc/rc.local, and we keep our firewall scripts in the same dir as rc.local, so let's put our firewall script right in /etc.
root@ubunturouter:/etc# scp mwood@192.168.1.1:/etc/rc.d/iptables* .
Seems to have worked. We are concerned with the file named /etc/iptables_nat_router.sh. Now in Fedora, some of the kernel modules needed by the firewall script are compiled right into the kernel, such that loading them produces errors. To figure out which ones were compiled in, I downloaded the source for the Fedora kernels and looked at the kernel config files. Do I need to do the same for Ubuntu? Let's first just try the firewall with all the module requests uncommented and see how that works out.
Let's just execute the module loading part of the script and see how that works out! Yes, that was a smart idea. I tried to load all of them, and there were no errors, so it looks like we're good to go leaving all of the kernel module loaders uncommented.
So here's what /etc/iptables_nat_router.sh looks like after crafting it for Ubuntu's kernel modules:
#!/bin/sh # # iptables firewall script for sharing # broadband Internet, with no public services # # From Linux Networking Cookbook, by Carla Schroder, O'Reilly, 2007 # # NOTE!!! Forwarding will not work unless # /proc/sys/net/ipv4/ip_forward == '1' # preferably set not here but through /etc/sysctl.conf # define variables ipt="/sbin/iptables" mod="/sbin/modprobe" LAN_IFACE="eth0" WAN_IFACE="eth1" #basic set of kernel modules $mod ip_tables # now compiled into kernel as of f10 $mod ip_conntrack # now compiled into kernel as of f10 $mod iptable_filter # now compiled into kernel as of f10 $mod iptable_nat $mod iptable_mangle $mod ipt_LOG $mod ipt_limit $mod ipt_state # now compiled into kernel as of f10 $mod ipt_MASQUERADE #add these for IRC and FTP $mod ip_nat_ftp $mod ip_nat_irc $mod ip_conntrack_ftp $mod ip_conntrack_irc # Flush all active rules and delete all custom chains $ipt -F $ipt -t nat -F $ipt -t mangle -F $ipt -X $ipt -t nat -X $ipt -t mangle -X #Set default policies $ipt -P INPUT DROP $ipt -P FORWARD DROP $ipt -P OUTPUT ACCEPT $ipt -t nat -P OUTPUT ACCEPT $ipt -t nat -P PREROUTING ACCEPT $ipt -t nat -P POSTROUTING ACCEPT $ipt -t mangle -P PREROUTING ACCEPT $ipt -t mangle -P POSTROUTING ACCEPT #this line is necessary for the loopback interface #and internal socket-based services to work correctly $ipt -A INPUT -i lo -j ACCEPT #Enable IP masquerading $ipt -t nat -A POSTROUTING -o $WAN_IFACE -j MASQUERADE #Enable unrestricted outgoing traffic, incoming #is restricted to locally-initiated sessions only $ipt -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT $ipt -A FORWARD -i $WAN_IFACE -o $LAN_IFACE -m state --state ESTABLISHED,RELATED -j ACCEPT $ipt -A FORWARD -i $LAN_IFACE -o $WAN_IFACE -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT # Allow ssh access from LAN, but not WAN $ipt -A INPUT -i $LAN_IFACE -p tcp -s 192.168.1.0/24 --dport 22 --syn -m state --state NEW -j ACCEPT # Allow DNS access from LAN $ipt -A INPUT -i $LAN_IFACE -p udp -s 192.168.1.0/24 --dport 53 -j ACCEPT $ipt -A INPUT -i $LAN_IFACE -p tcp -s 192.168.1.0/24 --dport 53 -j ACCEPT # Allow DHCP access from LAN $ipt -A INPUT -i $LAN_IFACE -p udp --dport 67 -j ACCEPT $ipt -A INPUT -i $LAN_IFACE -p udp --dport 68 -j ACCEPT # Accept important ICMP messages $ipt -A INPUT -p icmp --icmp-type echo-request -j ACCEPT $ipt -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT $ipt -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT #Reject connection attempts not initiated from inside the LAN $ipt -A INPUT -p tcp --syn -j DROP # XXX: what about dropping unwanted UDP?
Let's call that from our /etc/rc.local script:
#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. /etc/iptables_nat_router.sh exit 0
That should do it.
dnsmasq configuration
Now let's configure dnsmasq. First, back up the original file (it's fucking huge).
root@ubunturouter:/etc# cp dnsmasq.conf dnsmasq.conf.orig
and lay this down as the new /etc/dnsmasq.conf:
domain-needed bogus-priv local=/lan/ expand-hosts domain=lan interface=eth0 listen-address=127.0.0.1 # upstream nameservers server=68.87.71.226 server=68.87.73.242 server=68.87.64.146 dhcp-range=lan,192.168.1.100,192.168.1.149,12h dhcp-lease-max=100
I must confess to having scp'd that from my old router; so much easier!
Configure LAN port (eth0)
Looks like the only thing to do now is configure eth0 to come up as my LAN port, and all should be good!
Looks like the file to configure is /etc/network/interfaces, and that this should do it:
# This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). # The loopback network interface auto lo iface lo inet loopback # WAN interface auto eth1 iface eth1 inet dhcp # LAN interface auto eth0 iface eth0 inet static address 192.168.1.1 netmask 255.255.255.0 network 192.168.1.0 broadcast 192.168.1.255
OK. Time to plug in our router and see if the ethernet cards come up OK and if the firewall comes up OK. Then, we can start up dnsmasq and see if that's happy.
(Note: shut down VOIP box; shut down old router; plugged in new router; recycled cable modem; brought up new router.)
Well, shit, it started up just fine! And host lookup to yahoo.com worked.
Start dnsmasq
So now let's start dnsmasq so that I can have dns lookups from my LAN clients:
root@ubunturouter:~# /etc/init.d/dnsmasq start
Well, shit, that worked fine!
Let's make that change permanent:
root@ubunturouter:~# sysv-rc-conf --level 2 dnsmasq on root@ubunturouter:~# sysv-rc-conf --list dnsmasq dnsmasq 1:off 2:on 3:on 4:on 5:on
Woot!
Fix router hostname
My old router was just called router, so let's change the hostname from ubunturouter to router:
root@ubunturouter:/etc# hostname router
and put 'router' in /etc/hostname so that it's there for reboot.
Install ntpd
Install the ntp daemon so that our clocks are correct:
root@router:~# apt-get install ntp
That worked well.
Configure logwatch
Let's configure logwatch.
Ensure cron is installed, and python, and libgmail, and logwatch.
copy /usr/share/logwatch/default.conf/logwatch.conf to /etc/logwatch/conf/logwatch.conf
make these settings in /etc/logwatch/conf/logwatch.conf:
#MailTo = # notice how this is commented out #MailFrom = Logwatch # notice how this is commented out #Print = # notice how this is commented out too Save = /tmp/logwatch.txt # notice how this is not commented out #TmpDir = /var/cache/logwatch TmpDir = /tmp
Everything else should already be in place for cron to launch logwatch daily.
Now let's set up a cron job to check for package updates.
PLEASE NOTE the bug noted at https://bugs.launchpad.net/ubuntu/+source/debianutils/+bug/38022 and described at https://help.ubuntu.com/community/CronHowto says your shell scripts in /etc/cron.daily cannot have periods in their names! I guess cron is hooked up to something called run-parts, which will silently fail instead of running the scripts with periods in their names. That is therefore reflected in the names I have chosen below.
Create the file /etc/cron.daily/98_check_for_package_updates:
#!/bin/sh apt-get update > /dev/null 2>&1 apt-get -s upgrade > /tmp/check_for_package_updates.txt 2>&1
root@router:/etc/cron.daily# chmod +x 98_check_for_package_updates
create /etc/cron.daily/99_email (named 99* because jobs in cron.daily are executed in alphabetical order and we need e-mail to be sent AFTER logwatch and check yum check have run):
#!/usr/bin/python import libgmail f = open('/tmp/logwatch.txt', mode='rt') logwatch = f.read() f.close() f = open('/tmp/check_for_package_updates.txt', mode='rt') check_for_package_updates = f.read() f.close() message = logwatch + '\n\n' + check_for_package_updates ga = libgmail.GmailAccount('my.gmail.username', 'mypassword') ga.login() gmsg = libgmail.GmailComposedMessage('my.gmail.username@gmail.com', 'router logwatch', message) ga.sendMessage(gmsg)
root@router:/etc/cron.daily# chmod +x 99_e-mail
Done!