The Router Project — Part 4: Firewall and NAT
Feb 11, 2018
11 minute read

This is part 4 of the router series; part 1, part 2 and part 3 are also available

Contents

Conventions

I’m once again reminding you of the conventions used in this series. If you followed the guide up to now you should already know that ppp0 (or eth0 if you’re not using PPP) is the internet-facing interface, whereas eth1 is the LAN-facing one. We will use home.net as our domain and router as the hostname of the gateway.

Introduction

Everything discussed on part 3 dealt with the configuration of the interfaces on the router itself. In order to have your router actually do anything you will need to set up a functional firewall especially since the ISP has most probably shoved you behind a single IPv4 address. In order to allow the LAN clients to share this address we have to enable masquerading which performs the Address Translation (the AT in NAT) between the external address and the private addresses of the local network. In addition to that we will configure some common rules and perform some port forwarding for good measure.

Although raw iptables rules are feasible for a stateful firewall the verbosity of said rules makes them somewhat cumbersome to manage. So for this guide I am using Shorewall which is an abstraction layer above iptables. There are many other options such as nftables, ufw and FireHOL if Shorewall is not really appealing to you. In fact I might have actually tried FireHOL if I was aware of it before setting up my system with Shorewall. In any case I have found Shorewall to strike a good balance between ease of configuration, security and features.

Shorewall has separate executables for IPv4 and IPv6 built around a common core. This is why in most distributions Shorewall is split into three packages: shorewall-core, shorewall and shorewall6. There is a very comprehensive guide on their website on how to build a two-interface firewall which I recommend to read to get an understanding of what we are trying to achieve. I will illustrate some basic configuration points but these are in no case are a replacement for the proper documentation of Shorewall. The two-interface configuration is one of the most common encountered and it should be included as a guide in the Shorewall documentation package. A shorewall configuration comprises of a series of configuration files the most important of which are:

  • shorewall.conf: General Shorewall configuration (man shorewall.conf)
  • interfaces: List of involved interfaces (man shorewall-interfaces)
  • policy: Firewall policy configuration, ie. default rules (man shorewall-policy)
  • rules: Firewall rules, open ports and port forwarding (man shorewall-rules)
  • snat: SNAT configuration; used for masquerading (man shorewall-snat)
  • zones: Network zones definition; defines the conceptual zones of the network topology, such as the firewall zone (fw), the internet zone (net) or the LAN (loc) (man shorewall-zones)

There are a series of other files as well that deal with QoS and priority queues as well as finer tuning of the firewall (startup scripts etc). Since we won’t use them you can just copy the empty templates from the shorewall documentation usually found in /usr/share/doc/shorewall/Samples/two-interfaces.

IPv4 and NAT

I will focus first on the IPv4 firewall, the configuration of which resides in /etc/shorewall/.

We will start with the main configuration file shorewall.conf. This is a very long file with all the basic settings of Shorewall so I will list the values that you should at the absolute minimum edit. Shorewall provides very sane defaults for the rest.

IPv4 Main configuration file: shorewall.conf

# This must be set to Yes to allow the registration of shorewall as a boot
# service
STARTUP_ENABLED=Yes

# Level of verbosity; unless you are debugging your firewall anything else
# above 1 is overkill
VERBOSITY=1

# 7 - debug (Debug-level messages)
# 6 - info (Informational)
# 5 - notice (Normal but significant Condition)
# 4 - warning (Warning Condition)
# 3 - err (Error Condition)
# 2 - crit (Critical Conditions)
# 1 - alert (must be handled immediately)
# 0 - emerg (System is unusable)
LOG_LEVEL="info"

# This is set from sysctl.conf so tell shorewall to leave that as is
# Set it to "On" if you encounter problems.
IP_FORWARDING=Keep

IPv4 Zone definition: zones

This is the zone definition file. We define three zones the firewall itself (fw), internet (net) and LAN (loc), so go ahead and append the following. Please note that you must also keep the headers of the file intact to ensure proper parsing

#ZONE		TYPE		OPTIONS		IN_OPTIONS	OUT_OPTIONS
fw		firewall
net		ipv4
loc		ipv4

IPv4 Interface definition: interfaces

