Browser UI using sshuttle VPN in WSL
In this post we will create VPN connections using
sshuttle in WSL for Windows 11. This post will focus on pointing a Browser to a URL in a private network in Windows. The private network - VPN - can be reached from
sshuttle running in WSL. For this setup we unfortunately need to have a “double networking hop” in that Windows needs to be configured to send traffic to WSL (1) and WSL needs will then use
sshuttle to (2) forward that traffic to the public network.
In order to use
sshuttle VPN in Windows we need the following:
- Ability to
sshinto a host in the private network
- Administrator Access in Windows if you want to open a browser with a URL in the private network.
- We need to have root access to our
WSLinstance. This is a
Virtual Private Networks involve IP Addresses. However, you typically need more than just IP Addresses as we need a method to look up these IP Addresses. As we go through this please keep in mind how we resolve hosts and what network the DNS resolvers are in. More often than not the DNS resolver is also in the private network.
Let’s take an example scenario.
- On my Windows 11 Machine I want load a web page on my browser to
someserver.somecompanywhich resolves to the address
- The web server
someserver.somecompanyis in a private network
- To view the page on
sshuttleinto a remote host using
lloydrochester.com. The remote host
lloydrochester.comis inside the
172.22.0.0/24network and can connect to remote host
Let’s dive right into a complex network diagram. We’ll then dive into each connection.
The diagram above shows a total of 4 networks! Let’s explain them:
- The WSL Server and Windows 11 share a bridge network. This is called
vEthernet (WSL)on my machine.
- My Windows 11 machine is part of my home LAN network which I have the obscure
10.255.254.0/24network. Most people would likely have
- To simplify things my home router connects to the internet through the WAN, we’re also calling this the public internet.
- On the public internet we have our remote server that we will
sshuttleinto called lloydrochester.com.
- The host lloydrochester.com connects to the private network
eth1network interface that we can use to connect to
A basic VPN connection using
sshuttle in WSL
If we want to access
someserver.somecompany which resolves to
172.22.0.10 in this example we’d do the following
sshuttle -r lloydrochester.com 172.22.0.0/24
This is effectively saying we want to reach the
172.22.0.0/24 network via the remote host
lloydrochester.com over SSH.
Now what about DNS? We need to somehow resolve
Here are our options for DNS - some of these relate only inside our WSL instance:
- Put an entry into
/etc/hostsin Windows, and/or WSL if necessary.
- Use the
--dnsto use the resolver inside
/etc/resolve.confin the remote host
lloydrochester.comor even use the
--to-ns=<server>option to specify a DNS resolver inside our private network
172.16.0.0/24to take our queries.
- Stand up a
dnsmasqinstance on our WSL. More on this option later.
Let’s stop here for DNS, for the remaing we’ll statically define it without DNS.
Giving Windows 11 some VPN love
Could we open up a browser to
someserver.somecompany and have the page load? Nope. Why?
- The Windows 11 cannot resolve
- Even if Windows 11 could resolve what would make it forward packets to our WSL instance?
- If our WSL instance receives packets destined for
someserver.somecompanywill it route them properly?
Let’s solve each of these problems one-by-one.
Simple Resolution of hosts in Windows 11 to our WSL Instance
For Windows 11 to resolve
someserver.somecompany it’s easiest to use the Windows version of
/etc/hosts in the file
C:\Windows\System32\drivers\etc\hosts. This needs Administrator access but we can add the entry as such.
<our WSL IP address> someserver.somecompany
We’ll use the trick of pointing
someserver.somecompany to our WSL instance. To know the entry to add to the Windows host file simply run the following.
lloydroc@nifty:~$ ip route get 126.96.36.199 188.8.131.52 via 172.17.240.1 dev eth0 src 172.17.253.106 uid 1000 cache lloydroc@nifty:~$
Here we need to look at the
src IP address and also take note of the device name
eth0. Thus, I’d add the following line to
Your WSL instance will undoubtedly have a different address and perhaps device name depending on the flavor of Linux.
Proxy a connection from Windows to WSL
It turns out if Windows wants to make a connection to a WSL instance it needs to be explicitly specified. To do this we need to open a Power Shell terminal with Administrator privileges and run the following:
netsh interface portproxy add v4tov4 listenport=0-9000 connectaddress=172.17.253.106 connectport=0-9000 listenaddress=172.17.253.106
Please ensure you swap out
172.17.253.106 for the IP address of your WSL instance.
After, you’ve done this go ahead and run:
netsh interface portproxy show all
and if you need to remove the proxy run:
netsh interface portproxy reset
Testing a connection from Windows into WSL
This is when I like to test a connection from Windows to
someserver.somecompany can connect inside WSL. Let’s assume
someserver.somecompany is running on port
80. We can use
netcat for this:
In WSL I run:
sudo nc -kl 80
Now we’re listening on port
80 in WSL. So let’s test a connection from Windows in Power Shell.
PS C:\Users\lloyd> Test-NetConnection -ComputerName someserver.somecompany -Port 80 ComputerName : someserver.somecompany RemoteAddress : 172.17.253.106 RemotePort : 80 InterfaceAlias : vEthernet (WSL) SourceAddress : 172.17.240.1 TcpTestSucceeded : True
TcpTestSucceeded : True proves this is working.
TCP Forwarding from our WSL to our Private Network
From the configuration above we have connections coming in from
someserver.somecompany using our WSL IP address
172.17.240.1. We now need to forward these packets to the private network with the actual address of
someserver.somecompany which is
172.22.0.10. For this we’ll use socat which is a multipurpose relay in Linux.
socat TCP4-LISTEN:80,reuseaddr,fork TCP4:172.22.0.10:8000
Note, that the proxy listens on port 80 but proxies to the remote server on port 8000.
With this command running when connections come in from Windows on address:port
172.17.253.106:80 - our WSL IP address - they will be proxied to address:port
172.16.1.10:80. The address
172.16.1.10 is inside the subnet that is being served by our
sshuttle command above.
Hosting a Web Page on our Private Network
Since I have a limited setup I will add an additional IP to the remote SSH server (lloydrochester.com) and host a web page off this IP address.
sudo ip address add 172.22.0.10/32 dev eth0
Now setup from inside lloydrochester.com to the webpage.
$ cat index.html <html> <head></head> <body> <h1>Hello from a remote server!</h1> <p>Can you see this page from a Windows machine with WSL running sshuttle?</p> </body> </html> $ sudo python3 -m http.server --bind 172.22.0.10 Serving HTTP on 172.22.0.10 port 8000 (http://172.22.0.10:8000/) ...
From here I can open up a browser in Windows and see the following:
Where to go from here?
The setup required here is far from easy. It has multiple moving parts making it impractical for most use cases. I hope to post later proposing some better options. Here is what we can do to make these steps easier:
- Separate the Windows and WSL portions into their own scripts that have all of our commands.
- You can look at running
dnsmasqin WSL that will resolve addresses for us in WSL. Note, there is no easy way to do this resolution in Windows. We can point the Windows DNS to our WSL and
netsh interface portproxyDNS requests to our DSL, but this comes with additional complexity.