🔐 Taming WSL in Bridged Mode with ProtonVPN and WireGuard

in #homelab2 months ago (edited)

🔐 Taming WSL in Bridged Mode with ProtonVPN and WireGuard

image.png

When you're routing traffic from a bridged WSL instance through a ProtonVPN WireGuard tunnel running on your Windows host, you're essentially building a hybrid between enterprise-grade routing and developer-friendly sandboxing. Here's how I untangled this setup and got it working beautifully—including pitfalls, commands, and how to make it persist across reboots.


🧠 Initial Symptoms

I started off by noticing that my WSL traffic wasn't tunneling through ProtonVPN:

curl ifconfig.io
>> 203.0.113.45  # Not the ProtonVPN exit IP

This meant that WSL (bridged mode) was bypassing the VPN tunnel and going out through the LAN directly.


🔧 Step-by-Step Fix

1. Route WSL Through Windows Host

Since my WSL IP is in the same subnet (10.254.1.x) as the host:

ip route replace default via 10.254.1.60

This forced WSL to send its outbound traffic to the host.


2. Enable NAT on Windows

Windows doesn’t NAT bridged traffic by default. So I created a NAT rule:

New-NetNat -Name "WSLNat" -InternalIPInterfaceAddressPrefix 10.254.1.0/24

You can verify it with:

Get-NetNat

Boom—after applying this, WSL could finally reach public IPs through the host.


3. Fixing DNS Inside WSL

Originally I pointed DNS to my host:

echo "nameserver 10.254.1.60" | sudo tee /etc/resolv.conf

But it failed because the host wasn’t running a DNS forwarder. Instead, I switched to:

echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
sudo chattr +i /etc/resolv.conf

Locking the file ensured nothing overwrote it during tunnel setup or DHCP refresh.


🎯 Confirmation

After all changes:

curl ifconfig.io
>> 149.102.228.16  # ProtonVPN exit IP!

And:

ping 1.1.1.1
>> Success

At this point, WSL was fully routed through the host, DNS was working, and my tunnel was leak-free.


🔁 Reboot Protection

After reboot, ProtonVPN disabled my internet. The issue? NAT rules don’t persist automatically.

Fix: Recreate NAT rule using PowerShell after reboot:

New-NetNat -Name "WSLNat" -InternalIPInterfaceAddressPrefix 10.254.1.0/24

If desired, this can be scripted or added to a Scheduled Task.


🚫 Rolling Back Cleanly (If Needed)

To undo everything:

  • Remove NAT rule:
    Remove-NetNat -Name "WSLNat"
    
  • Reset route in WSL:
    sudo ip route del default
    sudo dhclient eth0
    
  • Restore DNS:
    sudo chattr -i /etc/resolv.conf
    sudo rm /etc/resolv.conf
    echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
    

⚡ Final Thoughts

This setup gives me WSL with full bridged networking, ProtonVPN privacy, and controlled DNS—all with minimal dependencies. Next steps? Script it all so I never have to touch it after reboot.

Need help configuring your own WSL tunnel flow? Start with ip route, validate with curl, and always double-check your NAT rules.