Build a network router and firewall with Fedora 22 and systemd-networkd

One of my favorite features of Fedora 22 is systemd-networkd and all of the new features that came with it in recent systemd versions. The configuration files are easy to read, bridging is simple, and tunnels are resilient.

I’ve recently started using a small Linux server at home again as a network router and firewall. However, I used systemd-networkd this time and had some great results. Let’s get started!

Overview

Our example router in this example has two network interfaces:

  • eth0: public internet connectivity
  • eth1: private LAN (192.168.3.1/24)

We want machines on the private LAN to route their traffic through the router to the public internet via NAT. Also, we want clients on the LAN to get their IP addresses assigned automatically.

Network configuration

All of the systemd-networkd configuration files live within

/etc/systemd/network

and we need to create that directory:

mkdir /etc/systemd/network

We need to write a network configuration file for our public interface that systemd-networkd can read. Open up

/etc/systemd/network/eth0.network

and write these lines:

[Match]
Name=eth0

[Network]
Address=PUBLIC_IP_ADDRESS/CIDR
Gateway=GATEWAY
DNS=8.8.8.8
DNS=8.8.4.4
IPForward=yes

If we break this configuration file down, we’re telling systemd-networkd to apply this configuration to any devices that are called

eth0

. Also, we’re specifying a public IP address and CIDR mask (like /24 or /22) so that the interface can be configured. The gateway address will be added to the routing table. We’ve also provided DNS servers to use with systemd-resolved (more on that later).

I added

IPForward=yes

so that systemd-networkd will automatically enable forwarding for the interface via sysctl. (That always seems to be the step I forget when I build a Linux router.)

Let’s do the same for our LAN interface. Create this configuration file and store it as

/etc/systemd/network/eth1.network

:

[Match]
Name=eth1

[Network]
Address=192.168.3.1/24
IPForward=yes

We don’t need to specify a gateway address here because this interface will be the gateway for the LAN.

Prepare the services

If we’re planning to use systemd-networkd, we need to ensure that it runs instead of traditional network scripts or NetworkManager:

systemctl disable network
systemctl disable NetworkManager
systemctl enable systemd-networkd

Also, let’s be sure to use systemd-resolved to handle our

/etc/resolv.conf

:

systemctl enable systemd-resolved
systemctl start systemd-resolved
rm -f /etc/resolv.conf
ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

Reboot

We’re now set to reboot! It’s possible to bring up systemd-networkd without rebooting but I’d rather verify with a reboot now than get goosed with a broken network after a reboot later.

Once your router is back up, run

networkctl

and verify that you have routable in the output for both interfaces:

[root@router ~]# networkctl
IDX LINK             TYPE               OPERATIONAL SETUP     
  1 lo               loopback           carrier     unmanaged 
  2 eth0             ether              routable    configured
  3 eth1             ether              routable    configured

DHCP

Now that both network interfaces are online, we need something to tell our clients about the IP configuration they should be using. There are plenty of good options here, but I prefer dnsmasq. It has served me well over the years and it provides some handy features along with DHCP, such as DNS caching, TFTP and IPv6 router announcements.

Let’s install dnsmasq and enable it at boot:

dnf -y install dnsmasq
systemctl enable dnsmasq

Open

/etc/dnsmasq.conf

in your favorite text editor and edit a few lines:

  • Uncomment
    dhcp-authoritative
  • This tells dnsmasq that it’s the exclusive DHCP server on the network and that it should answer all requests
  • Uncomment
    interface=

    and add

    eth1

    on the end (should look like

    interface=eth1

    when you’re done)

  • Most ISP’s filter DHCP replies on their public networks, but we don’t want to take chances here. We need to restrict DHCP to our public interface only.
  • Look for the
    dhcp-range

    line and change it to

    dhcp-range=192.168.3.50,192.168.3.150,12h
  • We’re giving clients 12 hour leases on 192.168.3.0/24

Save the file and start dnsmasq:

systemctl start dnsmasq

Firewall

