marbetschar / tins

Containers just like Virtual Machines - but with native performance
GNU General Public License v3.0
40 stars 3 forks source link

Support starting a Desktop Environment in a Window #16

Open marbetschar opened 4 years ago

marbetschar commented 4 years ago

It should be possible to start another Desktop Environment from a container in a window.

marbetschar commented 4 years ago

We now have a first, super rough draft for running X11 environment in master.

Although the Desktop Environment is not preinstalled, it works if we install and configure it following along the code of ContainerBox: https://github.com/AlexandreDey/ContainerBox/blob/master/etc/creator/templates/ubuntu

It seems LightDM is the key here to forward the Session to Xephyr. Also, starting the things in the right order seems to be crucial.

marbetschar commented 4 years ago

The following issues needs still to be solved:

Wait for cloud-init:

With the following command we can wait for cloud-init to complete: cloud-init status --wait and with cloud-init status we can probably get if it was successful.

Use pre-built images

Would be the better way to set things up as it provides a better UX. Opened issue for this: https://github.com/lxc/lxc-ci/issues/154

marbetschar commented 4 years ago

First success ๐ŸŽ‰๏ธ

Unfortunately cloud-init breaks way too often, seems like nearly on every install some package was not downloaded correctly, creates an unpackage error and therefore sabotages the whole installation experience.

Screenshot from 2020-05-15 00 13 26

marbetschar commented 4 years ago

Maybe there is some more we can learn from the x11docker project: https://github.com/mviereck/x11docker

marbetschar commented 4 years ago

Fossa seems to work better than Bionic, but still highly unreliable. We need to provide images with the desktop pre-installed - otherwise this does not seem to work good enough. But still some progress:

Screenshot from 2020-05-16 12 59 52

marbetschar commented 4 years ago

Rough outline of the desktops and platforms we should support from the beginning. This list is just an idea and can change at any time:

marbetschar commented 4 years ago

Thanks to the input from here, we are now able to start Wayland and Xwayland on host. Still need to find the proper way to map the newly created X server into the container though:

$ lxc exec Xfce -- xclock
Error: Can't open display: :0

But if we look at the container config, the environment variables are set:

$ lxc config show Xfce
architecture: x86_64
config:
  environment.DISPLAY: :0
  environment.PULSE_SERVER: unix:/home/marbetschar/pulse-native
  environment.XDG_RUNTIME_DIR: /run/user/1001
  environment.XSOCKET: /tmp/.X11-unix/X0
  image.architecture: amd64
  image.description: Ubuntu focal amd64 (xfce) (20200522_0751)
  image.name: ubuntu-focal-amd64-xfce-20200522_0751
  image.os: ubuntu
  image.release: focal
  image.serial: "20200522_0751"
  image.variant: xfce
  user.user-data: |
    #cloud-config tins-default
    groups:
      - sudo
      - wheel
    users:
      - name: marbetschar
        groups: [sudo,wheel]
        sudo: ALL=(ALL) NOPASSWD:ALL
    packages:
      - sudo
    runcmd:
      - groupmod --gid 1001 marbetschar
      - usermod --gid 1001 --uid 1001 --password $(openssl passwd -1 marbetschar) marbetschar

    #cloud-config tins-x11 extends tins-default
    packages:
      - x11-apps
      - mesa-utils
      - pulseaudio
    runcmd:
      - sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf
  volatile.base_image: 6dd2a2f95451b569ecfb496faf482342981b93ae800c9c8cf3f93062d1a06352
  volatile.eth0.hwaddr: 00:16:3e:85:e5:df
  volatile.idmap.base: "0"
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":231072,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":231072,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":231072,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":231072,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.power: RUNNING
devices:
  X0:
    path: /tmp/.X11-unix/X0
    source: /tmp/.X11-unix/X1
    type: disk
ephemeral: false
profiles:
- default
- tins-default
- tins-x11
stateful: false
description: ""

On host, the X1 socket is created:

$ ls -latrh /tmp/.X11-unix/
total 8.0K
srwxrwxrwx  1 root        root           0 Mai 24 21:26 X0
srwxrwxrwx  1 marbetschar marbetschar    0 Mai 27 15:14 X1

And in the container, the X0 socket is also available - don't know if the permissions are correct though:

