Cannot use SSH X11 forwarding inside container
Hi! first of all thanks for the great project.
I encountered a problem when trying to run openssh-server inside a container started with x11docker, and I want to confirm whether this behavior is expected, or whether x11docker can provide an option to support SSH X11 forwarding correctly.
1.Steps to Reproduce
1. Build a minimal test image containing OpenSSH server + Xterm
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y openssh-server xterm xauth
docker build -t ssh-x11-test .
This is a minimal environment intended only for verifying whether SSH X11 forwarding works inside an x11docker container.
2.Run the image using x11docker
Run the container with:
x11docker \
--network=bridge \
--name=tc \
--runasroot='mkdir /run/sshd && /usr/sbin/sshd -D \&' \
--runasroot="echo $(logname):1234 | chpasswd" \
-- --privileged -p 8022:22 -- \
ssh-x11-test
3. From the host, connect to the container via SSH with X11 forwarding
On the host machine, connect to the container using ssh with password 1234:
ssh -Y -p 8022 USER@CONTAINER_IP
once connected, you can verify environment with command:
echo $DISPLAY
Inside the SSH session, the DISPLAY variable is like DISPLAY=:120, However, This is the DISPLAY inherited from the x11docker-provided Xorg server inside the container, not the expected forwarded display such as DISPLAY=localhost:10.0
If I try to run Xterm with command xterm, The Xterm window appears inside the container’s Xorg display (the remote desktop), NOT on the external SSH client. Therefore, SSH X11 forwarding is not activated.
2.Closing Notes
I also performed the same tests using the x11docker/xfce image, and the result was identical — SSH X11 forwarding did not activate correctly, and the forwarded display was not created.
It is possible that I misunderstand how SSH X11 forwarding is expected to behave in an x11docker-managed environment, or that this behavior is intentional. If this setup is not supposed to work by design, please let me know.
I am aware that remote graphical access can be achieved through VNC or other methods; my intention here is simply to understand why SSH X11 forwarding does not function as expected inside containers started by x11docker.
Thanks for your time and for maintaining this project.
Did you see this wiki article? Maybe it helps: https://github.com/mviereck/x11docker/wiki/Remote-access-with-SSH
Yes, I’ve read that wiki page before. The method described there—using SSH X11 forwarding from the client to the host’s sshd—works well on my side.
Wiki scenario is like: (Client → Host)
ssh-client Host's sshd instance
| |
(1) |-- ssh -X HOST x11docker x11docker/xfce xfce4-terminal -->|
| |
(2) |<----------- xfce4-terminal GUI appears ------------------|
| |
Emm what I’m trying to achieve is slightly different: I would like to run an sshd instance inside the x11docker container, and then connect from the client to the container’s sshd with X11 forwarding enabled. In this setup the forwarded GUI applications should appear on the client, but instead the SSH session inherits the container’s internal DISPLAY (the one from the X server launched by x11docker), so X11 forwarding does not activate.
My scenario is like: (Client → Container (X11 forwarding via container sshd))
ssh-client Host Machine Container's sshd instance
| | |
(1) |----- ssh -X CONTAINER ------|--------------------------->|
| | |
(2) |----- xfce4-terminal ------|--------------------------->|
| | |
However xfce4-terminal GUI appears inside container X server,
not forwarded to host
So the wiki article addresses the host-side sshd scenario correctly, but my goal is to enable X11 forwarding through a container-internal sshd, which behaves differently.
So the wiki article addresses the host-side sshd scenario correctly, but my goal is to enable X11 forwarding through a container-internal sshd, which behaves differently.
ok, right.
Basically x11docker does nothing that should stop ssh -Y from working.
The only exception can happen if you use --hostdisplay that might need --xauth=trusted.
You could try --xoverip or even --xoverip=listentcp, maybe it helps.
Did you try without --network=bridge and without --privileged? That might cause issues.
Which X server is enabled if you run x11docker?
Thanks for the suggestions! I’ve tried several options, but the issue still persists. Here’s what I’ve tested so far:
I cannot remove --network=bridge because I need to connect via SSH from the host. I also tried --network=host, but the problem remains.
I did not use --hostdisplay; the container is running on the host's Xorg via x11docker (essentially --xc=no --xorg). I start x11docker directly from the command line so that Xorg takes over the tty.
I tried both --xoverip and --xoverip=listentcp, but neither helped.
Since running sshd requires chroot privileges, I cannot fully remove --privileged. At minimum, I need to keep --cap-add=SYS_CHROOT to allow sshd to start. Even with that, X11 forwarding over SSH still does not work.
The X server in use is the host’s Xorg started by x11docker, not a virtual framebuffer.
I am running out of ideas.
Maybe option --xtest could help to enable X option XTEST.
Maybe try --xauth=no
One point is that I did not work at this project for some years now, so I have to read the manual myself and forgot a lot.
If I try to run Xterm with command xterm, The Xterm window appears inside the container’s Xorg display (the remote desktop), NOT on the external SSH client. Therefore, SSH X11 forwarding is not activated.
Did you try to set DISPLAY manually to e.g. localhost:10?
Wow, I’m honestly surprised — the combination of --xauth=no and manually setting DISPLAY=localhost:10 actually works for enabling SSH X11 forwarding from inside the container?! I hadn’t expected that this would do the trick.
Great!
So it is basically an issue of environment variables.
Instead of insecure --xauth=no we should use file XAUTHORITY.
ssh should adjust the cookie in XAUTHORITY. However, it seems that it does not do it.
It might look for ~/.Xauthority, so I did a test run with --runasuser "ln -s \$XAUTHORITY \~/.Xauthority".
But that did not help.
So the question is, why ssh -Y does not set the environment variables and seemingly does not adjust the cookie file.
I would expect that xauth list shows two entrys, one with display number :10.
Edit: It seems that ssh ignores variable XAUTHORITY and uses a hardcoded ~/.Xauthority. That is anachronistic.
This helps: --runasuser "cp \$XAUTHORITY \~/.Xauthority"
Than ssh adjusts the cookie.
I have tried with ln -s instead of cp, but ssh deletes the softlink and creates a new file.
Might be worth a bug report at ssh developers. ssh should regard XAUTORITY and should not destroy softlinks.
I have still no idea why ssh -Y does not set the environment variables.
However, you can drop --xauth=no and set DISPLAY and XAUTHORITY manually.
Instead of --privileged just use --cap-default.
I've made an update to x11docker that creates /run/sshd and a softlink for ~/.Xauthority.
So this works if you set the environment variables manually:
x11docker --runasroot /usr/sbin/sshd --network --cap-default -- -p 8022:22 -- test
x11docker now creates a script in container /x11docker/sshenv that exports the environment variables and can be sourced after running ssh -Y.
After ssh login, just run source /x11docker/sshenv or . /x11docker/sshenv
However, fixing the issue on side of ssh would be better, it should set the variables itself.
Example file content:
$ cat /x11docker/sshenv
#! /bin/bash
# guessed environment variables for ssh -X access
# can be sourced after running ssh -X
export DISPLAY=localhost:10
export XAUTHORITY=/home/lauscher/.Xauthority
I have explained the setup in the wiki: https://github.com/mviereck/x11docker/wiki/Remote-access-with-SSH#ssh-x-forwarding-using-ssh-server-in-container
Yeah! Thank you for the quick fixes — I’ve tested the newly added /x11docker/sshenv helper script, and it works perfectly.
I suspect that the XAUTHORITY environment variable takes precedence over ~/.Xauthority.
This would explain why manually setting DISPLAY=localhost:10 didn’t work at first — the session was still using the XAUTHORITY file set by x11docker, which didn’t contain the forwarded cookie.
But once I used --xauth=no (which removes the XAUTHORITY variable), or manually set XAUTHORITY to point to ~/.Xauthority, then DISPLAY=localhost:10 started working immediately because SSH finally read the cookie from the file it actually updates.
However, it’s still unclear to me why ssh -X/-Y does not correctly set the DISPLAY and XAUTHORITY environment variables during session initialization. This behavior is still a bit puzzling.
Anyway, thank you again for the fix — I’m really happy to see new commits landing in the project after such a long time! I will go ahead and close this issue.
Hi, I’d like to reopen the previous issue related to SSH-X and XRDP failures.
After further investigation, I found the root cause seems to be:
Loading /etc/profile.d/10-x11docker-env.sh will override the correct environment variables (especially XAUTHORITY and DISPLAY) used by SSH-X XRDP and other X servers.
it seems like x11docker injects environment variables in two ways:
- Passed directly via
docker run --env ... - Written into
/etc/profile.d/10-x11docker-env.sh
The second method becomes problematic because:
Case A — SSH-X/-Y
SSH login that provides a login shell will automatically source /etc/profile → /etc/profile.d/*.sh. As a result, the script installed by x11docker (10-x11docker-env.sh) overwrites essential X11 environment variables:
-
XAUTHORITYis forcibly set to/x11docker/Xauthority.clientinstead of the correct per-user file such as~/.Xauthority. -
DISPLAYis overwritten to point to the external X server exported by x11docker (for example :100, :101, etc., rather than the X server started by SSH X forwarding.)
For example:
ssh -X/-Y user@container # X11 forwarding failure
However, non-login shells work fine, because they do not source /etc/profile or /etc/profile.d/*.sh, for example:
ssh -X user@container xclock # X11 forwarding success
Case B — XRDP Session
When launching a desktop session via XRDP (using either Xorgxrdp or Xvnc backend), xrdp-sesman starts the user session by executing /etc/xrdp/startwm.sh as default.
This script contains:
if test -r /etc/profile; then
. /etc/profile
fi
As a result, /etc/profile.d/10-x11docker-env.sh is sourced as well, overriding the session’s DISPLAY and XAUTHORITY variables in the same way as SSH login shells.
This causes the XRDP session startup to fail immediately, because the X server cannot access the correct authorization cookie.
Case C — Xpra works fine
I also tested running an Xpra session inside the container. Due to Xpra’s design, it launches a fully isolated session environment and does not source /etc/profile.d/*.sh. Therefore, its DISPLAY and XAUTHORITY values remain intact, resulting in a fully functional graphical session with no override issues.
Summary
I tested completely removing /etc/profile.d/10-x11docker-env.sh. After that, SSH -X/-Y forwarding does work correctly again.
However, since XAUTHORITY is no longer initialized in this case, XRDP sessions still fail unless XAUTHORITY=$HOME/.Xauthority is set manually, so I guess the right env file should be like:
# /etc/profile.d/10-x11docker-env.sh
export container=docker
export PULSE_COOKIE=/x11docker/pulseaudio.cookie
export PULSE_SERVER=unix:/x11docker/pulseaudio.socket
export HOME=/home/guiuser
export XAUTHORITY=~/.Xauthority # correct XAUTHORITY env
# export DISPLAY=:105 # avoid overwriting DISPLAY env
export USER=guiuser
Because I am still trying to fully understand all the internals and design decisions of x11docker, I feel that simply removing this file is a bit too “brute force” and might break other workflows. Therefore, I would like to reopen this issue and ask whether there is a more appropriate solution to avoid unintended environment overrides while still preserving the necessary settings for container-based GUI applications.
Thanks for your time and support!
Thank you for your detailed investigation!
I have kept the profile.d file, but as you suggest, x11docker does not set DISPLAY and sets XAUTHORITY to ~/.Xauthority.
(Side note, ~ does not work in scripts, x11docker sets an absolute path.)
The file /etc/profile.d/10-x11docker-env.sh is not needed essentially, it is just some sort of backup in case the variables set in docker run and elsewhere do not take effect for any reason.
I am not happy to use ~/.Xauthority, as HOME can vary in some setups. Tools like ssh and xrdp should regard the environment variable XAUTHORITY. So this is sort of a dirty workaround.
Thank you for the clarification!
Yes, it seems that XAUTHORITY does not affect the xauth entry writing behavior of SSH or XRDP—they still write entries to ~/.Xauthority. If they could write entries directly to /x11docker/Xauthority.client, I believe everything would work properly.
It would be desirable to eventually find a consistent solution regarding Xauthority handling. Perhaps it would make sense to keep this issue open for further discussion?
Perhaps it would make sense to keep this issue open for further discussion?
We can leave the ticket open.
It would be desirable to eventually find a consistent solution regarding Xauthority handling.
Maybe some day I'll open tickets at ssh and xrdp.