Building a Linux home server, gateway, firewall & router

Overview

This guide will attempt to show you how to build your own home server. I’ve included sections that I believe would be useful to home users, without over-complicating things. I’ve tried to make this guide accessible enough for people who are new to Linux, too.

We will assume that the core function of this server will be a gateway/firewall/router for your home network. If this is what you want, read on. However, if you wanted to build just a regular web/file/etc server, don’t follow this guide – it only works if the server is acting as a gateway for your LAN.

The goal of building a gateway/firewall/router relies upon a handful of other functions, such as DHCP and DNS. At the end of this article, there are also several optional extra features that you can add later.

Hardware

So, you’ve decided to build a home Linux server. What hardware do you need? Well, let’s look at each type of resource in turn:

Processor

Literally any processor will do for most applications. Even if you’re using your Internet connection heavily, the CPU will not be pushed at all. The only task in the list above that will even begin to push your CPU might be running some sort of PHP website.

Memory

Again, for a simple home server you don’t need a lot of RAM. My fully loaded server at home is currently using 309MB of its memory. For a basic server setup you could get away with 256MB but I’d recommend 512MB or more if you’re going to run a web server. The more you have, the better, as Linux will use it for caching frequently used files.

Disks/storage

A fully loaded CentOS server will probably need less than 3GB of disk space. As memory cards are so cheap these days, you could install to a CompactFlash card for a quieter server. The only thing that might take up space is a large website, or if youdecide to run a file server.

Network

Any old network connection will do for the Internet-facing side of your server. Whatever you have, it’ll be faster than your broadband/cable connection. The important network connection is the one that serves your private network. If you’re running a file server you might prefer to have a gigabit ethernet connection.

So now you know that a Linux home server doesn’t really need a lot of welly. It’s an ideal use for an old/spare desktop PC. The main problem with using an old PC is that they are noisy, and ineffecient at using power. Most older desktop PCs use around 100W. Very, very roughly, 1W for one year comes to around £1. So that’s £100/year running costs!

Depending on your house and the server’s location, the noise of an old PC might get on your nerves. It certainly does in my house. You might like to think about using a laptop (with an additional USB network adapter) or even something smaller and quiet like a Mac Mini or an EeeBox. According to Google, a Mac Mini only uses about 23W, too.

Choosing the OS

All Linux distributions are not equal. They vary greatly and it’s impossible to say that one is “better” than another. In this guide, I will be writing about building a server that runs CentOS. CentOS is a clone of Red Hat so the instructions should work on that too, as well as the closely-related Fedora. If you’re new to Linux, my advice would be to try CentOS unless you have a reason for installing something else.

Installing the OS

This is pretty straightforward on most modern Linux distributions. Just download and burn the CD or DVD, boot from it in your new server, and follow the instructions. If your server doesn’t have a CD drive, some distributions provide images designed to boot from a USB flash disk.

The most important thing to add here is that you should install the bare minimum of packages. Untick the boxes for everything – we will add what we need later.

Don’t forget the root password that you set – you’ll need that in a minute. After the installer has done its magic, wait for the server to restart and log on using the root username and password.

Setting up your network

Before we can get much further, the new server needs an Internet connection. If you have an existing home network, plug it into that. This guide assumes that you have a cable modem with an Ethernet connection into the server.

The basic network setup
The basic network setup

This diagrams shows the basic layout of your network. The modem plugs into the server’s Ethernet port. On CentOS and related distributions, Ethernet ports are known by the system as eth0, eth1 and so on. You need to find out which port is which on your server. The best way of doing this is to simply plug the modem into whichever port takes your fancy. Assuming you already logged onto your server, simply type ipconfig at the terminal, as shown below:


[root@zeus ~]# ifconfig
eth1      Link encap:Ethernet  HWaddr 00:19:5B:5D:21:B0
inet addr:84.x.y.z  Bcast:84.255.255.255  Mask:255.0.0.0
inet6 addr: fe80::219:5bff:fe5d:21b0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:17643164 errors:0 dropped:0 overruns:0 frame:0
TX packets:16261842 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:154049552 (146.9 MiB)  TX bytes:943414355 (899.7 MiB)
Interrupt:17 Base address:0xa000

