Network configuration for a testing gateway

Configuring basic network services for our example testing gateway machine

Author: Francesco Poli
Contact: invernomuto@paranoici.org
Version: 0.23
Copyright: Expat license
Notice:

Copyright (c) 2012-2024 Francesco Poli

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

About this document
Web form HyperText Markup Language
Source form reStructuredText
Web stylesheet Cascading StyleSheets
Build directives Makefile

Contents

Summary of previous episodes

In another document (HTML, reST) you saw how to install a Debian testing base system on a machine to be used as a network gateway/firewall/server. You also found references to other general workstation documents to follow in order to tune the system. Now it's time to begin shaping this machine for its primary purposes: being a network gateway and firewall.

DNS client

Install a practical command-line DNS client:

# aptitude install bind9-host

Copying data between directories or hosts

Sometimes you need to copy data between different directories (or between network-connected machines). For simple cases, cp (or scp) may suffice. However, more complicated cases may require something more sophisticated; install the following package:

# aptitude install rsync

Enabling the secondary network interface

In order to enable the secondary network interface (the one that will be connected to the LAN switch), edit the configuration file for network interfaces, until it looks like:

# cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug enp1s0
iface enp1s0 inet dhcp

# The local network interface
allow-hotplug enp2s0
iface enp2s0 inet static
        address 192.168.5.1/24

Then, edit the /etc/hosts configuration file, so that the first (IPv4) part becomes:

$ head -n 2 /etc/hosts
127.0.0.1       localhost
192.168.5.1     $HOSTNAME

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

where $HOSTNAME is the name that was previously chosen for the machine.

Issue the following command, if you want to immediately see the effects of the above configuration changes:

# ifup enp2s0

DNS forwarder and DHCP server

We want our machine to work as DHCP server and caching DNS proxy for the LAN connected to the secondary network interface (enp2s0). In order to achieve this result, install the following package:

# aptitude install dnsmasq

Then, edit the Dnsmasq configuration file /etc/dnsmasq.conf so that:

$ grep domain-needed /etc/dnsmasq.conf
domain-needed
$ grep bogus-priv /etc/dnsmasq.conf
bogus-priv
$ grep ^address= /etc/dnsmasq.conf
address=/double-click.net/127.0.0.1
address=/doubleclick.net/127.0.0.1
address=/ad.doubleclick.net/127.0.0.1
address=/ad.ca.doubleclick.net/127.0.0.1
address=/adremote.timeinc.net/127.0.0.1
address=/google-analytics.com/127.0.0.1
address=/googlesyndication.com/127.0.0.1
address=/adsremote.scripps.net/127.0.0.1
address=/a.as-us.falkag.net/127.0.0.1
address=/interclick.com/127.0.0.1
address=/a1.interclick.com/127.0.0.1
address=/media.fastclick.net/127.0.0.1
address=/network.realmedia.com/127.0.0.1
address=/ads.auctionads.com/127.0.0.1
address=/ads.adbrite.com/127.0.0.1
$ grep ^interface= /etc/dnsmasq.conf
interface=enp2s0
$ grep ^dhcp-range= /etc/dnsmasq.conf
dhcp-range=192.168.5.100,192.168.5.199
$ grep bogus-nx /etc/dnsmasq.conf
bogus-nxdomain=64.94.110.11
bogus-nxdomain=54.72.52.58
$ grep '^dhcp.*wpad' /etc/dnsmasq.conf
dhcp-name-match=set:wpad-ignore,wpad
dhcp-ignore-names=tag:wpad-ignore

and restart the daemon:

# systemctl restart dnsmasq.service

DHCP client configuration

The following DHCP client should be already installed (and not marked as automatically installed):

