So, you want to set up a linux router?

Welcome to the second iteration of this page.  The first iteration is here, however it won't be as useful.

The motivation for the existence of this page is to provide others a quick and easy, as well as provide myself a resource where I can rebuild my firewall if necessary.

Friday May 17th, 2002.  I've spent the past week or so iteratively rebuilding my firewall, improving security and general functionality.


Since each individual requirement can differ greatly, I'll first describe my scenario, then get into the nitty gritty of the solution.

I currently have DSL line with 4 fixed IP addresses. I've mapped all four to individual IP addresses that live internally on the LAN.  I use one of them for outbound communication from the LAN.

My requirements then were as follows:

  • Route incoming requests for my web sites to the server sitting behind the firewall for news, ftp, http, and https.
  • Route outgoing requests from internal machines through the one of the four IP addresses.
  • Run a DNS server on the router.
  • Provide DHCP support for all clients that aren't web servers (which are setup with fixed IP addresses).
  • Block all other traffic.


    My Hardware configuration

    The router is set up with two network cards, the first net card (eth1) is connected to the wonderful world of the internet (or it will be once I'm done configuring it). It has all four of the IP addresses that I want it to receive on. The second network card (eth0) runs my internal network and has the address 192.168.0.1, with a netmask of 255.255.255.0. More on how I set up the network cards below.


    Installation of Linux

    I used my existing connection to download the install information for RedHat 7.3 to my web server using an existing external IP address (no firewall yet).  I then made it available on one of the FTP sites for download as part of the RedHat setup since the firewall would be in the middle and not processing any packets while installing.  I then retrieved the most recent kernel version at the time (2.4.18-4), as well as the iptables source. Note that when getting the new kernel, put the .tar.gz file in /usr/src, delete the link from linux to what the current kernel version is, then extract the data as follows:

  • gzip -d linux-<kernel version>.tar.gz
  • tar -xvf linux-<kernel version>.tar

    This will create a new linux directory (it would have overwritten what you already had, and we don't want that). Rename the directory to linux-<kernel version> and recreate the linux link by using the commands:

  • mv linux linux-<kernel version>
  • ln -s linux linux-<kernel version> -- check the order of linux and linux- not sure

    Note that for 2.4 kernels you may need to ensure that there is a linux-2.4 soft link to the new source dir.  This maintains consistency if you want to upgrade later.

    I built the kernel with the following settings:

  • cd /usr/src/linux
  • make menuconfig
  • make dep ; make clean ; make bzImage
  • cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz-<kernel version>
  • make modules ; make modules_install

    Then lilo needs to be configured to recognize the new kernel. Edit the /etc/lilo.conf file and add the following:

  • You can look at my current lilo.conf file here. You can see where I added the new entry. I changed the old entry to be called "oldlinux" and set the new one to be called "linux"
  • Then run lilo to set the configuration when you boot.


    Configuring the network adapters

    You can do this linuxconf in RedHat, just be careful, since the wrong key will kill linuxconf. Here are copies of my eth0 and eth1 configuration. In RedHat these belong in /etc/sysconfig/network-scripts. If you look in ifcfg-eth1, (the link for eth1), you'll notice there isn't any specification for the additional IP addresses above. To configure those, the file ifcfg-eth1-range0 is needed in the same location.

    All other properties it inherits from ifcfg-eth1. Note that this doesn't (shouldn't) work if your primary device is a DHCP client (your IP address is automatically assigned to you provider, not fixed IP addresses in my case).  I did run into a problem where it wanted me to have a DEVICE line in the ifcfg-eth1-range0 file.  I modified the ifupaliases script in the same directory to pickup the DEVICE line from the parent (ifcfg-eth1).


    Configuring DHCP

    Make sure the dhcpd daemon is running on the ethernet adapter for the internal network (such as dhcpd eth0 in my configuration).

  • dhcpd.leases is required for dhcpd to run. It stores the IP addresses that are doled out to other computers and is persistent across reboots: touch /var/state/dhcp/dhcpd.leases
  • dhcpd in /etc/rc.d/rc.init contains the script for initializing dhcpd. We want it to run only on eth1, so edit /etc/rc.d/rc.init/dhcpd and change the line "/usr/sbin/dhcpd" to "/usr/sbin/dhcpd eth0".
  • A line needs to be added to the hosts file that looks like '255.255.255.255 dhcp'. This is the broadcast address for dhcp, and what will usually happen without this is it will be ANDed with the netcard address. We don't want that, we want it to go out "as is."
  • A route needs to be added for the host 'dhcp' so that all dhcp processing occurs only on eth1: 'route add -host dhcp dev eth0' This tells the routing system to route dhcp packets through eth0. This will allow Winboxes to work with the dhcp daemon. I have this being done in my firewall script (more on this below...).  I have encountered one issue that is yet to be resolved for dhcp.  I can get Winboxes to acquire a new lease if they don't have one (in which case the source address they use is 0.0.0.0).  However, if they already have an address (such as from a previous lease) that they are trying to renew, the request fails.  I have not isolated the cause of the problem yet.  Any information would be greatly appreciated.
  • Finally, we have to give it the range of IP addresses it will manage. Since my eth1 adapter is using 192.168.0.1 (netmask 255.255.255.0), I'll be providing IP addresses in the range of 192.168.0.64 to 192.168.0.127. You can sample the dhcpd.conf. This file belongs in /etc.

    The various entries in the dhcpd.conf are briefly described here:
    Line Description
    subnet 192.168.0.0 netmask 255.255.255.0 { This defines what network the dhcp daemon will manage, in this case, 192.168.0.0 with a network mask of 255.255.255.0.
    The brace indicates a beginning of scope for this subnet. You can actually set up multiple subnets on the same network adapter, or for different network adapters. This allows setting specific configurations for each.
    range 192.168.0.64 192.168.0.127; This specifies the range for the above subnet. It has to fit within the subnet specified. I'll explain why I chose this range later.
    default-lease-time 43200;
    max-lease-time 604800;
    This specifies how long a lease will live for when it is provided to a client computer. A client computer then is required to renew their lease after it has expired.
    option routers 192.168.0.1; This tells where the client computers receiving IP addresses to go when they don't know where to send packets. For example, if I want to hit linuxrouter.org, I would find out its IP address. Since that IP address isn't in my subnet, the request for linuxrouter.org would be sent to the router for it to forward on.
    option broadcast-address 192.168.0.255; I don't know much about this option, other than it makes it work <grin>
    option domain-name-servers 192.168.0.1; This will automatically configure the clients to use the router as a DNS server, too.
    option subnet-mask 255.255.255.0; Tells the client the scope of the subnet it lives on.
    } The end brace ends the scope for this subnet.


    Configuring DNS

    DNS uses the BIND package, you will want to make sure you have it installed. In particular, make sure you have a very recent version. The BIND package is a popular choice of hackers for acquiring control of your machine.

    By default, starting DNS ('named start') will cause it to listen on all available IP addresses on all devices. This is really convenient. You can use linuxconf to add domain names to the names you control. These will be names you have registered that are set up to point at your machine for resolving names for your domain.

    You will also probably want your ISP to host your secondary. If your connection to the ISP fails for some reason, at least people will still get the correct IP address, and, since they control the reverse lookup table for your IP address, they can set up the reverse lookup to correctly point to your domain instead of something meaningless.

    Then you will want to set up your DNS to propagate to your secondary. I haven't done this yet, but speculation would suggest just indicating the IP address in linuxconf.

    Finally, you will want to set up forwarder addresses in DNS. These are addresses of other DNS servers 'close' to you that might be faster to query for the IP address desired than necessarily going to one of the top level domain servers and working down to the desired name server for the domain being queried.

    Some other notes:

  • You will want to ensure host lookup occurs in the order hosts, then bind (the name server). This information is stored in /etc/host.conf.
  • You will want to remove some references to NIS servers (I don't know much about them) in the /etc/nsswitch.conf. In particular the line starting with 'hosts:'. Just remove the 'nis' and 'nisplus' references. Leaving the other references for nis and nisplus for other services does not seem to cause a problem on my machine.
  • /etc/resolv.conf should point to your local machine as the nameserver (local or 'lo' is almost always 127.0.0.1 for an IP address). DNS on your machine should automatically go to the other domain servers as necessary and cache requests for you. The search command is for domains to search if the address isn't found for what was typed.

    A 'sample' /etc/named.conf can be looked at here. For more info, you should look at the HOWTO for DNS (link above).


    IP Tables & Rules

    You need to download the source for this from The NetFilter homepage. Please keep in mind the minimum requirements for building IP Tables.

    I've created a number of quick scripts to make my life easier. All they do is take some arguments and use them with commonly used settings for NAT (network address translation). They were created as separate scripts so I could adjust them on the fly, rerun them, and have the new settings take effect immediately. The paths specified are where I decided to place these commands.

  • /firewall/inNAT takes a destination address and maps it to the real internal address that hosts the information. For example, if I had packets destined for 216.254.8.9, I could NAT them to 192.168.0.9 using 'inNAT 216.254.8.9 192.168.0.9'.  I have extended this to allow specifying port information as well.  Check out the file comments for the details.
  • /firewall/config has the configuration information that directs which interface is internal and which is external, ports and IP addresses to map, etc.  Changes such as getting a new IP address can much more simply be changed by making relevant modifications here.
  • /firewall/firewall is where most of the work is.  This initially flushes all of the tables to allow submitting changes really easy by rerunning the script.  Then it sets the policy on the NAT PREROUTING table to DROP.  This ensures that any packets are dropped unless they are explicitly allowed.  The previous incarnation of these scripts had the policy set on INPUT and FORWARD instead, but this simplifies the amount of work required, and the scope is greater (and less chance of making mistakes).  From there I set up some DHCP rules to ensure DHCP works (with the above caveat).  Then I institute some basic reverse packet filtering.  So if I get packets from 192.168.* on my external interface, I know immediately that I should drop them since they are bogus.  From there add support for DNS lookups.  Add the rules for mapping external ports to the internal web server ports.  Then allow all internal to outside.  Then I finally turn on forwarding since I want to minimize hack potential while booting up.

    This setup allows me to access my web servers from anywhere on my internal net using their "outside" address. So if I query www.oneoddsock.com from inside my network, I will get to it properly. However, this does not work from the firewall box itself. Take note of this when testing your configuration.

    Finally, you need to run all these handy commands from somewhere, such as /etc/rc.d/rc.local.


    Disclaimers

    This was put together as a quick guide as well as a store of information if I ever had my linux box compromised and had to rebuild it. It was also intended for anyone who was trying to do something similar. Why didn't I use Win2K or NT? I needed NAT, and I needed to run it on a machine that is a P133 with 32MB of RAM. WinNT and Win2K have lots of cool features, but they are both pigs. I am not responsible for the design decisions that put them in that state, though I sorely wish to do something about it. So many possibilities there...

    You can email me at my SpeakEasy address if you are having problems, and if I can't solve it, consider subscribing to the netfilter listserv at netfilter@lists.samba.org. The process is described on the NetFilter page.


    If you have comments, suggestions or questions, mail me at:
    bscriver@speakeasy.org