$ lxc exec Xfce -- ls -latrh /tmp/.X11-unix
total 0
drwxrwxrwt 1 root   root      4 May 27 13:03 .
srwxrwxrwx 1 nobody nogroup   0 May 27 13:14 X0

And the environment variables in the container:

$ lxc exec Xfce -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
container=lxc
XDG_RUNTIME_DIR=/run/user/1001
USER=root
LANG=C.UTF-8
XSOCKET=/tmp/.X11-unix/X0
PULSE_SERVER=unix:/home/marbetschar/pulse-native
DISPLAY=:0
TERM=xterm-256color
HOME=/root
toby63 commented 4 years ago

srwxrwxrwx 1 nobody nogroup 0 May 27 13:14 X0

Well for me its root (inside container): srwxrwxrwx 1 root root 0 May 27 16:19 X0

Also (as I already said) simos is favoring proxys:

X0:
    bind: container
    connect: unix:@/tmp/.X11-unix/X1
    listen: unix:@/tmp/.X11-unix/X0
    security.gid: "1000"
    security.uid: "1000"
    type: proxy

Source: https://blog.simos.info/running-x11-software-in-lxd-containers/

marbetschar commented 4 years ago

@toby63 I'm aware of the proxy approach and ultimately its the goal to use it, as its the recommended way. But in earlier testings the proxy approach did not work, because LXD was complaining about invalid properties (I guess LXD v3.0.3 does not support all needed proxy configuration properties yet).

As Tins is primarily for elementary and elementary 6.0 is not released yet, I have to stick with the "old" approach for now.

toby63 commented 4 years ago

As Tins is primarily for elementary and elementary 6.0 is not released yet, I have to stick with the "old" approach for now.

Ok, I understand. But I have some experience using the different approaches of simos and the disk-method does not seem to work so well anymore (I don't know why that is though :man_shrugging:). So I switched to a different proxy-method (because the abstract Unix sockets simos is using were not working for me):

X0:
    bind: container
    connect: unix:/tmp/.X11-unix/X0
    listen: unix:/mnt/X11-unix/X0   
# this is because of some problems described here: https://github.com/lxc/lxd/issues/4540; you can try using the usual /tmp-path
    gid: "0"
    uid: "0"
    mode: "0777"
    security.gid: "1000"
    security.uid: "1000"
    type: proxy 

Just for testing, you might try these two proxy approaches.

Also: Can't you use snap on Elementary too? This way you could install a newer version of lxd :thinking:.

marbetschar commented 4 years ago

@toby63 unfortunately I get the following errors:

Error 500: Invalid devices: Invalid device configuration key for proxy: security.gid

Or if I remove security.gid and security.uid:

Error 500: Invalid devices: Invalid device configuration key for proxy: gid

And if I remove gid and uid along with mode:

Error when starting proxy device 'X0' for container Xfce: Proxy device doesn't support the connection type: unix

Snap would be an option, yes. But I was hoping for as much automation as possible - and when packaging for AppCenter as *.deb I can add simply add lxd as dependency. Also don't know if a snap is easier to get going, as I expect other permission issues cause the package is in its own "sandbox', right?

marbetschar commented 4 years ago

I guess I fixed the Display Server permission issue by adding the raw.idmap. Unfortunately for this to work the user needs to execute the following shell command once (added to the README):

# allow lxd to remap your user id into a container:
echo "root:$UID:1" | sudo tee -a /etc/subuid /etc/subgid

Now the permissions of X0 in the container seem correct:

$ lxc exec Xfce -- ls -latrh /tmp/.X11-unix
srwxrwxrwx 1 1001 marbetschar   0 May 27 19:19 X0

Still not able to sucessfully execute xclock from container though:

$ lxc exec Xfce -- su --login marbetschar -c "DISPLAY=:0 xclock"
No protocol specified
Error: Can't open display: :0

Although on host it works. $ DISPLAY=:1 xclock leads to the following result:

Screenshot from 2020-05-27 21-24-36

mviereck commented 4 years ago

No protocol specified Error: Can't open display: :0

This is an authentication issue. For a quick check, run insecure DISPLAY=:1 xhost + on host. For a secure setup, you'll need to set up a cookie.

marbetschar commented 4 years ago

Authentication does not seem to be the root cause here. I tried as suggested, but still get the same error in container.

Fun fact: As soon as Xwayland runs, every new window which is opened on host does get send to it and is rendered in DISPLAY=:1 - which obviously is not the desired behaviour ;)