# aptitude search '~i dhcp'
i   dhcpcd-base                     - DHCPv4 and DHCPv6 dual-stack client (binar

Create the following configuration file:

# echo 'nameserver 127.0.0.1' > /etc/resolv.conf.head

in order to use the DNS cache provided by Dnsmasq on the gateway box itself, as well as on the local network.

If these packages are installed, you may purge them:

# aptitude --purge-unused purge isc-dhcp-client isc-dhcp-common

In order to enable this configuration change, take down and up the primary network interface:

# ifdown enp1s0 ; ifup enp1s0

Network time synchronization

We want the clock of our machine to be as accurate as possible. Install the following NTP client and server:

# aptitude install chrony

Then, edit its configuration file /etc/chrony/chrony.conf so that:

$ grep ^pool /etc/chrony/chrony.conf
pool 2.debian.pool.ntp.org iburst minpoll 10 maxpoll 12
$ grep '^log ' /etc/chrony/chrony.conf
log tracking measurements statistics
$ grep -B 2 ^makestep /etc/chrony/chrony.conf
# Step the system clock instead of slewing it if the adjustment is larger than
# 200 seconds, but only in the first three clock updates.
makestep 200 3
$ grep -B 1 ^allow /etc/chrony/chrony.conf
# Allow local clients to connect to this server.
allow 192.168/16

and restart the daemon:

# systemctl restart chrony.service

Firewall and NAT

We want our machine to act as a firewall and NAT (Network Address Translator). First of all, add this configuration file:

# cat > /etc/sysctl.d/99-local-gateway.conf << EOF
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.ip_forward=1
EOF

You can apply these settings immediately with the following command:

# sysctl --system

They will however be applied automatically from the next reboot.

The following firewall command line tool should be already installed (and not marked as automatically installed):

# aptitude search '~i ^nftables'
i   nftables                        - Program to control packet filtering rules

Prepare the following configuration file:

$ cat nftables.conf
#!/usr/sbin/nft -f

flush ruleset

define wan = enp1s0  # interface to the public insecure net
define lan = enp2s0  # interface to the local net to be protected

table inet nat {
    chain POSTROUTING {
        type nat hook postrouting priority srcnat; policy accept;

        # masquerade everything going out to the public net
        oifname $wan counter masquerade
    }
}

table inet filter {
    chain base_checks {
        # allow established valid connections
        ct state invalid counter drop
        ct state established,related counter accept

        # always allow pinging
        ip protocol icmp counter accept
        meta l4proto ipv6-icmp counter accept

        # always reject connections to identd (see Securing Debian manual FAQ)
        tcp dport "auth" counter reject
    }

    chain INPUT {
        type filter hook input priority filter; policy drop;
        jump base_checks

        # accept locally generated traffic from loopback interface
        iifname "lo" counter accept

        # rules for traffic coming from the public net
        iifname $wan counter jump wan_in

        # rules for traffic coming from the local net
        iifname $lan counter jump lan_in
    }

    chain FORWARD {
        type filter hook forward priority filter; policy drop;
        jump base_checks

        # rules for traffic passing from the local net to the public net
        iifname $lan oifname $wan counter jump lan_to_wan

        # rules for traffic passing from the public net to the local net
        iifname $wan oifname $lan counter jump wan_to_lan
    }

    chain OUTPUT {
        type filter hook output priority filter; policy drop;
        jump base_checks

        # accept locally generated traffic to loopback interface
        oifname "lo" counter accept

        # rules for traffic going to the public net
        oifname $wan counter jump wan_out

        # rules for traffic going to the local net
        oifname $lan counter jump lan_out
    }

    chain lan_in {
        # local services accessible from the local net
        tcp dport "ssh" counter accept
        udp dport { "domain", "bootps" } \
          counter accept

        # log packets that failed to be accepted
        limit rate 3/hour burst 5 packets counter log level debug
    }

    chain lan_out {
        # local net remote services accessible from the local system
        udp dport "bootpc" counter accept

        # log packets that failed to be accepted
        limit rate 3/hour burst 5 packets counter log
    }

    chain lan_to_wan {
        # public net remote services accessible from the local net
        tcp dport { "ftp", "ftps", "http", "https", "http-alt", \
                    "ssh", "hkp", "smtp", "submissions", \
                    "submission", "pop3", "pop3s", "dict", "ircd", \
                    "git", "whois" } \
          counter accept
        udp dport "ntp" counter accept

        # public net radio/video streams accessible from the local net
        tcp dport { 11590, 8000, 8294 } \
          counter accept

        # public net remote proxy services accessible from the local net
        tcp dport 8888 counter accept

        # public net remote media (audio/video) services accessible
        # from the local net
        # (for Google meet)
        udp dport 19302-19309 counter accept
        # (for Microsoft teams)
        udp dport 3478-3481 counter accept

        # log packets that failed to be accepted
        limit rate 3/hour burst 5 packets counter log
    }

    chain wan_in {
        # local services for which access from the public net
        # will be denied, without even logging connection attempts

        # epmap         Microsoft end-point mapper
        # 137-139       NetBIOS noise
        # microsoft-ds  Windows Share attacks
        # ms-sql-s      Microsoft SQL Server
        # 2967          Symantec overflow attacks
        tcp dport { "epmap", 137-139, "microsoft-ds", "ms-sql-s", 2967 } \
          counter drop

        # 67-68     bootps & bootpc (for DHCP)
        # 137-139   NetBIOS noise
        # ipp       Internet Printing Protocol
        # 1025-1031 so-called "WinPopUP spam"
        # ms-sql-s  Microsoft SQL Server
        # 2222      MS-Office for MacOSX antipiracy
        udp dport { 67-68, 137-139, "ipp", 1025-1031, "ms-sql-s", 2222 } \
          counter drop

        # make ssh service accessible from the public net
        # (enable only when needed...)
        # tcp dport "ssh" counter accept

        # log packets that weren't dropped (yet) or accepted
        limit rate 3/hour burst 5 packets counter log level debug
    }

    chain wan_out {
        # public net remote services accessible from the local system
        tcp dport { "ftp", "ftps", "http", "https", "http-alt", "hkp" } \
          counter accept
        udp dport { "domain", "bootps", "ntp" } \
          counter accept

        # log packets that failed to be accepted
        limit rate 3/hour burst 5 packets counter log
    }

    chain wan_to_lan {
        # log packets that failed to be accepted
        limit rate 3/hour burst 5 packets counter log level err
    }
}

table inet helpers {
    # make ftp service properly accessible
    ct helper ftp-standard {
        type "ftp" protocol tcp
    }
    chain PREROUTING {
        type filter hook prerouting priority filter;
        tcp dport "ftp" ct helper set "ftp-standard"
    }
}

You may want to check its syntax:

# nft -c -f ./nftables.conf

If you want to test this firewall ruleset, you can do so by issuing the following command:

# nft -f ./nftables.conf

If you're satisfied with the result, you can copy this new ruleset to the configuration file that the systemd service expects to load:

# install -m 744 ./nftables.conf /etc/nftables.conf

In order to load this ruleset at boot, enable the systemd service:

# systemctl enable nftables.service

In order to load the ruleset defined in /etc/nftables.conf:

# systemctl start nftables.service

To reload the ruleset defined in /etc/nftables.conf:

# systemctl reload nftables.service

To flush the ruleset, you may use the following command:

# systemctl stop nftables.service

The syntax is explained in the following man page:

$ man nft

Migration from firewall rules based on iptables or iptables-legacy

If you already have firewall rules set with iptables-legacy, you should flush them, since they do not easily coexist with the rules set with nft. In order to migrate from the previous firewall configuration based on iptables-legacy to a new one based on nft, you may use a machine-translated ruleset as a starting point for the nftables.conf configuration file you are going to manually write:

# iptables-legacy-save > /tmp/iptables.txt
# iptables-restore-translate -f /tmp/iptables.txt > /tmp/nft.txt

Then flush all the rules based on iptables-legacy. For instance, if you were using ferm, issue the command:

# systemctl disable --now ferm.service

At this point load the translated rules with nft and obtain a first draft version of nftables.conf:

# nft flush ruleset
# nft -f /tmp/nft.txt
# echo '#!/usr/sbin/nft -f' > nftables.conf
# echo 'flush ruleset'     >> nftables.conf
# nft -s -S list ruleset   >> nftables.conf

Now you have a preliminary version of nftables.conf, which needs to be heavily refined by hand, before it becomes something like the one shown above. Once you are satisfied with the new firewall configuration, you may purge any package that you were using to manage the iptable-legacy rules (for instance, ferm).

Conclusions

Now the machine is ready to behave as a network gateway and firewall. But it could also be useful as a print server. More details in a separate document (HTML, reST).