Linux router firewall configuration with NetworkManager and firewalld

Introduction

I use Linux as an Internet router. I use it with multiple rules (rather simple) using a very simple baseline configuration. The router has two main network interfaces – one facing the internal LAN, and one facing the external fibre connection, where it needs to use a specific VLAN, and then – use PPPoE to have a connection.

I used NetworkManager and firewalld solely to achieve this configuration. In the past – I used custom ifup/ifdown scripts, and iptables rules. Since I rebuilt everything, I wanted to use the out-of-the-box tools to achieve this configuration.

In this guide I will show how to create a Linux router and firewall configuration with NetworkManager and firewalld

Prerequisites

  • Linux distribution with NetworkManager and firewalld (I used OracleLinux 9)
  • Two network interfaces (one for LAN, one for external connection)
  • Basic understanding of networking concepts
  • Root or sudo access

In my set enp1s0 is the LAN interface. enp2s0 is the fibre-facing interface (which carries a virtual VLAN-tagged interface, which carries a PPPoE dial-up virtual interface).

Network Interfaces Configuration using NetworkManager

LAN (internal) interface configuration

The ‘LAN’ configuration is fairly straight forward. A manual network connection with a manual IP address. No default gateway, of course. A simple nmcli command can configure the interface:

nmcli connection add type ethernet con-name lan ifname enp1s0
nmcli connection modify lan ipv4.method manual ipv6.method disabled \
  ipv4.addresses 192.168.0.1/24 ipv4.dns 192.168.0.1 autoconnect yes

The IP is 192.168.0.1, the interface is enp1s0, and the name is ‘lan’, which will appear in ‘ip address’ command.

Fibre-facing external interface – VLAN tagged

nmcli c  add type vlan con-name external ifname external vlan.id 100 dev enp2s0 autoconnect yes ipv4.method disabled ipv6.method disabled

Note the important variables: the VLAN ID and the network device.

Internet dial-up (PPPoE) interface

Now we can configure the PPP interface, which requires the VLAN-tagged interface:

nmcli connection add type pppoe con-name internet connection.interface-name ppp0 pppoe.parent external pppoe.username username pppoe.password password ppp.noauth yes

Note the important variables: interface-name (ppp0 should be fine for PPPoE), pppoe.parent, which is the VLAN-capable interface, username and passowrds.

In theory – we have all the required interfaces functioning. At this stage, you should have a working Internet connection from the Linux router to the outside (try pinging 8.8.8.8 for example) and internal network.

It’s time to put some efforts around the firewall.

Firewall configuration using Firewalld

I made sure to use several limited number of zones. Let’s take a quick look on the goal:

  1. Zone “internal” for all internal communication (LAN)
  2. Zone “public” for router-to-VLAN access, and for all interfaces not configured.
  3. Zone “external” for Internet to router (meaning – on our PPP0 device)

Internal LAN rule (free access to all)

Let’s create the internal rule:

firewall-cmd --permanent --new-zone=internal
firewall-cmd --permanent --zone=internal --set-target=ACCEPT

We would like the LAN interface to be member of this zone, so we would modify it accordingly:

nmcli connection modify lan connection.zone internal

External (from Internet) access – Block all but SSH

Let’s create the internet (external ) zone:

firewall-cmd --permanent --new-zone=internet
firewall-cmd --permanent --zone=internet --add-service=ssh
firewall-cmd --permanent --zone=internet --add-masquerade --add-forward 

I want to be able to access from the Internet via SSH. We need to add the “internet” interface to the correct zone automatically as well, so we should run:

nmcli connection modify internet connection.zone internet

Creating the cross-zone policy to allow traffic forwarding

To conclude all this, we need to create a policy which will allow us the NAT from the ‘lan’ to the ‘internet’. We would do it like this:

firewall-cmd --permanent --new-policy internet-access
firewall-cmd --permanent --policy internet-access --set-target ACCEPT
firewall-cmd --permanent --policy internet-access --add-ingress-zone internal
firewall-cmd --permanent --policy internet-access --add-egress-zone internet
firewall-cmd --permanent --policy internet-access --add-rich-rule 'rule tcp-mss-clamp value="pmtu"'

We create a policy to allow routing of packets between two zones (each configured with a dedicated interface), with ingress zone (internal) and egress zone (external), and with MSS rule to solve an interesting issue I’ve had with blocked PMTU discovery. Without this last rule, sometimes large packets would break, and leave you with partially-working setup.

We should also add the VLAN interface to ‘public’ zone, which has a sensible default. You can modify this zone’s ruleset and allowed services, but this will not be part of this guide.

nmcli connection modify external connection.zone public

Finalising and applying

To finish our configuration, we should apply all the settings enabled for firewalld, which were only saved on-disk, by running the following command:

firewall-cmd --reload

We should also bring all our interfaces up, to apply their firewall membership (since the zone did not exist up until now), so running

nmcli connection up lan
nmcli connection up external
nmcli connection up internet

should do the trick.

Blocking specific LAN IP addresses from accessing the Internet

The above mentioned configuration and setup guide would walk you through the process, and the result, if working as expected, is that all the LAN IP addresses will have Internet access through your Linux router. If this is the desirable result, you should stop here, and jump to the conclusion below.

If there are some specific IP addresses you would like to prevent from accessing the Internet – here is the method and explanation on how to do it. There might be some printer, or smart-home device you want to block, and for that – you need the device’s IP address.

We can tag specific source IP addresses into a dedicated zone. If we block these IP addresses access using this zone, they will be blocked from accessing the router, which might not be the desirable result. In my case – the router acts as DNS as well, and I do not want to block this service to these sources.

So I ‘match’ the sources to a new zone, with regular LAN access ruleset, except that there is an additional policy to manage this zone’s access to the external zone.

Let’s create a dedicated zone for this purpose:

firewall-cmd --permanent --new-zone=special-devices
firewall-cmd --permanent --zone=special-devices --add-source=192.168.0.100/32 # Adding a source
firewall-cmd --permanent --new-policy blocked-internet-access
firewall-cmd --permanent --policy blocked-internet-access --set-target REJECT
firewall-cmd --permanent --policy blocked-internet-access --add-ingress-zone special-devices
firewall-cmd --permanent --policy blocked-internet-access --add-egress-zone internet

Note the IP source. You may repeat this line multiple times for each IP address, or create an ipset (not discussed here) for this purpose.

I can share with you that this policy is a competing policy to the internet-access policy. In my case – it did not function (after applying, of course), but enforcing a low (better) priority on it did the trick:

firewall-cmd --permanent --policy=blocked-internet-access --set-priority=-2

And to apply all rules – reload firewalld again:

firewall-cmd --reload

Test this with a test IP address and verify that the functionality is as-expected.

Conclusion

If you followed this guide, and all worked well, you should have a working Linux router. It does not include DHCP/DNS configuration (maybe some other time), but it will route IP communication from a LAN to WAN and to the Internet, using NAT, and in a safe and controlled environment.

I wanted to write down this guide a while now, however – couldn’t find the required time for that. I wanted to do so today because I added the IP blocking mechanism (has an issue with my kids – and I can now block their smart devices using cron jobs) so I wanted to document everything while it was fresh in my head. The other parts, however, are based on memory, shell history (most of it is long gone) and some minor trial and error on my system. Since I didn’t want to break down my own Internet connection – only some of it is tested – and even so – not to the full extend.

Drop me a line if something’s not working as expected, or if you encountered an issue. I might try to setup a lab to test this configuration in a controlled environment. Only requires some time.

I hope that this guide did help you and made things very clear. Thank you for reading so far down the rabbit hole 🙂

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.