How X over SSH really works
Imagine you are sitting in front of a machine named "home" that has a keyboard, mouse, display, and is running an X server. Now you open a terminal and ssh to a machine called "remote"
(which doesn't need to have an X server running), and run a program
like "firefox" which pops a window in your screen. How does this all
work?
First, let's clear up the client/server terminology confusion. When talking about X:
- X client: a process (like firefox or xemacs) which uses the X client API to display things and receive mouse/keyboard events.
- X server: a process (usually just "X") which X clients connect to. At times (as we'll see below) other processes can act as X servers.
user@home: echo $DISPLAY
:0 # hostname is "localhost" by default
user@home: xcalc # pops up a calculator on my screen
user@home: DISPLAY="nosuchhost:99"
user@home: xcalc
Error: Can't open display: nosuchhost:99
:0 # hostname is "localhost" by default
user@home: xcalc # pops up a calculator on my screen
user@home: DISPLAY="nosuchhost:99"
user@home: xcalc
Error: Can't open display: nosuchhost:99
Now let's see what happens when you ssh to another machine:
user@home: ps aux | grep X # yep, X server running @home
root ... /usr/bin/X :0 ...
user@home: ssh -X user@remote # -X enables X-forwarding
user@remote: ps aux | grep X # nope, no X server here
user@remote: echo $DISPLAY
:11.0
user@remote: xcalc # pops up screen @home
root ... /usr/bin/X :0 ...
user@home: ssh -X user@remote # -X enables X-forwarding
user@remote: ps aux | grep X # nope, no X server here
user@remote: echo $DISPLAY
:11.0
user@remote: xcalc # pops up screen @home
Wait a minute, the $DISPLAY variable is pointing to the localhost ("remote"). Natural questions to ask at this point:
- There is no X server @remote, so why didn't xcalc just fail on startup?
- Why was the display number "11".
- How did xcalc show something @home?
user@remote: ps aux | grep user
root ... sshd:user@pts/11
root ... sshd:user@pts/11
What's happening is that there's an "X emulator" running @remote that was setup just for your ssh session, that is listening on display 11.
To review, here's a play-by-play:
- You type "ssh -X user@remote" in your terminal
- The ssh process connects to the sshd server @remote.
- sshd spawns a new process that is an X-server-emulator listening on some display number, e.g. "11"
- sshd sets the $DISPLAY to point to that local "X-server" (e.g. ":11")
- xcalc reads this $DISPLAY and conncects to this X-server. xcalc thinks it's displaying to the local machine.
- the X-server-emulator simply forwards the X commands from xcalc through the ssh connection, to the original ssh process.
- The ssh process @home now acts as a normal X-client and sends those commands to the X-server @home.
user@remote: DISPLAY="home:0"
user@remote: xcalc
user@remote: xcalc
This doesn't work because the X server @home won't let other hosts connect to it. To change this (not that you should -- see below) you can do:
user@home: xhost +remote
xhost
is a command which says "that host can connect to our X-server".
However, everybody uses ssh X forwarding instead. Here are some security reasons why:
- Normally, X-traffic (like your keystrokes) is sent unencrypted from X-client to X-server.
- Ssh nicely sends that data through an encrypted channel, so it doesn't go over the internet in the clear.
- "xhost +remote" is putting a lot of trust in 'remote' being a nice guy. If remote ever gets hacked, it could connect to the X-server @home and listen to all its keystrokes.
Other notes for the curious:
- if $DISPLAY is set to "localhost:0" (or any explicitly named host) it uses tcp-ip to send the X-traffic locally.
- if $DISPLAY is just ":0" it uses a special (more efficient, non-tcp-ip) connection.