Here we assign the interface to their respective zones along with some extra options to enhance the security of the firewall. If you run a DHCP server in your network (you most definitely will) you must add the dhcp option to the interface connected to the LAN. Obviously if you are not using PPP you will need to replace ppp0 with the actual external interface (probably eth0).

#ZONE		INTERFACE		OPTIONS
net	ppp0	tcpflags,nosmurfs,routefilter,logmartians,sourceroute=0
loc	eth1	dhcp,tcpflags,nosmurfs,routefilter,logmartians

IPv4 Default policy: policy

These are the default rules of the firewall (the iptables -P equivalent). The last rule must be always present.

#SOURCE		DEST		POLICY	LOGLEVEL	RATE	CONNLIMIT
loc	net	ACCEPT
fw	net	ACCEPT
fw	loc	ACCEPT
net	all	DROP	$LOG_LEVEL

# This line must always be present
all	all	REJECT	$LOG_LEVEL

In a nutshell we allow all outbound traffic from both the LAN (loc -> net) and the router itself (fw -> net). Additionally we allow traffic flowing from the router to the LAN (fw -> loc). Finally we drop all incoming traffic (net -> all) and log it according to the LOG_LEVEL define in shorewall.conf.

IPv4 Network Address Translation: snat

SNAT is only relevant to IPv4 since we most probably only have one address available. In this particular case we transform all traffic originating from 192.168.1.0/24 (our LAN) to the address of the ppp0 interface which is the IP address assigned to us from our ISP.

#ACTION		SOURCE		DEST	PROTO	PORT	IPSEC	MARK	USER	SWITCH	ORIGDEST	PROBABILITY
MASQUERADE	192.168.1.0/24	ppp0

If you have more than one IP address and you want them to be assigned to particular unicast addresses you will have to add additional SNAT rules (not MASQUERADE). See the relevant documentation for more details on that.

IPv4 Firewall rules: rules

The final file contains the open port policy for your both your LAN hosts and the router itself. The format of the rules are either

SERVICE(ACTION)  SOURCE  DESTINATION  PROTOCOL

or

ACTION            SOURCE  DESTINATION  PROTOCOL  PORT

Here is a minimal set of annotated rules. As always there is very comprehensive documentation on the possible rule incantations on the shorewall website

#ACTION		SOURCE		DEST		PROTO	DPORT	SPORT	ORIGDEST	RATE	USER	MARK	CONNLIMIT	TIME	HEADERS	SWITCH	HELPER

# These must always be in that order
?SECTION ALL
?SECTION ESTABLISHED
?SECTION RELATED
?SECTION INVALID
?SECTION UNTRACKED

# Rules are applied to NEW connections. Already established connections are
# automatically ACCEPTed
?SECTION NEW

# Drop invalid TCP packets originating from the internet
Invalid(DROP)   net all    tcp
# Allow outbound DNS requests
DNS(ACCEPT)     $FW net
DNS(ACCEPT)     loc $FW
# Allow SSH connections from the local network into the router
SSH(ACCEPT)     loc $FW
# Same with ping
Ping(ACCEPT)    loc $FW
# And with NTP requests
NTP(ACCEPT)     loc $FW

# DROP all pings from the internet into our LAN
Ping(DROP)      net $FW
Ping(DROP)      net loc

# ACCEPT ICMP packets from the router to both the LAN and the internet
ACCEPT          $FW loc     icmp
ACCEPT          $FW net     icmp

# Port forwarding (DNAT)
# Forward incoming connections to port 12345 to the same port of host 192.168.1.6
DNAT            net loc:192.168.1.6:12345  tcp  12345
# Forward incoming connections to port 9912 to port 54321 of host 192.168.1.9
DNAT	        net loc:192.168.1.9:54321  tcp  9912

IPv6 Firewall

The IPv6 configuration lies in /etc/shorewall6 and almost everything from the IPv4 ruleset applies here as well with some exceptions.

IPv6 Main configuration file: shorewall.conf

There are no particular differences between the IPv4 and IPv6 cases. Just copy the file over!

IPv6 Zone definition: zones

Just copy zones file from /etc/shorewall and replace all instances of ipv4 with ipv6. Done!

IPv6 Interface definition: interfaces

