Skip to main content

Firewall Setup

Install Shorewall

To manage nftables/iptables I decided to go with Shorewall since it is easy to configure and very mature. At some point I may look into switching to FireHol since it looks even simpler to configure but I wanted something I knew I'd be able to make do everything I needed.

I started by installing shorewall, shorewall-doc that include examples, and shorewall-init which can lockdown the system at boot before Shorewall has had a chance to configure the firewall.

# apt install shorewall shorewall-doc shorewall-init

Then I update the Shorewall configuration. All I have to do is update some logging parameters to reflect that I'm using ulogd2 and that I want IPv4 forwarding enabled when Shorewall starts.

# /etc/shorewall/shorewall.conf
- LOG_LEVEL="info"
+ LOG_LEVEL="NFLOG(1,0,1)"
...
- LOGFILE=/var/log/messages
+ LOGFILE=/var/log/firewall.log
...
- IP_FORWARDING=Keep
+ IP_FORWARDING=Yes

All my configuration files are adapted from the examples that shorewall-doc makes available under /usr/share/doc/shorewall/examples.

I changed a few names and added some additional zones like wg and warp which is a zone where all exiting traffic gets automatically routed through a VPN tunnel. This makes it easy for any Docker container I want to use a VPN I just put it on that subnet/zone.

# /etc/shorewall/zones
+ #------------------------------------------------------------------------------
+ # For information about entries in this file, type "man shorewall-zones"
+ #
+ # See http://shorewall.net/manpages/shorewall-zones.html for more information
+ ###############################################################################
+ #ZONE   TYPE    OPTIONS                 IN                      OUT
+ #                                       OPTIONS                 OPTIONS
+ fw      firewall
+ wan     ipv4
+ lan     ipv4
+ dmz     ipv4
+ warp    ipv4
+ wg      ipv4

Setting up the interfaces and assiging them zones is pretty self-explanatory.

# /etc/shorewall/interfaces
+ #------------------------------------------------------------------------------
+ # For information about entries in this file, type "man shorewall-interfaces"
+ #
+ # See http://shorewall.net/manpages/shorewall-interfaces.html for more information
+ ###############################################################################
+ ?FORMAT 2
+ ###############################################################################
+ #ZONE   INTERFACE       OPTIONS
+ wan     WAN_IF          tcpflags,dhcp,nosmurfs,routefilter,logmartians,sourceroute=0,physical=eth0
+ lan     LAN_IF          tcpflags,dhcp,nosmurfs,routefilter,logmartians,physical=eth1
+ dmz     DMZ_IF          tcpflags,dhcp,nosmurfs,routefilter,logmartians,physical=eth1.8
+ warp    WARP_IF         tcpflags,dhcp,nosmurfs,routefilter,logmartians,physical=eth1.9

My real policy differs slightly but I included a basic example policy.

# /etc/shorewall/policy
+ #------------------------------------------------------------------------------
+ # For information about entries in this file, type "man shorewall-policy"
+ #
+ # See http://shorewall.net/manpages/shorewall-policy.html for more information
+ ###############################################################################
+ #SOURCE DEST            POLICY          LOGLEVEL        RATE    CONNLIMIT
+ 
+ $FW     all			  ACCEPT
+ lan	  all             ACCEPT
+ dmz     $FW,wan	      ACCEPT
+ warp    $FW			  ACCEPT
+ 
+ wan     all             DROP            $LOG_LEVEL
+ # THE FOLLOWING POLICY MUST BE LAST
+ all     all             REJECT          $LOG_LEVEL

Because my example policy is pretty liberal, my rules in this example are pretty sparse.

# /etc/shorewall/rules
+ #------------------------------------------------------------------------------------------------------------
+ # For information about entries in this file, type "man shorewall-rules"
+ #
+ # See http://shorewall.net/manpages/shorewall-rules.html for more information
+ ######################################################################################################################################################################################################
+ #ACTION         SOURCE          DEST            PROTO   DEST    SOURCE          ORIGINAL        RATE          USER/    MARK    CONNLIMIT       TIME            HEADERS         SWITCH          HELPER
+ #                                                       PORT    PORT(S)         DEST            LIMIT         GROUP
+ ?SECTION ALL
+ ?SECTION ESTABLISHED
+ ?SECTION RELATED
+ ?SECTION INVALID
+ ?SECTION UNTRACKED
+ ?SECTION NEW
+ 
+ #       Don't allow connection pickup from the net
+ Invalid(DROP)   wan             all             tcp
+ 
+ DNS(ACCEPT)     all!wan,warp    $FW
+ DNS(ACCEPT)     $FW,dmz         lan:10.0.1.2
+ 
+ Web(ACCEPT)     dmz             $FW
+ Web(DNAT)       wan             dmz:10.0.8.2

Last but not least is the magic that allows private addresses to access the greater Internet by masquerading as the one public IPv4 address I am assigned. The following just says all traffic heading out of WAN_IF (eth0) coming from a private IP range should be masqueraded.

# /etc/shorewall/snat
+ #------------------------------------------------------------------------------
+ # For information about entries in this file, type "man shorewall-snat"
+ #
+ # See http://shorewall.net/manpages/shorewall-snat.html for more information
+ ###########################################################################################################################################
+ #ACTION                 SOURCE                  DEST            PROTO   PORT    IPSEC   MARK    USER    SWITCHORIGDEST PROBABILITY
+ MASQUERADE              10.0.0.0/8,\
+                         169.254.0.0/16,\
+                         172.16.0.0/12,\
+                         192.168.0.0/16          WAN_IF

Now that everything is configured it might be wise to run shorewall check just to make sure I didn't have any typos.

You can hook Shorewall into the boot process to make sure the system is secure during boot by enabling shorewall-init and shorewall. First we tell shorewall-init that it needs to take shorewall into account.

# /etc/default/shorewall-init
- PRODUCTS=""
+ PRODUCTS="shorewall"

Then we simply tell them to start at boot.

# systemctl enable shorewall
# systemctl enable shorewall-init

Modify Interfaces

Now that Shorewall will secure everything at bootup it is safe to upate my /etc/networking/interfaces with their addresses.

# /etc/networking/interfaces
 auto eth1
- iface eth1 inet manual
+ iface eth1 inet static
+         address 10.0.1.1/21
  
  auto eth1.8
- iface eth1.8 inet manual
+ iface eth1.8 inet static
          vlan-raw-device eth1
+         address 10.0.8.1/24
  
  auto eth1.9
- iface eth1.9 inet manual
+ iface eth1.9 inet static
          vlan-raw-device eth1
+         address 10.0.9.1/24

Now if I reboot the system all my interfaces will come up configured and the system will be protected by nftables/iptables configured by Shorewall.

Be sure to sanity check your configuration, if you need to SSH into a system to configure it Shorewall has to allow SSH traffic.

# reboot