Will continue to experiment and have another look at the x11docker code, seems like I'm missing some tiny bits to get this working correctly.

marbetschar commented 4 years ago

Generating the X client cookie now on host, passing it to Xwayland as well as mounting it into the container. Also mapping X1 -> X1 in the container as suggested (for details see this commit).

But trying to run xclock still fails:

$ lxc exec Xfce -- xclock
No protocol specified
Error: Can't open display: :1

From what I can tell, the environment in the container looks good:

$ lxc exec Xfce -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
container=lxc
PULSE_SERVER=unix:/home/marbetschar/pulse-native
TERM=xterm-256color
HOME=/root
USER=root
LANG=C.UTF-8
DISPLAY=:1
XAUTHORITY=/home/marbetschar/.Xauthority

What seems a bit weird, the environment of the container user looks entirely different - don't know if this is relevant though:

$ lxc exec Xfce -- su --login marbetschar -c env
MAIL=/var/mail/marbetschar
USER=marbetschar
XDG_SESSION_TYPE=unspecified
HOME=/home/marbetschar
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
LOGNAME=marbetschar
XDG_SESSION_CLASS=background
TERM=xterm-256color
XDG_SESSION_ID=c2
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
XDG_RUNTIME_DIR=/run/user/1000
LANG=en_US.UTF-8
SHELL=/bin/sh
PWD=/home/marbetschar
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
mviereck commented 4 years ago

But trying to run xclock still fails:

Try DISPLAY=:1 xhost + on host. That should remove (nearly) all restrictions.

Fun fact: As soon as Xwayland runs, every new window which is opened on host does get send to it and is rendered in DISPLAY=:1 - which obviously is not the desired behaviour ;)

What is your host desktop environment? I once got a bug report with GNOME on host with the same issue. GNOME often does strange things. Maybe it does not regard DISPLAY as it should. Does DISPLAY=:0 xclock work as intended or does it appear on :1?

marbetschar commented 4 years ago

@mviereck you were right, its indeed a permission/authentication issue: There was an issue which lead to a wrong UID, so that probably prevented authentication as well.

Once I fixed the UID, removed -auth flag from Xwayland and set DISPLAY=:1 in the container, Xfce started working as you can see in the screenshot below ๐ŸŽ‰๏ธ๐ŸŽ‰๏ธ

Next I'll try set the environment variable for the user automatically and then readd -auth. It seems we are quite close now!

Once again, thanks for all your support - without it, this would took me waaaaaay longer to get running ๐Ÿ˜๏ธ

Screenshot from 2020-05-28 12-26-00

marbetschar commented 4 years ago

We now have something close to workable. Only thing missing: Authentication (surprise, surprise ...).

Does anyone has an idea whats wrong?

I tested the following three cases by executing the following command: lxc exec Xfce -- su --login marbetschar -c startxfce4

Nr. 1: No -auth flag for Xwayland Xfce starts successfully in Xwayland window.

Nr. 2: -auth flag points to host ~/.Xauthority And the file is mounted into the container for the same user also to ~/.Xauthority. This throws the already well known Connection refused error (for details in code see this commit).

Nr. 3: -auth flag points to newly created .Xauthority on host And the file is mounted into the container for the same user to ~/.Xauthority. This throws the same error as Nr. 2 above (for details in code see this commit).

marbetschar commented 4 years ago

Just figured out that running the following on host works:

$ DISPLAY=:1 XAUTHORITY=.cache/com.github.marbetschar.tins/Xfce/.Xauthority xclock

So I guess the X client cookie is host specific... Is there a way to create it on host and share it with the container, or do I need to create it in container and somehow share it with host because Xwayland runs on host?

toby63 commented 4 years ago

:thinking: I am still not so convinced about your approach, but in simos tutorial it used to work: https://blog.simos.info/how-to-run-wine-graphics-accelerated-in-an-lxd-container-on-ubuntu/

Update: Somehow my search function did not find your profile in https://github.com/marbetschar/tins/blob/master/data/lxd/profiles/tins-x11.json So you already set that :sweat_smile: .