If you have two Ethernet adapters, there will be entries for eth0 and eth1. One of them should have an “inet addr”, also known as an IP address. This one is then your Internet-facing network adapter. Make a note of it! Throughout the rest of this guide, I assume that eth0 faces the private network and eth1 faces the Internet.

Just to make sure that everything is alive on your server, test the connection:

[root@zeus ~]# ping -c4 www.google.com
PING www.l.google.com (209.85.229.147) 56(84) bytes of data.
64 bytes from ww-in-f147.google.com (209.85.229.147): icmp_seq=1 ttl=244 time=27.2 ms
64 bytes from ww-in-f147.google.com (209.85.229.147): icmp_seq=2 ttl=244 time=26.5 ms
64 bytes from ww-in-f147.google.com (209.85.229.147): icmp_seq=3 ttl=244 time=26.8 ms
64 bytes from ww-in-f147.google.com (209.85.229.147): icmp_seq=4 ttl=244 time=27.3 ms

--- www.l.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 4030ms
rtt min/avg/max/mdev = 26.516/26.920/27.331/0.332 ms

If you get 4 responses, all is well. If not, go back and fix it. Yep, it’s that kind of guide 😉 Now let’s move on to…

Setting up the basics

First things first, we need to update the packages that were installing when you installed CentOS from the CD.

[root@zeus ~]# yum -y update

This could take some time, so just let yum work its magic. After this, we need to install a few more packages to perform core functions.

[root@zeus ~]# yum -y install bind bind-chroot dhcp hddtemp vim nano

Disable remote root login:

[root@zeus ~]# echo "PermitRootLogin no" >> /etc/ssh/sshd_config

I recommend you reboot after this:

[root@zeus ~]# init 6

Now we need to set up the other network connection – the one that serves your private LAN. For this we will use the private range of IP addresses – the ones that start 192.168.x.y.
For the first time since building the server, you need to use a text editor. I use vim but nano is a good one for beginners. Google for comparisons of various editors if you’re not sure. We need to edit the file the controls the private network connection. Double-check that you’ve chosen the right eth before you type this command:

[root@zeus ~]# vim /etc/sysconfig/networking/devices/ifcfg-eth0

There are a few entries in this file that must be changed – but there are also some that must be kept the same. Set the following ones to these values:


BOOTPROTO=none
IPADDR=192.168.0.1
ONBOOT=yes
NETMASK=255.255.255.0

Don’t change DEVICE, HWADDR, or TYPE. Anything else can be safely deleted. Save your changes when you’re happy. To make this change take effect, type

[root@zeus ~]# service network restart

Just to make sure it worked, try ipconfig again and make sure that eth0 has an IP address of 192.168.0.1 and eth1 has some random other IP address.

Routing & Firewall

The routing (sending traffic to the right place, either inbound or outbound) and firewalling (filtering traffic) are controlled by iptables. First we need to enable the ability to route traffic between the two network interfaces:

[root@zeus ~]# echo 1 > /proc/sys/net/ipv4/ip_forward
[root@zeus ~]# echo 1 > /proc/sys/net/ipv4/tcp_syncookies

And we also need to make sure this ability is switched on every time you start the server:

echo "echo 1 > /proc/sys/net/ipv4/ip_forward" >> /etc/rc.local
echo "echo 1 > /proc/sys/net/ipv4/tcp_syncookies" >> /etc/rc.local

Now for the tricky part. Writing an iptables config that protects the server while allowing the Internet through to the computers on your network. I will include a basic config here which should be enough to get you going. It allows all computers on your network to access the Internet, but does not allow unsolicited incoming traffic. It also open up your server to run as a web server. If this isn’t what you want, delete the two lines that mention 80 and 443 from the tcp_wan_inbound section. If you want to customise it a little, try playing with the Easy Firewall Generator for iptables.

If you use this exemplar config, open the file /etc/sysconfig/iptables for editing. Delete all the contents, and replace them with the following.