Some of the options from /etc/shorewall do not apply here and you will need to add forward=1 to both interfaces resulting in the following file

#ZONE		INTERFACE		OPTIONS
net	ppp0	tcpflags,forward=1,sourceroute=0
loc	eth1	dhcp,tcpflags,forward=1

Note that dhcp on eth1 is not really necessary if you are using autoconfiguration for your hosts. For the sake of completeness I will show how to setup DHCPv6 later on so you might as well leave it there for the time being.

IPv6 Default policy: policy

Exactly the same as in IPv4. Just copy the file over.

IPv6 Network address translation: snat

Technically you can do NAT on IPv6 although there is no point to do so as you probably have a bazillion available address anyway. So just leave snat empty.

IPv6 Firewall rules: rules

Rule configuration is pretty much the same as in IPv4 with two notable exceptions

  1. You must allow ipv6-icmp packets from $FW to both loc and net because otherwise autoconfiguration of IPv6 addresses will not work. So remove the icmp rules from your ruleset and replace them with ipv6-icmp.
  2. There is no need for port forward as we have real internet routable addresses for all our hosts (isn’t that marvelous!?). Since you are not doing port forwarding you just need to allow traffic from the net into your local network for the ports of your preference. A rule like ACCEPT net loc tcp 9912 will allow all connections to port 9912 on your LAN from the internet. You can limit these to the hosts of your preference by adding the :[IPv6Address] suffix to loc, for instance loc:[2001:db8::3].

Accessing your modem’s configuration

As a final remark, if you are using an xDSL or cable connection you might have noticed that you can no longer access your modem’s configuration web page (or ssh server for that matter). This is because, if you followed this guide up to now, it is residing on a different subnet than yours. There are some minimal alterations that must be made to shorewall configuration to allow for such functionality. If you look back at part 3 you might remember that we assigned the address 192.168.0.2 to the interface connected to the modem (eth0). I will assume that your modem is in the same subnet, for example 192.168.0.100. We need to edit fice files zones, interfaces, snat, policy, rules. As the modem is in a different subnet we will need to define a new corresponding zone; let’s call it modem.

#ZONE      TYPE      OPTIONS      IN_OPTIONS      OUT_OPTIONS
modem      ipv4

Then we declare the interface corresponding to this zone

#ZONE      INTERFACE      OPTIONS
modem      eth0

Since we defined a new zone we need to setup a default policy for that zone. The only required policy is to allow traffic from within the firewall to the modem. So add the following to policy.

#SOURCE      DEST      POLICY      LOGLEVEL      RATE      CONNLIMIT
fw           modem     ACCEPT

If you need full access from the local network (loc) to the modem you can additionally append:

loc          modem     ACCEPT

Personally I feel that it might be better to limit access to your modem to a limited number of computers, especially since the security record of most network equipment aimed at consumers leaves a lot to be desired. But this is up to you!

As the modem probably won’t accept connections outside its subnet we need a SNAT rule to do the translation from 192.168.1.0/24 to 192.168.0.0/24. So add the following to snat

SNAT(192.168.0.2)      192.168.1.0/24      eth0:192.168.0.1      tcp

The above will translate addresses originating from 192.168.1.0/24 to 192.168.0.0/24 through the eth0 interface that has the address 192.168.0.1 in that subnet. I’ve added tcp to limit access to TCP connections only but feel free to use what you like.

If you don’t need to restrict access to your modem you can stop here. Restart shorewall and point your browser to your modem’s webpage. Should you need more granular control you will need to additionally edit rules to specify IPs or MAC addresses that have access to the modem zone (since by default such requests will be REJECTed). In the example below we will be allowing SSH access to the computer with IP 192.168.1.8 and web access to the computer with MAC address AA:BB:CC:DD:EE:FF. Adjust to your needs!

SSH(ACCEPT)      loc:192.168.18            modem
HTTP(ACCEPT)     loc:~aa-bb-cc-dd-ee-ff    modem

Please note that MAC addresses must be prefixed with ~ and separated by - instead of :. Have fun!

This is the end of part 4. Again if you are following this guide with Alpine linux this is probably a good time to save your settings. Go ahead and lbu commit them. You can then proceed onto part 5 where we will setup a DHCP server and a DNS caching server using dnsmasq.