Objective
Given the following interfaces on a Raspberry Pi:
- eth0 (192.168.0.0/24) - Private network (i.e. NAT)
- wlan0 (192.168.10.0/24) - Public network with Internet access (i.e. LAN)
- tun0 (VPN) - VPN connection
Build a firewall that achieves the following:
- Deny ALL inbound traffic to:
wlan0
,tun0
(block incoming connections) - Route ALL outbound traffic from
eth0
totun0
(block "lateral" connections; i.e. no LAN access)- If
tun0
is down, DO NOT alloweth0
to usewlan0
instead (i.e. VPN killswitch)
- If
Mainstream Example
I've seen numerous "VPN killswitch" examples at this point with UFW and they all share a common configuration:
# Defaults
ufw default deny outgoing
ufw default deny incoming
# Allow local over ethernet (without VPN)
sudo ufw allow out to 192.168.0.0/24 # Allow out to LAN
sudo ufw allow in to 192.168.0.0/24 # Allow in to LAN
# Allow outgoing over ethernet to VPN
sudo ufw allow out to [VPN] port 1194 proto udp
# Allow outgoing over tun0
sudo ufw allow out on tun0 # Allow out over VPN
Sources:
- https://forums.whirlpool.net.au/archive/2561726
- https://www.reddit.com/r/VPN/comments/4ik46m/vpn_killswitch_setup_using_ufw/
- https://www.youtube.com/watch?v=x9fb2UAWkBE
- https://github.com/andreisid/bash/blob/master/killswitch.sh
My NAT Example (Alternative)
Granted, in my application, I have an intermediate device (Raspberry Pi) in play that acts as a router, firewall, DNS & DHCP server, and VPN client so it is a slightly different setup. However, it would seem that the NAT table (/etc/ufw/before.rules
) handles the ufw allow out/in to 192.168.0.0/24
statements and pretty much routes all outbound traffic coming from eth0
to tun0
(second objective item) as-is:
# NAT table to "forward" private network to VPN tunnel
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 192.168.0.0/24 -o tun0 -j MASQUERADE
COMMIT
This should "forward" anything coming from the private network (eth0
) into the VPN tunnel (tun0
), correct? Or do I need FORWARD
clauses in here as well?
After that, I'm not sure at this point if there is anything else I would need to configure, such as:
# Set defaults (also see /etc/default/ufw)
sudo ufw default deny incoming
sudo ufw default deny outgoing
# Allow incoming requests to DNS/DHCP services (UDP) on eth0 interface only (i.e. Private Network -> Pi:43,67/udp)
sudo ufw allow in on eth0 from any to any port 53,67 proto udp
# Allow incoming requests to SSH service (TCP) on eth0 interface only (i.e. Private Network -> Pi:22/tcp)
sudo ufw allow in on eth0 from any to any port 22 proto tcp
# Allow outbound on wlan0 interface for VPN traffic only (i.e. Pi -> LAN:1194)
sudo ufw allow out on wlan0 from any to any port 1194 proto udp
# Allow all outbound traffic on eth0 (i.e. Pi -> Private Network)
sudo ufw allow out on eth0
# Allow all outbound traffic on VPN tunnel only (i.e. Pi -> VPN)
sudo ufw allow out on tun0
In the tests I've done (traceroute
, ping
, etc.) with the NAT table alone, I can see my outbound Internet connection stops at the Raspberry Pi when I disconnect the VPN. However, what I'm still trying to confirm is whether this covers all possible leak scenarios (i.e. DNS, etc.).
Note: I am also using dnsmasq
so the Raspberry Pi is also the DNS server issued by the DHCP server to clients on the private network. I wonder if all I need to do is either configure dnsmasq
to forward DNS queries through tun0
only (if that's even possible), or settle for forwarding onto public DNS instead (i.e. 8.8.8.8, 8.8.4.4). Also, as of right now, this setup blocks my outbound connections still, but issuing a sudo ufw allow out on wlan0
restores that connection (with VPN killswitch still in effect). So I feel like I'm close and maybe just a couple more rules off.
Would greatly appreciate someone taking the time to look over these details and provide their feedback!