Securing Pi using iptables (VPN gateway mode)

Hi all!

Recently I bought a Pi4 and I am going to use it for:

  1. VPN gateway with native NordVPN client and NordLynx protocol.
  2. Adguard Home DNA (Pi hole analogue, which I am not fan of) running in a container (Docker).
  3. Unifi controller - also running in a container.
  4. A few other containers.

I have some experience with Linux, and I have already disabled root and other build in users, tuned up security settings a bit. Now I need to set up firewall rules. Iptables always has been always will be a pain in the… for me. Could somebody please help me with setting up the them correctly? Basically I want to do the following:

  1. Accept all connections from any client in the LAN (10.10.10.0/24).
  2. Act as a VPN gateway and route all outgoing traffic from “eth0” to “nordlynx” tunnel.
  3. Forward a few ports to be able to receive incoming connections and route them.
  4. Provide all the routing for Docker (it creates a lot of rules after installation).
  5. Reject all other connections from the Internet unless the connection has been initialised by a client in LAN or connection is initiated to a forwarded port/service.

I’ve looked through a few articles and it looks like I have to use iptables (not ufw or any other userfriendly firewall) because NordVPN’s killswitch changes iptables.

Thank you very much for any advice!

I came up with the following solution.

  1. Installed the Webmin package. It has a module for iptables which helps a lot in creating/understanding firewall rules.
  2. Unfortunately, the Webmin only works with the legacy iptables, not the new nftables firewall, which is enabled by default in the new Debian releases. The nftables works with a completely different syntax so I switched back to the iptables-legacy by running the following commands:
    sudo update-alternatives --set iptables /usr/sbin/iptables-nft
    sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
    sudo update-alternatives --set arptables /usr/sbin/arptables-nft
    sudo update-alternatives --set ebtables /usr/sbin/ebtables-nft
  3. Installed iptables-persistent package sudo apt-get install iptables-persistent and saved existing rules.
  4. Configured the Webmin to use iptables-persistent file: Networking → Linux Firewall → press the gear button on top (module settings) → choose “Configuration category”: IPv4 Configuration → Set “File to save/edit IPv4 rules” to file “/etc/iptables/rules.v4” → Save and Next → Set “File to save/edit IPv6 rules” to “/etc/iptables/rules.v6” → Save. After that the Webmin will automatically save the rules and they will stay intact after reboot.
  5. Installed the NordVPN native app to use NordLynx protocol (based on WireGuard). It is sad that NordVPN do not provide configurations for 3rd-party WireGuard apps.
  6. Uncommented “net.ipv4.ip_forward” parameter and checked that is equals to “1” in the /etc/sysctl.conf file. Alternatively you can change this setting in the Webmin: Networking → Network Configuration → Routing and Gateways → Act as router? → Yes.
  7. Added a NAT rule:
    sudo iptables -t nat -A POSTROUTING -o nordlynx -j MASQUERADE
  8. Added firewall rules:
    sudo iptables -A INPUT -s 10.10.10.0/24 -i eth0 -j ACCEPT
    sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    sudo iptables -A INPUT -i lo -j ACCEPT
    The first one allows incoming connections from my LAN and eth0 interface. This rule should be revised later, because the Raspberry Pi connects to the internet using gateway 10.10.10.1 thus any incoming connection from the Internet goes through gateway and will be accepted by Pi.
    The second one allows incoming traffic if the connection has been initialized by the Raspberry Pi.
    The third one allows loopback traffic.
    sudo iptables -A FORWARD -i eth0 -o nordlynx -j ACCEPT
    sudo iptables -A FORWARD -i nordlynx -o eth0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    The first one allows traffic from eth0 go through VPN (nordlynx) interface.
    The second one allows traffic to go back but only if the connection has been initialized from the eth0 interface.
    sudo iptables -P FORWARD DROP
    sudo iptables -P INPUT DROP
    Drop all other Forward and Incoming traffic. Be careful with these rules - they might isolate Pi from the network if previous rules contain error.
  9. Configured the NordVPN app:
    nordvpn set technology NordLynx
    nordvpn whitelist add subnet 10.10.10.0/24
  10. Now the VPN connection works. And it is possible to use Raspberry Pi as a VPN gateway. To connect to the VPN you should run command:
    nordvpn c <server - otional>
    But I have faced with a problem which I will describe next.

Native NordVPN app creates additional rules in the iptables firewall when connected or set to the KillSwitch mode. These rules bring mess in my own firewall configuration, and I couldn’t find a way either to disable or to adjust them.

When connected or set to the KillSwitch mode the NordVPN app creates 3 additional rules in each section and puts them on top my rules:
Input:
-P INPUT DROP
-A INPUT -s 104.140.53.106/32 -i eth0 -j ACCEPT
-A INPUT -s 10.10.10.0/24 -i eth0 -j ACCEPT
-A INPUT -i eth0 -j DROP
-A INPUT -s 10.10.10.0/24 -i eth0 -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT

Output:
-P OUTPUT ACCEPT
-A OUTPUT -d 104.140.53.106/32 -o eth0 -j ACCEPT
-A OUTPUT -d 10.10.10.0/24 -o eth0 -j ACCEPT
-A OUTPUT -o eth0 -j DROP

I do not want all packets to be accepted from 104.140.53.106/32 (which is a VPN network), I want them to be accepted only in two cases: this is a RELATED, ESTABLISHED connection or the packet is going to an open port (for now I have none). All other options are unsafe.
Then the second rule just doubles the fourth one because of the whitelisted network in the NordVPN app.
The third rule is obsolete, since the default action is set to DROP. This rule will make other rules not to be applied.
Another problem is when the server or the NordVPN app are unexpectedly shut down all created rules stay in iptables, which creates a mess.

I haven’t yet found a way ti fix this. I will be grateful for any suggestions.

You should rearrange the sequence of the rules.
The conntrack rule should be on the top.
The input from the 104 can be omitted. The dietpi is initiating the connection so the return packets will be accepted on the conntrack rule.
Other than that if the dietpi is behind the ISP router, all these rules don’t matter much.

Thank you!

The 3 rules on top (both in input and output chains) are created by the NordVPN app each time the VPN connection is established. The app then will delete these rules if the VPN connection is turned off and the KillSwitch setting is also turned off. If the KillSwith is turned on then the rules will not be deleted.
I have contacted the NordVPN support team and got a recommendation to use Ipsec protocol instead because this behaviour cannot be adjusted.
I wrote a custom script instead. It calls the NordVPN app and then re-creates iptables.

Does this solve your issue?

Yes. At least with the firewall rules.
Not the ideal solution, but if it works - it works! Hope the app developers will fix the issue.