Difference I see (I did only a quick search in your master branch, but I guess you haven't implemented that):

$ lxc config set wine-games raw.idmap "both $UID 1000"
$ lxc restart wine-games
$

This โ€œboth $UID 1000โ€ syntax is a shortcut that means to map the $UID/$GID of our username in the host, to the default non-root username in the container (which should be 1000 for Ubuntu images, at least).

Setting the uid exactly the same, is maybe necessary to use the same .Xauthority file...

marbetschar commented 4 years ago

@toby63 thereโ€˜s room for improvement for sure. Just trying to get something working and take it from there.

UID/GID is not the problem, as I already set these. Will try to create the .Xauthority in container and pass it to Xwayland on host - I guess this will do the trick.

mviereck commented 4 years ago

-auth flag points to newly created .Xauthority on host

I recommend to not use ~/.Xauthority from host but a custom file instead. Avoid mixing host cookies with container cookies.

So I guess the X client cookie is host specific... Is there a way to create it on host and share it with the container

There is some network specific information in the first 4 bytes of the cookie. You can replace them with ffff to allow the cookie for other networks. From an xinitrc generated by x11docker:

# Prepare cookie with localhost identification disabled by ffff, needed if X socket is shared. ffff means 'familiy wild'
Cookie="$(xauth -i -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client nlist | sed -e 's/^..../ffff/')"
echo "$Cookie" | xauth -v -i -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client nmerge -

Full cookie creation process example done by x11docker:

# create new XAUTHORITY cookies
:> /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client

echo 'Requesting trusted cookie from X server'
xauth -v -i -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client generate :103 . trusted timeout 3600

[ -s '/home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client' ] || { 
  [ 'trusted' = 'untrusted' ] && note 'Could not create untrusted cookie. 
  Maybe your X server misses extension SECURITY.'
}
[ -s '/home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client' ] || { 
  # still no cookie? try to create one without extension security
  debugnote 'xinitrc: Failed to retrieve trusted cookie from X server. Will bake one myself.'
  echo 'Failed to retrieve trusted cookie from X server. Will bake one myself.'
  xauth -v -i -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client add :103 . e051bb8485c5132b0838dfac8e65a097
  ls -l /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client
}

# Prepare cookie with localhost identification disabled by ffff, needed if X socket is shared. ffff means 'familiy wild'
Cookie="$(xauth -i -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client nlist | sed -e 's/^..../ffff/')"
echo "$Cookie" | xauth -v -i -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client nmerge -

debugnote "xinitrc: Created cookie: $(xauth -f /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client list 2>&1)"
ls -l /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client
cp /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client /home/lauscher/.cache/x11docker/xonly-98896611612/Xauthority.server
chmod 644 /home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client

[ -s '/home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client' ] || warning 'Cookie creation failed!'
export XAUTHORITY=/home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client
[ 'yes' = 'no' ] || [ ! -s '/home/lauscher/.cache/x11docker/xonly-98896611612/share/Xauthority.client' ] && unset XAUTHORITY && warning '--weston-xwayland: X server :103 runs without cookie authentication.'

Some tricks here: -auth COOKIEFILE in Xwayland points no a non-existing file. The cookie file and its content is generated after startup of Xwayland. xauth requests the cookie from Xwayland itself. Once the cookie file has content, Xwayland can only be accessed with the cookie.

TL;DR: You can go an easier way and just create a valid cookie file before Xwayland starts.

DISPLAY=:1
XAUTHORITY="/path/to/my/cookie"
xauth -v -i -f $XAUTHORITY add $DISPLAY . $(mcookie)
Cookie="$(xauth -i -f $XAUTHORITY nlist | sed -e 's/^..../ffff/')"
echo "$Cookie" | xauth -v -i -f $XAUTHORITY nmerge -

Once again, thanks for all your support - without it, this would took me waaaaaay longer to get running

You're welcome. :-) I believe it is good to have an alternative to x11docker based on LXC. Just not to be bound to Docker for those setups.

marbetschar commented 4 years ago

This did the trick, thank you!! Since everything is in place now, next step is to autostart the desktop environment ๐Ÿ‘๏ธ

marbetschar commented 4 years ago

Basic Image Server is up & running: https://images.desktop-linuxcontainers.org/

Will split up the remaining tasks, into multiple issues so it gets easier to work with.