We’re almost done! Now it’s time to tell iptables to masquerade any packets from our LAN to the internet. But wait, it’s 2015 and we have tools like

firewall-cmd

to do that for us in Fedora.

Let’s enable masquerading, allow DNS, and allow DHCP traffic. We can then make the state permanent:

firewall-cmd --add-masquerade
firewall-cmd --add-service=dns --add-service=dhcp
firewall-cmd --runtime-to-permanent

Testing

Put a client machine on your LAN network and you should be able to ping some public sites from the client:

[root@client ~]# ping -c 4 icanhazip.com
PING icanhazip.com (104.238.141.75) 56(84) bytes of data.
64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=1 ttl=52 time=69.8 ms
64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=2 ttl=52 time=69.7 ms
64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=3 ttl=52 time=69.6 ms
64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=4 ttl=52 time=69.7 ms

--- icanhazip.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 69.659/69.758/69.874/0.203 ms

Extras

If you need to adjust your network configuration, just run

systemctl restart systemd-networkd

afterwards. I’ve found that it’s quite intelligent about the network devices and it won’t reconfigure anything that hasn’t changed.

The

networkctl

command is very powerful. Check out the

status

and

lldp

functions to get more information about your network devices and the networks they’re connected to.

When something goes wrong, look in your systemd journal:

[root@router ~]# journalctl -u systemd-networkd
-- Logs begin at Fri 2015-07-31 01:22:38 UTC, end at Fri 2015-07-31 02:11:24 UTC. --
Jul 31 01:46:14 router systemd[1]: Starting Network Service...
Jul 31 01:46:14 router systemd-networkd[286]: Enumeration completed
Jul 31 01:46:14 router systemd[1]: Started Network Service.
Jul 31 01:46:15 router systemd-networkd[286]: eth1            : link configured
Jul 31 01:46:15 router systemd-networkd[286]: eth0            : gained carrier
Jul 31 01:46:15 router systemd-networkd[286]: eth0            : link configured
Jul 31 01:46:16 router systemd-networkd[286]: eth1            : gained carrier
Fedora Project community

