Host's Xwayland use by unpriviliged LXC container

Hi community,

I am having trouble to access the host’s Xwayland from inside an unprivileged container.
What I did/found so far:

  • lxc-create … --tempate download (ubuntu xenial) → done
  • config bridge, ip on host and inside containe → done
  • I can lxc-start my container and lxc-attach into it. Inside the container I do have internet connectivity.
  • I mount /tmp/.X11-unix from the host into my container (see config below). I do see its contents from inside the container, I can create,read and write regular files in /tmp/.X11-unix/ inside my container and they show up on the host as expected.

When I try to run xclock or xeyes I get the error:
Error: Can’t open display: :1

  • $DISPLAY inside container is set to same value as on host
  • xhost + (on host) before lxc-start does not. xhost + insode container gives same error message.
  • sudo’ing to a different user on host and running xeye works (with Xwayland)
    starting plasma on X11 insted of on wayland with Xwayland makes xclock work from inside the container (with the steps mentioned here)

Only the combination of lxc container on a wayland session (with Xwayland) does not work. What am I missing here?

I went through several tutorials/posts/man pages:

What would be the best way to debug this issue? It shows up only on wayland with Xwayland. On a X11 session on the host these issues disappear.

My config:

# Template used to create this container: /usr/share/lxc/templates/lxc-download
# Parameters passed to the template:
# For additional config options, please look at lxc.container.conf(5)

# Uncomment the following line to support nesting containers:
#lxc.include = /usr/share/lxc/config/nesting.conf
# (Be aware this has security implications)


# Distribution configuration
lxc.include = /usr/share/lxc/config/common.conf
lxc.include = /usr/share/lxc/config/userns.conf
lxc.arch = linux64

# Container specific configuration
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536
lxc.rootfs.path = dir:/home/k.nick/.local/share/lxc/xenial/rootfs
lxc.uts.name = xenial

# Network configuration
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.hwaddr = 00:16:3e:bf:d3:70

lxc.net.0.ipv4.address = 10.10.10.23/24
lxc.net.0.ipv4.gateway = 10.10.10.1



lxc.mount.entry = /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir
lxc.mount.entry = /tmp/.X11-unix tmp/.X11-unix none bind,optional,create=dir
lxc.mount.entry = /tmp/.X1-lock tmp/.X1-lock none bind,optional,create=file
lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file
lxc.mount.entry = /run/user/1000 mnt/wayland none bind,optional,create=dir
#lxc.mount.entry = /run/user/1000 run/user/1000 none bind,optional,create=dir

#lxc.mount.entry = /tmp/.X11-unix tmp/.X11-unix none bind,optional,create=dir

In Incus, I’m using abstract unix socket for xWayland (notice the @):

incus config device add <container_name> xwayland_socket proxy bind=container connect=unix:@/tmp/.X11-unix/X1 listen=unix:@/tmp/.X11-unix/X1 security.uid=1000 security.gid=1000

It doesn’t show up in /tmp/.X11-unix/ folder like a regular socket. The 1000 in security.uid=1000 and security.gid=1000 is the UID and GID of the host user.

Sounds interesting. On your notice I did some further digging and came up with some nice discussion:

However I could not find any way to implement your incus solution on plain lxc. Any idea what something like that would look like?

The abstract Unix socket functionality in Incus is implemented using the proxy device in Incus. When you use a proxy device, Incus spawns a separate process that facilitates the two-way transfer. The proxy device is quite versatile. Compared to a bind mount, a proxy device (with the separate process) could incur some delays since there is a separate process involved. However, no one has been complaining which is good.

Therefore, for LXC one would need something that is supported there. Also, go into the process of debugging. When a process is opening a Unix socket, the other side know what is the UID of the first process. There is access control on the UID. You need to make sure that you get that right as well.

Thanks a lot for this hint. For now I am issuing a xhost + command which is supposed to disable all access control mechanisms. A test with a sudo into a different user account proves that this approach is effective - that other user is able to start graphical tools (like xterm, xclock,…).

Unfortunately this does not work for tools from within a container.

So your mentioning of the proxy device in incus gets me thinking: while it seems to be an elegant solution to this kind of problem, I wonder why this kind of funcionality seemingly has not been implemented in lxc. At least I can not find it being mentioned any where.

A proxy device is quite advanced and departs from the simplicity you get with LXC. With LXC, you run commands and they complete as soon as you get back your prompt. With the proxy device you get a guarantee from Incus that when you start that container, you get the proxy device(s) up and running. You do not have the supporting facilities in LXC to have that functionality.

1 Like