Building a dedicated Linux VPN gateway with NAT, selective routing, firewall policies, and a kill switch.
Introduction
One of the advantages of operating a home networking lab is the ability to experiment with technologies that are commonly used in enterprise environments.
For this project, I wanted to route traffic from a specific VLAN through a commercial VPN without installing VPN software on individual devices or changing the default route for the rest of my network.
My solution was to combine three technologies:
- Palo Alto Policy-Based Forwarding (PBF) to redirect selected network traffic.
- WireGuard and Proton VPN to establish an encrypted tunnel.
- Linux IP forwarding and NAT to turn a Linux system into a dedicated VPN gateway.
The result is a network architecture where devices belonging to a selected VLAN can access the internet through Proton VPN, while devices on other VLANs continue using the normal internet connection.
An important part of this project was implementing a kill switch. If the VPN tunnel fails, traffic originating from the VPN-designated VLAN should not automatically fall back to the unencrypted WAN connection.
One of the major problems I ran into was needing the VPN to need to use a separate interface rather than the one that is being using for LAN traffic.
1. Understanding the Network Architecture
My existing network uses a Palo Alto firewall to manage routing, network segmentation, and security policies between multiple VLANs.
For this configuration, I designated a Linux machine with the address 192.0.2.10 as the WireGuard VPN gateway.
The test device resides on VLAN 101, using the address 198.51.100.229.
The logical traffic path looks like this:
Client Device
198.51.100.229
|
| VLAN 101
v
Palo Alto Firewall
198.51.100.1
|
| Policy-Based Forwarding
v
Linux VPN Gateway
192.0.2.10
|
| IP Forwarding + NAT
v
WireGuard Interface (wg0)
|
| Encrypted VPN Tunnel
v
Proton VPN Server
|
v
Internet
The Linux gateway also requires an ordinary network connection to reach Proton’s public VPN endpoint. Although client traffic travels through WireGuard, the encrypted WireGuard packets themselves still cross the underlying network and ISP connection.
Why use Policy-Based Forwarding?
Under normal circumstances, the firewall makes forwarding decisions using its routing table.
When a packet arrives, the firewall examines the destination address and determines the appropriate next hop using the most specific matching route.
However, I did not want to modify the default route for the entire network.
Instead, I wanted routing behavior determined by the source network.
Policy-Based Forwarding makes this possible.
By creating a PBF policy matching VLAN 101, I could instruct the firewall to send that traffic to 192.0.2.10, regardless of the normal default route.
This allowed me to retain my existing network architecture without requiring a separate VPN client on every endpoint.
2. Generating the Proton VPN WireGuard Configuration
The first step was creating a WireGuard configuration through Proton VPN.
A Proton VPN subscription with access to the desired WireGuard server was required.
I navigated to:
After logging into my account, I generated a new WireGuard configuration and selected an appropriate VPN server.
The generated configuration contains the information necessary to establish the encrypted connection, including:
- The local WireGuard private key
- The VPN server’s public key
- The assigned tunnel IP address
- The VPN server endpoint and UDP port
- The routes that WireGuard should handle
- DNS configuration
One important security consideration is that the configuration contains a private key. This file should never be committed to a public GitHub repository or included in a blog post without removing sensitive information.
Something else to consider when doing this is the level of traffic blocking, if you are doing URL filtering on PIhole you might not want to do it again on the VPN. For me I am all about layering the filtering and blocking ads and trackers at every layer possible from Firewall to DNS.
3. Installing WireGuard on Linux
With the configuration downloaded, the next step was installing WireGuard on the Linux gateway.
On an Ubuntu or Debian system, WireGuard can be installed with:
sudo apt update
sudo apt install wireguard wireguard-tools
Next, I copied the downloaded configuration into /etc/wireguard/ and renamed it wg0.conf.
sudo cp proton.conf /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf
The configuration can then be brought online using:
sudo wg-quick up wg0
To confirm that the tunnel is operational:
sudo wg show
This displays information about the WireGuard peer, latest handshake, and transmitted and received bytes.
To shut down the tunnel:
sudo wg-quick down wg0
These commands are particularly useful when testing the VPN or troubleshooting network connectivity.
For a more permanent deployment, WireGuard can also be managed through systemd.
sudo systemctl enable --now wg-quick@wg0
At this stage, the Linux machine should be capable of communicating through Proton VPN. However, additional configuration is necessary before it can route traffic for other devices.
At this point you might as well create a macro for sudo wg-quick up wg0 and name it something you’ll remember or just something goofy with the alias command. Always recommend a spell of some sorts from Harry Potter.
4. Turning Linux Into a VPN Router
Establishing a WireGuard connection only solves part of the problem.
By default, a Linux machine is generally configured as an endpoint rather than a router. Even though the Linux gateway can communicate through the VPN, it will not automatically forward traffic received from other devices.
To address this, I needed to configure two important networking features:
IP forwarding allows Linux to route packets between interfaces.
Network Address Translation (NAT) allows traffic originating from the client VLAN to be translated to an address usable through the VPN tunnel.
Enabling IP forwarding
First, I enabled IPv4 forwarding:
sudo sysctl -w net.ipv4.ip_forward=1
To make this persistent across reboots:
echo "net.ipv4.ip_forward=1" | \
sudo tee /etc/sysctl.d/99-vpn-router.conf
sudo sysctl --system
With forwarding enabled, Linux can act as a router between the internal network and the WireGuard tunnel, provided the routing table and firewall rules permit the traffic.
Configuring masquerading
The next requirement was source NAT.
Without NAT, packets entering the Linux gateway would retain their original source addresses, such as 198.51.100.229.
These private addresses are not appropriate as source addresses for ordinary internet traffic exiting through Proton VPN.
Masquerading rewrites the source IP address to the address assigned to the outgoing WireGuard interface.
Using iptables, I configured the following rule:
sudo iptables -t nat -A POSTROUTING \
-s 198.51.100.0/24 \
-o wg0 \
-j MASQUERADE
This rule tells Linux to apply source NAT to packets originating from VLAN 101 as they leave through wg0.
I also needed to permit forwarding between the network-facing interface and WireGuard.
Assuming the physical network interface is eth0:
sudo iptables -A FORWARD \
-i eth0 -o wg0 \
-s 198.51.100.0/24 \
-j ACCEPT
sudo iptables -A FORWARD \
-i wg0 -o eth0 \
-d 198.51.100.0/24 \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT
These rules are examples and must be adapted to the actual interface names and existing Linux firewall configuration.
They also assume that the Linux routing table knows how to reach VLAN 101 through the Palo Alto firewall. A route may be required:
sudo ip route replace 198.51.100.0/24 \
via 192.0.2.1
This return route is important because WireGuard may install a default route through the VPN.
Without an appropriate return route, the Linux gateway could receive client packets but fail to return responses to the original VLAN.
Understanding the NAT process
Suppose my test client sends an HTTPS request to a public website.
Initially, the packet looks like this:
Source: 198.51.100.229
Destination: Public web server
Protocol: TCP/443
The Palo Alto firewall forwards the packet to the Linux gateway based on the PBF rule.
The Linux gateway then routes the packet into WireGuard and performs masquerading.
The inner packet’s source address becomes the Linux WireGuard tunnel address, and the packet is encapsulated and encrypted for transmission to Proton VPN.
Proton subsequently handles the traffic’s exit toward the public internet.
For a website receiving this traffic, the apparent public source IP address is the Proton VPN exit address rather than my home ISP’s public IP.
Linux connection tracking maintains the necessary NAT state so return traffic can be delivered to the original client.
5. Creating the Palo Alto Policy-Based Forwarding Rule
Once the Linux gateway was configured, the next step was creating a PBF policy on the Palo Alto firewall.
Within the Palo Alto web interface, I navigated to:
Policies → Policy Based Forwarding
I created a new policy matching traffic originating from VLAN 101.
The example configuration was:
| Setting | Value |
|---|---|
| Policy name | VLAN101-Proton-VPN |
| Source zone | VLAN101 / Trust |
| Source address | 198.51.100.0/24 |
| Destination | Internet-bound traffic |
| Action | Forward |
| Egress interface | Interface toward Linux gateway |
| Next-hop address | 192.0.2.10 |
The precise source zone and egress interface depend on the interface and zone configuration of the firewall.
In my case, the Linux gateway was reachable through the network containing 192.0.2.10.
A critical detail is that PBF should redirect only the intended traffic. Local networks, management services, and other internal destinations may need explicit exclusions.
For example, a higher-priority No PBF policy can exempt internal destinations so that traffic between trusted VLANs continues using the normal routing table.
After creating the forwarding rule, I committed the configuration.
How the forwarding decision changes
Without PBF:
VLAN 101 → Palo Alto → ISP Gateway → Internet
With PBF:
VLAN 101 → Palo Alto → Linux Gateway
|
v
WireGuard
|
v
Proton VPN
This is the core of the project.
The Palo Alto firewall doesn’t need to terminate the WireGuard tunnel. It only needs to forward the appropriate traffic to a gateway that can.
6. Implementing Firewall Security Policies
Policy-Based Forwarding and security policies serve different purposes.
PBF controls the path a packet takes.
Security policies determine whether the packet is allowed to traverse the firewall.
This distinction is important because a PBF rule does not automatically grant permission for the redirected traffic.
I needed security rules that allowed authorized client traffic to use the Linux VPN gateway while preventing those same clients from bypassing the VPN through the normal WAN path.
The general security design was:
- Permit required access to internal services, including the VPN gateway where appropriate.
- Permit authorized VPN-routed traffic according to the firewall’s zone design.
- Explicitly deny direct WAN access from the VPN-designated VLAN.
- Log denied connections to make troubleshooting easier.
The exact security policy matches depend on whether the Linux gateway and ISP interface belong to separate Palo Alto security zones.
Separating the VPN transit interface into its own zone can make the intended security boundaries clearer.
An explicit deny rule for traffic from VLAN 101 directly to the WAN zone provides an additional layer of protection.
However, a security deny rule alone is not sufficient if the VPN gateway itself can forward packets through its ordinary physical interface.
For that reason, I also needed to consider a kill switch on the Linux gateway.
7. Designing the VPN Kill Switch
One of the most important parts of this configuration is determining what should happen if the WireGuard tunnel becomes unavailable.
Without safeguards, a VPN failure could cause traffic to follow the normal default route instead.
This is a potential privacy failure because traffic that was supposed to use the VPN could leave through the ISP connection.
I approached this problem using defense in depth.
Palo Alto protection
The firewall should prevent the VPN-designated VLAN from using the normal WAN path.
This includes considering what happens if PBF monitoring detects that the Linux gateway is unreachable.
Palo Alto PBF monitoring can change forwarding behavior when the monitored next hop becomes unavailable. Depending on the selected configuration, traffic may be evaluated against other PBF rules or the normal routing table.
Therefore, path monitoring should not be treated as a kill switch by itself.
A more deterministic option is to configure a specific PBF forwarding rule followed by a matching Discard rule, so traffic cannot simply continue through the regular routing table if the forwarding rule becomes inapplicable.
This behavior must be tested with the firewall’s actual monitoring configuration and PAN-OS version.
Linux gateway protection
On Linux, I wanted forwarded traffic from VLAN 101 to be permitted through WireGuard but not through the regular network interface toward the ISP.
With an appropriate default-deny forwarding policy, this means only the explicitly permitted VPN forwarding traffic can pass.
An illustrative iptables design is:
sudo iptables -A FORWARD \
-s 198.51.100.0/24 \
-o wg0 \
-j ACCEPT
sudo iptables -A FORWARD \
-s 198.51.100.0/24 \
! -o wg0 \
-j DROP
The drop rule must be ordered correctly relative to other forwarding rules, and an existing permissive rule may need to be removed. A rule appended after an earlier broad ACCEPT rule will not provide the intended protection.
IPv6 forwarding requires equivalent safeguards if IPv6 is enabled.
A working kill switch should produce two outcomes:
VPN connected: Devices on VLAN 101 can access the internet using Proton VPN.
VPN disconnected: Devices on VLAN 101 lose internet connectivity instead of falling back to the ordinary ISP route.
This behavior should be explicitly tested rather than assumed from the presence of firewall rules.
8. Testing and Troubleshooting
Once the routing and firewall policies were in place, I tested connectivity from the Linux gateway and the VLAN 101 client.
Verify WireGuard
On Linux:
sudo wg show
The latest handshake and packet counters provide an initial indication that WireGuard is working.
Inspect routing
ip route
ip rule
This is especially important because wg-quick may install policy routing rules when a full-tunnel WireGuard configuration is used.
Verify NAT
sudo iptables -t nat -L -n -v
The masquerading rule’s packet counters should increase as forwarded traffic passes through the VPN.
Capture VPN traffic
On the WireGuard interface:
sudo tcpdump -ni wg0
On the physical interface:
sudo tcpdump -ni eth0
These captures can help distinguish traffic entering the Linux router from traffic exiting through the encrypted tunnel.
Verify the public IP address
From a client on VLAN 101, I checked the public IP address using:
curl -4 https://api.ipify.org
If the configuration works, the address should match Proton VPN’s public exit IP instead of my ISP’s public address.
I also tested traffic from a device on a non-VPN VLAN to confirm that it continued using the normal ISP connection.
Test the kill switch
Finally, I manually disconnected the VPN:
sudo wg-quick down wg0
I then attempted to access the internet from VLAN 101.
The expected result was that the client could no longer reach public internet destinations.
At the same time, other VLANs not subject to the VPN policy should remain functional.
I also checked DNS behavior to make sure the VPN-designated clients weren’t sending DNS queries through an unintended resolver or forwarding path.
This test is arguably more important than simply confirming that the VPN connects successfully.
A functional VPN tunnel is useful, but a properly enforced VPN routing policy should also behave predictably when the tunnel goes down.
9. Adding a VPN On/Off Toggle
Another useful addition was a simple administrative toggle for the Linux VPN gateway.
Instead of typing the WireGuard commands individually, I could create a small Bash utility.
For example:
#!/bin/bash
case "$1" in
up)
sudo wg-quick up wg0
;;
down)
sudo wg-quick down wg0
;;
status)
sudo wg show wg0
;;
*)
echo "Usage: $0 {up|down|status}"
exit 1
;;
esac
This script could be saved as vpn-toggle.sh.
The commands become:
./vpn-toggle.sh up
./vpn-toggle.sh down
./vpn-toggle.sh status
The objective is to make WireGuard function like a manageable network service rather than an application running on individual clients.
Importantly, turning the VPN off should not automatically disable the kill switch.
The administrative toggle controls the tunnel, while the firewall policy controls whether client traffic is permitted to bypass it.
Keeping those responsibilities separate reduces the likelihood of accidentally exposing traffic through the ordinary WAN.
10. Lessons Learned
This project provided an opportunity to combine several networking concepts into one practical deployment.
Policy-Based Forwarding is not a substitute for a routing table. It provides a mechanism to override forwarding decisions for selected traffic, but the underlying routing environment still needs to support return traffic and connectivity to the next hop.
Linux makes a flexible network appliance. By enabling IP forwarding, setting up NAT, and configuring firewall policies, a general-purpose Linux system can operate as a dedicated VPN router.
NAT and routing solve different problems. Routing determines where packets go. NAT changes packet addressing so private client traffic can be carried through the VPN environment and correctly associated with return traffic.
PBF and security policy must be configured together. The forwarding policy selects the desired path, while the security rules enforce which paths clients are allowed to use.
VPN availability and VPN enforcement are different problems. Establishing an encrypted tunnel doesn’t automatically prevent unencrypted fallback. The kill switch must be designed and tested independently.
Conclusion
The final architecture allows the Palo Alto firewall to selectively forward traffic from a specific VLAN through a dedicated Linux WireGuard gateway connected to Proton VPN.
It achieves this without changing the network-wide default route or requiring VPN clients on individual devices.
Beyond the VPN itself, this project was a useful exercise in Linux routing, source NAT, firewall segmentation, policy-based forwarding, packet inspection, and failure handling.
The most valuable lesson was that establishing a VPN connection is only one part of building a reliable VPN routing solution. Understanding packet flow and enforcing the intended behavior when components fail is just as important.

Leave a Reply