X11 Forwarding
Even more detailed instructions
Because of incompatibility between Windows and Linux, the process to do this is a bit convoluted. For Windows 10, a quick and dirty way to forward X11 from remote Linux computer is specified here. Initial configuration as follows:
Local Windows
- Download an X11 server, e.g. VcXsrv, run it, and set the display number to 0.
Remote Linux
- SSH into the remote server and verify in
/etc/ssh/sshd_config
:- X11 forwarding is enabled:
X11Forwarding yes
- Check X11DisplayOffset value, e.g. mine says
X11DisplayOffset 10
- Restart sshd if configuration changed, e.g.
sudo systemctl restart sshd
- Download any X-based software to test X11 connection later
- e.g.
xeyes
usingsudo apt install x11-apps
To open an X11 connection:
- In Windows, open Powershell and load the display variable with the initial display number, e.g.
$env:DISPLAY = "localhost:0"
- SSH into the remote server with insecure X11 forwarding, e.g.
ssh -Y [USER]@[IPADDR]
- Set the display variable to offset (10, in my case) plus the display number in Windows, e.g.
export DISPLAY=localhost:10
- Run an X-based software and see if the software loads in an individual window in the local Windows, e.g.
xeyes
- Profit
To summarize as an example:
powershell> $env:DISPLAY = "localhost:0" powershell> ssh -Y justin@192.168.0.10 bash$ export DISPLAY=localhost:10 bash$ xeyes
After diving down the rabbit hole, I'm pretty confident about how this X11 forwarding works now. Written in the middle of the night though - I can't be trusted to write coherently.
This article specifically addresses how X11 is forwarded through an SSH connection, not how the protocol works. If interested in the graphical primitives, see this amazing series of articles by Jasper St. Pierre that even provide illustrations using a browser-based X server. A nice summary can also be found here, while the history of X is broadly described in Wikipedia's article.
Alternatives to X11 today (with network transparency) generally fall under the camp of VNC or RDP, which alternative windowing systems such as Wayland and Mir may provide (though their primary usage is still graphical display on the local host).
tl;dr How to setup X11 forwarding on Windows to remote Linux?
- Ensure remote machine has
xauth
installed (in order to run X clients). Optionally install lightweight X11 apps for testing (e.g.x11-apps
in Ubuntu forxeyes
) - Modify
/etc/ssh/sshd_config
on remote to includeX11Forwarding yes
- Add the
DISPLAY
environment variable with the valuelocalhost:0.0
- Add
ForwardX11 yes
andForwardX11Trusted yes
into SSH config for your remote machine - Download VcXsrv and run
XLaunch
with default values - Login to the remote and run your X11 app
Do not perform X11 forwarding to untrusted computers!
Terminology
X Window System
X is a networked windowing environment that works on a server-client basis. An X server manages access to graphical displays and input devices on the host computer, to which X clients can connect to and send graphical data (primitives) / receive user input. Implementations of an X server tend to follow that of Xorg's reference implementation.
In a remote context, the remote system is typically a headless server providing the graphical data, while the user sits at the local system interacting with the graphical windows. The X server hence sits at the local system, while the X client runs on the remote server.
X11
X11 is the 11th edition of the X Window System network protocol, which provides network transparency, allowing X clients and servers to communication over local networks (with Unix sockets or loopback TCP addresses) as well as remote networks (via TCP addresses).
This network protocol is often used due to its early adoption, and strong guarantees that many OS supports it.
xhost
xhost
is a program that provides host-based access control to the X server. By default the xhost
list is empty and only local connections from the same machine can connect to the X server. This is still a terrible default since it does not prevent other users on the same machine from connecting as well1). Worse still is the use of xhost +
which allows connections from anywhere.
xauth
xauth
is a program that allows import and export of a short key called a magic cookie that can be shared between clients and servers for communication2). The server generates the key and sends it to the client. The client attaches this key whenever graphical data is sent to the server, which the server validates before display.
A common and sufficient security mechanism is called MIT-MAGIC-COOKIE-1
, which generates a 128-bit cookie and stored in the .XAuthority
file in the user's home directory. The magic cookie can be forwarded via a number of mechanisms including the xrsh
program, or sent through an SSH tunnel which is what this article is about :)
xinit
xinit
is a program that manually starts an X server. The startx
script is the front-end for xinit
, and is typically called by a display manager (to activate the window manager). ~/.xinitrc
provides some level of configuration for the X server.
SSH X11 forwarding flow
This Teleport article provides a well-rounded explanation, which I will attempt to modestly summarize.
We simplify the network components into the following:
- The user sits at a local machine who wants to display a GUI from a remote machine.
- The local machine hosts an X server, and initiates an X11 forwarding connection as an SSH client.
- The remote machine hosts an SSH server listening to clients, and can run X client programs that talk to the local X server.
Initial forwarding
Local machine sends an SSH connection request to the remote SSH server (sshd), together with a request to forward X11. If SSH server is configured to allow X11 forwarding, a $DISPLAY
environment variable is set on the remote.
Suppose:
$DISPLAY == localhost:13.0
on a remote Unix machine$env:DISPLAY == localhost:0
on a local Windows machine
then the (remote) SSH server listens to localhost:6013
for incoming X11 commands. The SSH connection proceeds on port 22 as usual. On the local machine, the SSH client will forward X11 commands to localhost:6000
port (which the X server is listening on).
By default -Y
bypasses all SECURITY
extensions in X11, and ignores missing .XAuthority
files (any X11 commands tunneled through the SSH connection is accepted by the X server). Fake authentication data is sent over the SSH tunnel. If the untrusted flag -X
is used:
- Local
xauth
first creates a dummy (not fake!) xauth key and sends it to the remote SSH server. Mapping is kept in.XAuthority
- SSH server uses remote
xauth
to store in remote user's.XAuthority
file as well (with appropriate read permissions) - The cookie/key will be marked "untrusted" instead of the usual "trusted", which limits the reach of X clients
Once X11 forwarding is setup, the SSH connection proceeds as usual. The entire SSH connection can be viewed with:
ssh -vvv -X ...
X client request
When an X client sends a draw request (say by the user through the SSH connection), the previous xauth
key is attached to the X11 command, and both are sent to localhost:6013
(note that X clients without the .XAuthority
file simply send the request to the same port but without the key - this is the case for non-root users on the same machine). The SSH server receives this and forwards it to the SSH client, which then proceeds in one of two ways:
- If
-X
is supplied during X11 forwarding and the xauth key sent by the remote does not match that in the.XAuthority
file, the request is dropped entirely. - Otherwise, the request is forwarded to
localhost:6000
as per specification above to the X server.- With
-Y
, all requests, regardless of the xauth key attached, will be forwarded. - With
-X
, the dummy xauth key is replaced with a real xauth key to the X server.
Achieving X11 forwarding via SSH
A quick graphical brief of what SSH X11 forwarding is all about can be found here.
Enable X11 forwarding on SSH server
The SSH daemon (sshd) needs to support X11 forwarding, which is easily done by adding X11Forwarding true
in /etc/ssh/sshd_config
. Note also the configuration X11DisplayOffset 10
that modifies the TCP address / socket mapping, e.g. remote DISPLAY will start from localhost:10
instead of localhost:0
.
Set DISPLAY environment variable
The DISPLAY environment variable first needs to be set to allow the SSH client to know where to forward X commands to. If no DISPLAY variable is set, the following debug message will appear in the SSH X11 forwarding.
debug1: X11 forwarding requested but DISPLAY not set
The variable (DISPLAY
for cmd.exe, $env:DISPLAY
for Powershell) can be set on a session-basis using set
, or made persistent via setx
. An alternative graphical method is using the "Edit the system environmental variables" GUI and add to either the user or system environment variables (necessary if using programs that automatically spin up their own sessions for SSH).
The format of DISPLAY is [HOSTNAME]:[DISPLAY].[SCREEN]
, where one (virtual) display can have multiple screens. Unix implementations of xauth assume the unix
socket as the default host - in Windows, the loopback addresses 127.0.0.1
(or alternatively localhost
) must be explicitly specified. The screen number is optional and 0
by default if not specified. Possible mapping of DISPLAY to corresponding TCP / Unix endpoints for display number of 13 (hence good to be precise).
$DISPLAY | Windows | Unix |
---|---|---|
localhost:13.0 | localhost:6013 | localhost:6013 |
:13 | unix:6013 (sad) | /tmp/.X11-unix/X13 |
unix:13.0 | - | /tmp/.X11-unix/X13 |
VcXsrv uses display number of 0 by default as well (when unspecified, i.e. -1, and no other X servers are running), so DISPLAY typically comes to localhost:0
on a Windows system.
The remote DISPLAY value is negotiated by the SSH server by default, do not modify it unilaterally. This provides a nice test if X11 forwarding is working, by checking with echo $DISPLAY
and see if anything appears.
Enable X11 forwarding
SSH does not forward X11 by default, for security reasons. This can be enabled on a per-session basis using -X
/ -Y
in the SSH command, or globally by modifying the $HOME/.ssh/config
file to include the ForwardX11 yes
/ ForwardX11Trusted yes
lines. For example:
- ~/.ssh/config
# ForwardX11 == '-X' / ForwardX11Trusted == '-Y' # Change to 'Host *' to apply to all hosts in config Host 192.168.1.2 ForwardX11 yes ForwardX11Trusted yes
For untrusted X11 forwarding, the magic cookie needs to be generated by xauth
first. The path to xauth
must be manually specified when using VcXsrv, like so:
- ~/.ssh/config
# 'C:\Progra~1' is synonymous to 'C:\Program Files' Host 192.168.1.2 ForwardX11 yes XAuthLocation C:\Progra~1\VcXsrv\xauth.exe
In Visual Studio Code, note that using trusted X11 forwarding -Y
requires the untrusted X11 forwarding -X
flag as well.
Run X server on local machine
For VcXsrv, the XLaunch.exe
program starts a wizard to allow specification of display number (default of -1 maps to display number 0, i.e. localhost:6000
listener), as well as other options. Do not enable "Disable remote access", since all X11 commands are internal (after tunneling through the SSH connection).
After doing the SSH X11 forwarding, run X clients such as xeyes
to test X11 forwarding works.
Why we still use -Y
Quoting from OpenSSH implemented in Debian:
Debian-specific: X11 forwarding is not subjected to X11 SECURITY extension restrictions by default, because too many programs currently crash in this mode. Set the ForwardX11Trusted option to "no" to restore the upstream behaviour. This may change in future depending on client-side improvements.
Several Unix systems today use trusted X11 forwarding by default when using the -X
(untrusted X11 forwarding) flag. Using -Y
however is bad, since rogue X clients can do things like takeover the X server, or do keylogging, or take screenshots of local display. This article goes further to argue untrusted X11 forwarding is still dangerous since the X server is running as root.
Bottomline, do not use X11 if you cannot trust the remote. Use alternatives like VNC and RDP instead.
Windows
As of September 2022, Windows 10 still requires a third-party X server for X11 capabilities. Popular options include:
- VcXsrv is open-source and actively maintained
- MobaXterm has an embedded X server and packaged with other features, but is proprietary
- Upgrading to Windows 11 which has native X11 support
Some bugs on VcXsrv implementation that require notice:
- VcXsrv's
xauth
usesunix
socket as the default endpoint, which is not available in Windows (should use the loopback addresslocalhost
/127.0.0.1
instead). - In untrusted scenarios,
mktemp_proto
fails because of writes toTMPDIR
which is typically linked to/tmp
directory in Unix, and this directory does not exist in Windows. Can be circumvented by setting theTMPDIR
environment variable to any user-writable location (conventionallyC:\Temp
).
The command executed by the X servers in particular when using -X
is as shown in the debug log:
debug2: client_x11_get_proto: C:\cygwin64\bin\xauth.exe -f C:\Temp/ssh-ZLktm6tmnewa/xauthfile generate unix:0.0 MIT-MAGIC-COOKIE-1 untrusted timeout 1260 2>/dev/null
Warning: untrusted X11 forwarding setup failed: xauth key data not generated
This fails for different reasons: sometimes /dev/null
is not defined (can create a C:\dev\null
file perhaps), sometimes it's NUL
instead (and fails on Powershell), the %TMPDIR%/ssh-[RANDOMSTRING]
directory may need to exist beforehand, etc. Issues have been raised in Win32-OpenSSH (and another), in VSCode Remote-SSH plugin, in OpenSSH Portable.