31 Comments

  1. Kostic

    Great article! I think that Fedora router will be my next project.

    • Thanks, Kostic! Making a Linux router is fun. πŸ˜‰

      • Major Hayden, I ran through your procedures and it went flawless. Great article. However, I did notice that once I ran through your steps (I used 192.168.1.1 as my test interal nic), I could not change that IP once I took the router to its operational location. If I change that IP to, say 192.168.1.233, it no longer NATs for the client computers. Any thought?

  2. Nice guide, can this be extended to support multi ISP’s ?
    and have some balancing and fail-over options ?

    Thanks,

    • Rabin — You could do multiple uplinks via some scripts. For example, you could try pinging the gateway addresses on both links and run scripts when one of them stops pinging for a certain amount of time. I’m not aware of anything built into systemd-networkd to do that, though.

      • Forgot to close HTML tag in previous reply:

        systemd.netdevhas it listed under “Supported kinds of virtual network devices” and I believe it is just what he needs.

  3. anon

    How did you get a network interface with the old naming style, i.e. eth0? Does it persist across reboots?

  4. Great write up! It’d be awesome to see a follow up with multiple ISP links (having one internal network load balanced across multiple WANs, or multiple internal networks using PBR between multiple providers).

    Excellent primer on configuring a basic home router and firewall!

  5. Sonny

    Bookmarked!
    Thanks, very helpful

  6. Roger

    This would be su interesting project with the Banana Pl router. Are VLANs also supported here?

  7. A user over on the G+ posting for this post asked an interesting question too — is there a webGUI availble to control systemd-networkd?

    https://plus.google.com/+fedora/posts/7mkPcgj6UPS

    • I’m not aware of any GUI’s of any sort for systemd-networkd. There’s always room for one! πŸ™‚

  8. Lee Yates

    Great article, thanks! I’ve tinkered with pfSense, IPFire and others in the past and am currently speccing an AM1 (Kabini) build as a firewall/router appliance for our home network (300Mbps WAN). Judging by your article setting up a router on Fedora is now even easier than setting up pfSense with a GUI! It would be fairly trivial to then set up openvpn and iptables to also have the box function as a VPN gateway (with network split across LAN interfaces if necessary). Nice! Thanks for giving me something else to tinker with. πŸ™‚

  9. PaweΕ‚

    Nice article. I really like how Fedora Magazine adds useful tutorials alongside news. Please keep them coming! πŸ™‚

  10. Cameron

    Great article, I have just tried to to follow this on the newly released clean install of fc23.

    I however run into an issue where, /etc/resolv.conf pointing to /run/systemd/resolve/resolv.conf would create a permission issue for dnsmasq.

    Many-thanks.

    It would also be great to have a guide on how to do IPv6 routing in the full systemd world.

    • Hey Cameron,

      There’s a problem with the latest SELinux policies and they don’t allow dnsmasq to read /etc/resolv.conf (which is linked to /run/systemd/resolve/resolv.conf). I’m planning to get a bug open today to get it fixed.

      Major

      • Andrew

        I’m seeing this too. It seems dnsmasq wants to write to /etc/resolv.conf. It also wants to set an inotify which may take execute permissions. I’m not sure of that. This is FC 22.

        • Andrew

          If I start dnsmasq from the command prompt: dnsmasq -q -d

          Then it works. I’ve confirmed by watching a DHCPREQUEST work.

          • MichaΕ‚ Kowalcze

            Check your /lib/systemd/system/dnsmasq.service file and change

            After=network.target
            to:
            After=systemd-resolved.service network.target

            It helped in my case.
            If works – perhaps main guide could be updated to include this change?

  11. TonyBoston

    Hi and thanks for the great Post.
    Anyway, I ran into an issue here. After setting up the services and reboot the machine, theres only an ems3 interface and I cant see eth0-eth2 anywhere. I am running F23 server in a virtual machine.
    Do you have any advice on that one?
    Thanks,
    Tony

    • @TonyBoston: You’ll want to add another network device to the virtual machine using the manager, so you can experiment with two network interfaces. The naming is due to the virtual hardware being detected. You should be able to use the same steps and substitute the names provided on your VM.

  12. Andrew

    You might need to tweak your startup order. I added to dnsmasq.service:

    [Unit]
    After=network.target

    So far I haven’t had any more problems with dnsmasq starting up before the resolve.conf file is written.

  13. Francisco Franco

    Thanks for a great write up. I used it for an Arch Wifi Router project and your systemd-networkd instructions are more accurate than Arch’s own Router and Software Access Point documentation. I even setup webmin for remote administration.
    AWESOME

  14. For those of you having trouble with the resolve.conf file, I found a solution that works great.

    Open up your /etc/dnsmasq.conf file, and file and find the following line:

    #server=/localnet/192.168.0.1

    Below that line, add the following values, of course adjusting the IP addresses to your needs. I will be using google dns.

    server=8.8.8.8
    server=8.8.4.4

    Simply save the file, and reboot your router, and you should not have any troubles. I’ve left my resolve.conf file with those settings as well, but in the event there are issues with dnsmasq looking for that file, you’ve now hard coded the dns entries in the dnsmasq daemon.

    Cheers

  15. Sam Mingo

    Any ideas where you’re suppose to stash policy routing rules that you’d typically create with ip rule add … in systemd and/or systemd-networkd world?

  16. Gabriel

    I have a modem configured in bridged mode by my ISP, how can I configure fedora to use my public static IP?

  17. Kent Brown

    Is there a way to do this while having DHCP on a different server?

Comments are Closed

The opinions expressed on this website are those of each author, not of the author's employer or of Red Hat. Fedora Magazine aspires to publish all content under a Creative Commons license but may not be able to do so in all cases. You are responsible for ensuring that you have the necessary permission to reuse any work on this site. The Fedora logo is a trademark of Red Hat, Inc. Terms and Conditions