X11 Forwarding and Windows Subsystem for Linux 2
Thomas Ward April 19, 2021WSL2 X11 Forwarding Guide Summary
The below guide documents how to install Windows Subsystem for Linux 2 (WSL2) and configure X11 forwarding so graphical programs run from inside WSL2 display locally on Windows. Unlike the other existing guides, it does not let Windows Firewall open up the X11 display server port to the entire world, so you have improved security.
This guide will walk you through:
- Installing WSL2
- Configuring Windows firewall to only allow connections to the X11 display server from the WSL2 instance running on your computer.
- Installing an X11 display server, VcXsrv
- Configuring WSL2 to auto-update the firewall rule when IP addresses change and know how to send graphical output to VcXsrv.
Install Linux (WSL2)
Installation instructions are available from microsoft here. I recommend doing the manual steps to avoid having to join the Windows Insider Program and install a preview build of Windows. Make sure to follow the instructions for WSL2, not 1. You can pick any Linux distribution you want. The instructions below are confirmed to work with Ubuntu.
Configure Windows Firewall
You will now need to configure Windows Firewall to permit connections from WSL2 to the X11 display server. You will install the display server in the next step. We do this step first to avoid Windows Firewall from auto-creating an insecure firewall rule when you run the X11 display server. Many guides on X11 forwarding and WSL2 make this firewall rule too permissive, allowing connections from any computer to your computer. This means someone could theoretically, if they are on your same network, start sending graphical display information to your computer.
To avoid this, we will make Windows Firewall only accept internet traffic from the WSL2 instance.
To set this up, you can copy the below to a script and run it from within WSL2:
#!/bin/sh
LINUX_IP=$(ip addr | awk '/inet / && !/127.0.0.1/ {split($2,a,"/"); print a[1]}')
WINDOWS_IP=$(ip route | awk '/^default/ {print $3}')
# Elevate to administrator status then run netsh to add firewall rule
powershell.exe -Command "Start-Process netsh.exe -ArgumentList \"advfirewall firewall add rule name=X11-Forwarding dir=in action=allow program=%ProgramFiles%\VcXsrv\vcxsrv.exe localip=$WINDOWS_IP remoteip=$LINUX_IP localport=6000 protocol=tcp\" -Verb RunAs"
Alternatively, you can manually add the rule through a GUI by doing the following:
- Open "Windows Defender Firewall with Advanced Security"
- Click add new rule brings up the New Rule Wizard (next to navigate between each section):
-
Rule type: Custom
-
Program: "This program path:"
%ProgramFiles%\VcXsrv\vcxsrv.exe
-
Protocol and ports
-
Protocol type: TCP
-
Local port: 6000
-
Remote port: any
-
Scope
- Local IP address: Obtain the IP address to put in by running the below command in WSL2
ip route | awk '/^default/ {print $3}'
-
remote IP addresses
- Obtain IP address to enter by running the below in WSL2
ip addr | awk '/inet / && !/127.0.0.1/ {split($2,a,"/"); print a[1]}'
-
Action: "Allow the connection
-
Profile: Selection Domain, Private, and Public
-
Name: "X11 forwarding"
-
If you already had installed an X11 server, Windows may have created firewall rules that will mess with the above configuration. Search for them and delete them in "Windows Defender Firewall with Advanced Security."
Install an X11 server, VcXsrv
Graphical programs run inside linux need a "display server" that will do the hard work of displaying the program and processing mouse and keyboard input. On Linux, traditionally this has been done by the X Window System, X11. X11 was designed in an era where most programs, called clients, were run on a powerful central computer. The clients graphical output was then sent over the network to the user's computer then displayed on its monitor by the "display server."
For our setup, the WSL2 linux programs that display graphics will be our "clients." For example, you are running R in WSL2 and want to display plots. They will send information to Windows, and therefore you need to install an X11 server on Windows that will take their information and display it graphically.
I recommend installing VcXsrv Windows X Server. When you install it, accept the default options. It will not autostart or run after installed. If there is an option to start VxSsrv after install, do not accept this option.
Configure VxXsrv to Autostart
You now can start the X11 display server, VxXsrv. I recommend making it automatically start with windows so you do not need to manually start it each time you restart your computer.
To create an autostart for VcXsrv:
-
WIN + R
-
type
shell:startup
(launches you to the windows file explorer) -
Right click >
new
>shortcut
-
Enter:
"%ProgramFiles%\VcXsrv\vcxsrv.exe" :0 -multiwindow -clipboard -wgl -ac
:0
says to create instance listening on port 6000- Runs server in multiwindow mode (so each window you can click, drag, and resize independently)
- clipboard: enables clipboard integration
- wgl: enables Windows WGL interface for hardware-acceleration
- ac disables access control (insecure anyways. Will use strict firewall rules instead)
- More documentation here
-
Name it whatever you like (I called it
X11
) -
Double click it to run it now
-
It will always run now when you start
Configure WSL2 to send graphics to VcXsrv
Almost done!
Last, you need to configure WSL2 to know where the X11 display server is located for graphical client information.
To do so, you need to configure WSL2 to:
- Know where the X11 server is
- Render the graphics on Windows, not Linux side
- Reconfigure the Windows firewall if the WSL2 IP address changes.
The WSL2 IP address frequently changes, which causes problems for a lot of people. It also is the reason that most guides to setup X11 forwarding make the Windows firewall rules too permissive, and thereby open your computer to the world to access.
We can solve all the above problems by putting the following at the end of your ~/.profile
:
# IP addresses for currently running Linux and Windows systems
LINUX_IP=$(ip addr | awk '/inet / && !/127.0.0.1/ {split($2,a,"/"); print a[1]}')
WINDOWS_IP=$(ip route | awk '/^default/ {print $3}')
# IP addresses in current windows defender firewall rule
# netsh outputs line of "^(Local|Remote)IP:\s+IPADDR/32$" so get second field of
# 'IPADDR/32' and split it on '/' then just print IPADDR
FIREWALL_WINDOWS_IP=$(netsh.exe advfirewall firewall show rule name=X11-Forwarding | awk '/^LocalIP/ {split($2,a,"/");print a[1]}')
FIREWALL_LINUX_IP=$(netsh.exe advfirewall firewall show rule name=X11-Forwarding | awk '/^RemoteIP/ {split($2,a,"/");print a[1]}')
# Update firewall rule if firewall rules IPs don't match actual ones
if [ "$FIREWALL_LINUX_IP" != "$LINUX_IP" ] || [ "$WINDOWS_IP" != "$FIREWALL_WINDOWS_IP" ]; then
powershell.exe -Command "Start-Process netsh.exe -ArgumentList \"advfirewall firewall set rule name=X11-Forwarding new localip=$WINDOWS_IP remoteip=$LINUX_IP \" -Verb RunAs"
fi
# Appropriately set DISPLAY to Windows X11 server
DISPLAY="$WINDOWS_IP:0"
# Tell X11 programs to render on Windows, not linux, side
# docs: https://docs.mesa3d.org/envvars.html
LIBGL_ALWAYS_INDIRECT=1
export DISPLAY LIBGL_ALWAYS_INDIRECT
All set. Sometimes when you launch a new terminal window, you will notice a slight delay and a Windows User Account Control box asking if you want "netsh.exe" to make changes to your advice. This is normal, so always click "Yes" as this is WSL2 updating the firewall rules to keep X11 forwarding working.
Test out your new setup by running a graphical application in WSL2.
An easy option is to start R and run hist(cars$dist)
.
A histogram should appear:
Enjoy X11 forwarding from WSL2 to Windows.
Comments, questions, input, concerns?
I hope this article helps! Please contact me with any questions or input on the article using any of the methods on my contact page.