*mangle
:PREROUTING ACCEPT [30:2184]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [219:28347]
:OUTPUT ACCEPT [21:2964]
:POSTROUTING ACCEPT [427:80322]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A POSTROUTING -o eth1 -j MASQUERADE
COMMIT
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [4:912]
# DEFINE VARIOUS CHAINS
:bad_packets - [0:0]
:bad_tcp_packets - [0:0]
:icmp_packets - [0:0]
:tcp_lan_inbound - [0:0]
:tcp_wan_inbound - [0:0]
:tcp_outbound - [0:0]
:udp_lan_inbound - [0:0]
:udp_wan_inbound - [0:0]
:udp_outbound - [0:0]
:syn_flood - [0:0]
# ALLOCATE TRAFFIC TO CHAINS
-A INPUT -i lo -j ACCEPT
-A INPUT -j bad_packets
-A INPUT -d 224.0.0.1 -j DROP
-A INPUT -s 192.168.0.0/255.255.255.0 -i eth0 -j ACCEPT
-A INPUT -d 192.168.0.255 -i eth0 -j ACCEPT
-A INPUT -i eth0 -p udp -m udp --sport 68 --dport 67 -j ACCEPT
-A INPUT -i eth0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -j tcp_lan_inbound
-A INPUT -i eth1 -p tcp -j tcp_wan_inbound
-A INPUT -i eth0 -p udp -j udp_lan_inbound
-A INPUT -i eth1 -p udp -j udp_wan_inbound
-A INPUT -i eth1 -p icmp -j icmp_packets
-A INPUT -i eth1 -p tcp --syn -j syn_flood
-A INPUT -m pkttype --pkt-type broadcast -j DROP
-A FORWARD -j bad_packets
-A FORWARD -i eth0 -p tcp -j tcp_outbound
-A FORWARD -i eth0 -p udp -j udp_outbound
-A FORWARD -i eth0 -j ACCEPT
-A FORWARD -i eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p icmp -m state --state INVALID -j DROP
-A OUTPUT -s 127.0.0.1 -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -s 192.168.0.1 -j ACCEPT
-A OUTPUT -o eth0 -j ACCEPT
-A OUTPUT -o eth1 -j ACCEPT
-A bad_packets -s 192.168.0.0/255.255.255.0 -i eth1 -j DROP
-A bad_packets -m state --state INVALID -j DROP
-A bad_packets -p tcp -j bad_tcp_packets
-A bad_packets -j RETURN
-A bad_tcp_packets -i eth0 -p tcp -j RETURN
-A bad_tcp_packets -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m state --state NEW -j DROP
-A bad_tcp_packets -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
-A bad_tcp_packets -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j DROP
-A bad_tcp_packets -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -j DROP
-A bad_tcp_packets -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,ACK,URG -j DROP
-A bad_tcp_packets -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP
-A bad_tcp_packets -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -j DROP
-A bad_tcp_packets -p tcp -j RETURN
-A icmp_packets -p icmp -f -j DROP
-A icmp_packets -p icmp -m limit --limit  2/s --limit-burst 5 -j ACCEPT
-A icmp_packets -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A icmp_packets -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A icmp_packets -p icmp -j RETURN
-A tcp_wan_inbound -p tcp -m tcp --dport 80 -j ACCEPT
-A tcp_wan_inbound -p tcp -m tcp --dport 443 -j ACCEPT
-A tcp_wan_inbound -i eth1 -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
-A tcp_wan_inbound -i eth1 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --rttl --name SSH -j DROP
-A tcp_wan_inbound -p tcp -m tcp --dport 22 -j ACCEPT
-A tcp_wan_inbound -p tcp -j RETURN
-A tcp_outbound -p tcp -j ACCEPT
-A udp_lan_inbound -p udp -j RETURN
-A udp_wan_inbound -p udp -m udp --dport 137 -j DROP
-A udp_wan_inbound -p udp -m udp --dport 138 -j DROP
-A udp_wan_inbound -p udp -j RETURN
-A udp_outbound -p udp -j ACCEPT
-A syn_flood -m limit --limit 1/s --limit-burst 3 -j RETURN
-A syn_flood -j DROP
COMMIT

After you’re done inserting the firewall rules, you must apply them:

[root@zeus ~]# service iptables restart

DHCP

Let’s move on to setting up DHCP. This is a service that dynamically allocates IP addresses to other computers on your network, so they will automatically work when you connect them. Open dhcpd.conf in your editor:

[root@zeus ~]# vim /etc/dhcpd.conf

And make it look like this:

