OMGee
Graphical apps seem to work correctly now when you pair with a Windows X11 server such as the one within MobaXterm. See firefox running in the screenshot.
YooToobe
(https://youtu.be/cWlYe0CE2iU)
Howto
Prerequisites
This requires WSL2 (the new generation of Windows System for Linux) which is only available to insiders on the Fast Track. You need to have installed WSL2 by the methods documented at https://docs.microsoft.com/en-us/windows/wsl/wsl2-install.
Ubuntu prerequisites
Once you have your Ubuntu 18.04 installed from the store and you’ve opened it the first time, you need to install some extra packages that aren’t part of the out-of-the-box filesystem:
- daemonize
- dbus-user-session
- fontconfig
These can be installed by running the following in the bash shell:
sudo apt-get update && sudo apt-get install -yqq daemonize dbus-user-session fontconfig
/usr/sbin/start-systemd-namespace
To always enable systemd and by extension snapd when you start bash you need to create a file at /usr/sbin/start-systemd-namespace
with the following content:
#!/bin/bash
SYSTEMD_PID=$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')
if [ -z "$SYSTEMD_PID" ] || [ "$SYSTEMD_PID" != "1" ]; then
export PRE_NAMESPACE_PATH="$PATH"
(set -o posix; set) | \
grep -v "^BASH" | \
grep -v "^DIRSTACK=" | \
grep -v "^EUID=" | \
grep -v "^GROUPS=" | \
grep -v "^HOME=" | \
grep -v "^HOSTNAME=" | \
grep -v "^HOSTTYPE=" | \
grep -v "^IFS='.*"$'\n'"'" | \
grep -v "^LANG=" | \
grep -v "^LOGNAME=" | \
grep -v "^MACHTYPE=" | \
grep -v "^NAME=" | \
grep -v "^OPTERR=" | \
grep -v "^OPTIND=" | \
grep -v "^OSTYPE=" | \
grep -v "^PIPESTATUS=" | \
grep -v "^POSIXLY_CORRECT=" | \
grep -v "^PPID=" | \
grep -v "^PS1=" | \
grep -v "^PS4=" | \
grep -v "^SHELL=" | \
grep -v "^SHELLOPTS=" | \
grep -v "^SHLVL=" | \
grep -v "^SYSTEMD_PID=" | \
grep -v "^UID=" | \
grep -v "^USER=" | \
grep -v "^_=" | \
cat - > "$HOME/.systemd-env"
echo "PATH='$PATH'" >> "$HOME/.systemd-env"
exec sudo /usr/sbin/enter-systemd-namespace "$BASH_EXECUTION_STRING"
fi
if [ -n "$PRE_NAMESPACE_PATH" ]; then
export PATH="$PRE_NAMESPACE_PATH"
fi
/usr/sbin/enter-systemd-namespace
Next you need to create a new file at /usr/sbin/enter-systemd-namespace
, with executable permissions (chmod +x
), and containing the following code:
#!/bin/bash
if [ "$UID" != 0 ]; then
echo "You need to run $0 through sudo"
exit 1
fi
SYSTEMD_PID="$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')"
if [ -z "$SYSTEMD_PID" ]; then
/usr/sbin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
while [ -z "$SYSTEMD_PID" ]; do
SYSTEMD_PID="$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')"
done
fi
if [ -n "$SYSTEMD_PID" ] && [ "$SYSTEMD_PID" != "1" ]; then
if [ -n "$1" ] && [ "$1" != "bash --login" ] && [ "$1" != "/bin/bash --login" ]; then
exec /usr/bin/nsenter -t "$SYSTEMD_PID" -a \
/usr/bin/sudo -H -u "$SUDO_USER" \
/bin/bash -c 'set -a; source "$HOME/.systemd-env"; set +a; exec bash -c '"$(printf "%q" "$@")"
else
exec /usr/bin/nsenter -t "$SYSTEMD_PID" -a \
/bin/login -p -f "$SUDO_USER" \
$(/bin/cat "$HOME/.systemd-env" | grep -v "^PATH=")
fi
echo "Existential crisis"
fi
Sudoers
Using sudo visudo
, add the following sudoers
rule to the bottom of the sudoers
file (you must use the visudo
utility for this, not vi
or nano
directly, so that the changes are correctly adopted by your system):
Defaults env_keep += WSLPATH
Defaults env_keep += WSLENV
Defaults env_keep += WSL_INTEROP
Defaults env_keep += WSL_DISTRO_NAME
Defaults env_keep += PRE_NAMESPACE_PATH
%sudo ALL=(ALL) NOPASSWD: /usr/sbin/enter-systemd-namespace
/etc/bash.bashrc
To tie these files together we need to add a line to /etc/bash.bashrc
, which triggers the process of starting and entering the namespace for all interactive bash sessions. This was previously handled by /etc/profile.d/00-wsl2-systemd.sh
, which you should now delete although it won’t hurt to leave it. Execute the following command to add the required line:
sudo sed -i 2a"# Start or enter a PID namespace in WSL2\nsource /usr/sbin/start-systemd-namespace\n" /etc/bash.bashrc
We can then add two environment variables to Windows which will cause bash to read the bashrc file for all sessions, both interactive and non-interactive. Run the following two commands to achieve this:
cmd.exe /C setx WSLENV BASH_ENV/u
cmd.exe /C setx BASH_ENV /etc/bash.bashrc
Visual Studio Code should now correctly enter the namespace when using the WSL Remote extension
Optional: Strict confinement support (custom kernel)
Compile yourself
- Check out (git clone) the source at https://github.com/diddlesnaps/WSL2-Linux-Kernel/tree/snapd-support into an Ubuntu distro:
sudo apt-get install git-core git clone -b snapd-support https://github.com/diddlesnaps/WSL2-Linux-Kernel
- Install some compilers:
sudo apt-get install build-essential flex bison libssl-dev libelf-dev
- Change directory into the kernel source and compile:
cd WSL2-Linux-Kernel make KCONFIG_CONFIG=Microsoft/config-wsl
- Copy the compiled kernel to your Windows user profile directory (replace
diddledan
with your Windows username):cp arch/x86/boot/bzImage /mnt/c/Users/diddledan/vmlinux-wsl2-snapd
- In Windows create or edit a file at
%USERPROFILE%\.wslconfig
, e.g.C:\Users\diddledan\.wslconfig
with at least the following two lines. Change the username to match your Windows username. The double-backslashes\\
are required:[wsl2] kernel=C:\\Users\\diddledan\\vmlinux-wsl2-snapd
- Exit all WSL2 sessions and in
cmd.exe
or powershell runwsl --shutdown
to ensure that the new kernel is used when you restart your WSL2 session(s).
Use precompiled binary
- Download https://github.com/diddlesnaps/WSL2-Linux-Kernel/releases/download/4.19-snapd-20190923/vmlinux-wsl2-snapd
- Copy the binary to
%USERPROFILE%
, e.g.C:\Users\diddledan\vmlinux-wsl2-snapd
- Edit or create a file alongside called
.wslconfig
with at least the following two lines. Change the username to match your Windows username. The double-backslashes\\
are required:[wsl2] kernel=C:\\Users\\diddledan\\vmlinux-wsl2-snapd
- Exit all WSL2 sessions and in
cmd.exe
or powershell runwsl --shutdown
to ensure that the new kernel is used when you restart your WSL2 session(s).
Done
Now every time you start the terminal/bash it will automatically put you inside a pid namespace with snapd running.
Diagnostics of the environment by snapd
Below are some simple diagnostic listings that show the environment according to snapd.
Version details
$ snap version
snap 2.41
snapd 2.41
series 16
ubuntu 18.04
kernel 4.19.72-microsoft-standard+
Confinement
With stock Microsoft kernel
$ snap debug confinement
partial
With custom kernel
$ snap debug confinement
strict
Sandbox features
With stock Microsoft kernel
$ snap debug sandbox-features
confinement-options: classic devmode
dbus: mediated-bus-access
kmod: mediated-modprobe
mount: freezer-cgroup-v1 layouts mount-namespace per-snap-persistency per-snap-profiles per-snap-updates per-snap-user-profiles stale-base-invalidation
seccomp: bpf-actlog bpf-argument-filtering kernel:allow kernel:errno kernel:kill_process kernel:kill_thread kernel:log kernel:trace kernel:trap
udev: device-cgroup-v1 tagging
With custom kernel
$ snap debug sandbox-features
apparmor: kernel:caps kernel:dbus kernel:domain kernel:file kernel:mount kernel:namespaces kernel:network kernel:network_v8 kernel:policy kernel:ptrace kernel:query kernel:rlimit kernel:signal parser:unsafe policy:default support-level:full
confinement-options: classic devmode strict
dbus: mediated-bus-access
kmod: mediated-modprobe
mount: freezer-cgroup-v1 layouts mount-namespace per-snap-persistency per-snap-profiles per-snap-updates per-snap-user-profiles stale-base-invalidation
seccomp: bpf-actlog bpf-argument-filtering kernel:allow kernel:errno kernel:kill_process kernel:kill_thread kernel:log kernel:trace kernel:trap
udev: device-cgroup-v1 tagging
Edits (newest edit is last)
Edited to improve profile script.
Edited to update scripts - the old scripts are below for reference
00-wsl2-systemd.sh
SYSTEMD_PID=$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')
if [ -z "$SYSTEMD_PID" ]; then
sudo /usr/sbin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
SYSTEMD_PID=$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')
fi
if [ -n "$SYSTEMD_PID" ] && [ "$SYSTEMD_PID" != "1" ]; then
exec sudo /usr/bin/nsenter -t $SYSTEMD_PID -a su - $LOGNAME
fi
sudoers
%sudo ALL=(ALL) NOPASSWD: /usr/sbin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
%sudo ALL=(ALL) NOPASSWD: /usr/bin/nsenter -t [0-9]* -a su - [a-zA-Z0-9]*
Update dependencies and profile script
- Add APT dependencies for Ubuntu
- Replace
su
withlogin
to initialise auser.slice
anduser@.service
within the session- This starts a systemd user instance with dbus supported (dbus is provided by
dbus-user-session
in the Ubuntu Dependencies section)
- This starts a systemd user instance with dbus supported (dbus is provided by
Update enter-systemd-namespace
script
- Add
while
loop to wait forsystemd
to actually start
Update scripts for WSL Interoperability (can start Windows apps from the PID namespace)
- Pass
$WSL_INTEROP
toenter-systemd-namespace
in00-wsl2-systemd.sh
profile script - Use
$WSL_INTEROP
inenter-systemd-namespace
when entering the PID namespace
Update sudo config and scripts for env-passthru
- Use sudoers configuration for environment passthrough
- Remove brittle command-line environment variable pass-through
Update sudoers and enter-systemd-namespace, Add custom kernel
- Remove usage of
$LOGNAME
- Don’t propagate
$LOGNAME
into sudo session - Add custom kernel
- Update diagnostic output to include custom kernel variants
Update entry method
- Remove
/etc/profile.d/00-wsl2-systemd.sh
- Add
/usr/sbin/start-systemd-namespace
- Add line to
/etc/bash.bashrc
to callstart-systemd-namespace
file
Update for non-interactive sessions
- Edit
/usr/sbin/start-systemd-namespace
to pass initial command - Edit
/usr/sbin/enter-systemd-namespace
to use passed initial command - Add instructions to set Windows environment variables
Working VSCode WSL Remote extension
- Edit
/usr/sbin/start-systemd-namespace
to save initial environment (required to capture VSCode-specific variables) - Edit
/usr/sbin/enter-systemd-namespace
to restore initial environment (required to pass-through VSCode-specific variables)
Docker Tech Preview support
- Edit
/usr/sbin/enter-systemd-namespace
to better quote the input command when non-interactive sessions are required