ddns-update-style interim;
ignore client-updates;
subnet 192.168.0.0 netmask 255.255.255.0 {
option routers 192.168.0.1;
option subnet-mask 255.255.255.0;
option domain-name-servers 192.168.0.1;
option ip-forwarding off;
range dynamic-bootp 192.168.0.100 192.168.0.254;
default-lease-time 21600;
max-lease-time 43200;
}

Save it, close it, and let’s start the DHCP service.

[root@zeus ~]# service dhcpd start

Assuming that all goes OK, we also need to tell DHCP to start every time you turn the server on:

[root@zeus ~]# chkconfig --level 2345 dhcpd on

DNS

Now we come to set up DNS, so the clients on your network can look up domain names (e.g. google.com) and resolve them to IP addresses (e.g. 209.85.227.147).

The config here sets up your server as a DNS caching forwarder. Every time a computer on your network looks up a domain name, the server asks the upstream DNS servers (provided by your ISP), forwards the reply to the client and remembers the answer so next time a different client asks for the same domain name, the server can respond without wasting time by referring to the upstream DNS servers.

So first, you need to know the IP address(es) of the DNS servers provided by your ISP. As your server is already online and working, it should already know at least one DNS server, and you can make it tell you like this:

[root@zeus ~]# cat /etc/resolv.conf
nameserver 83.146.21.6
nameserver 212.158.249.5

There may be one or more nameservers (DNS servers), and there may be other lines too. But all we want for now is to write down the IP addresses of all the DNS servers. We back up and edit the DNS config file to set these options:

[root@zeus ~]# cp /var/named/chroot/etc/named.conf /var/named/chroot/etc/named.conf.old
[root@zeus ~]# vim /var/named/chroot/etc/named.conf

Make it look like this, replacing my fictitious 1.1.1.1 and 2.2.2.2 with the IP addresses of your own DNS servers.

options {
listen-on port 53 { 192.168.0.1; 127.0/8; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
allow-query { 192.168.0.0/24; 127.0/8; };
recursion yes;
forwarders { 1.1.1.1; 2.2.2.2; }; #IP of upstream ISP nameservers
forward only; #rely completely on our upstream nameservers
};

When you are done, restart the service and set it to start on boot:

[root@zeus ~]# service named restart
[root@zeus ~]# chkconfig --level 2345 named on

Optional steps

So, now you should find that you are able to simply connect a computer to your private network and it should pick up an IP address and all the details of your DNS system and just work automatically on the Internet. It’s worth restarting your server too, just to make sure it starts all of the services when it loads up (otherwise you’ll wonder why there’s no Internet after a power cut!).

I have also written some guides for setting up other, more advanced features for your home server, including:

3 thoughts on “Building a Linux home server, gateway, firewall & router

  1. Hey Jonathan,
    I’m interested what your thoughts on “building a Linux home server, gateway, firewall & router” are twelve years on. I mean: it’s not just that your preference for OS and components might have changed, but everyone seems to love Onedrive or Dropbox instead of Samba, and I don’t know anyone now who uses any gateway/router/firewall apart from the one their ISP provides. The Cloud has been so embraced that on-premises servers seem not to be used even in most businesses now.
    Computing philosophy and purpose aside though, and assuming you did want to create a new one, what would you do (or what have you done) differently now? I ask you because this document is one of the clearest and most succinct on this topic, and looks easy to follow but I’m a bit late to the party and expect it will require a lot of alteration anyway to achieve the same effect today.

    Like

    1. This is a great question! I think you’re right that “most” people will use off-the-shelf technology at home. You can do so much with a standard router, a WD MyBook and a smart TV that you probably won’t need anything else.

      I still have hardware at home though. I’m using my ISP’s router but with DHCP and DNS disabled. I’m providing DHCP and DNS from a Raspberry Pi.

      For storage, I’m running an HP MicroServer with TrueNAS on it and for all my other services I’m actually running a Kubernetes cluster on 5 low-power Intel NUC nodes. This runs various websites, ownCloud for my data sync, music streaming, etc.

      I probably wouldn’t suggest anything quite so complex for most people, but my day job is actually building and running on-premise Kubernetes clusters for a cloud provider.

      I guess most of this guide would still work and be effective if you installed Ubuntu on a Raspberry Pi.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: