diff --git a/app_containers/.local/bin/run_app_container b/app_containers/.local/bin/run_app_container index 9bd1a27..0d08a15 100755 --- a/app_containers/.local/bin/run_app_container +++ b/app_containers/.local/bin/run_app_container @@ -5,9 +5,24 @@ die() { exit 1 } +cleanup() { + rm -rf "$container_xdg_runtime" + # Remove the temporary facl-based permissions + setfacl -x u:$((user_on_host)) $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY + xhost -si:localuser:\#$((user_on_host)) + + for input in $(find /dev/input -type c); do + sudo setfacl -x u:$user_on_host $input + done +} + [ -z "$CONTAINER_NAME" ] && die "\$CONTAINER_NAME not set" [ -z "$DISPLAY" ] && die "\$DISPLAY not set (you must run the script in a desktop environment" +# Use a GUI-available askpass program for sudo +# This should be made configurable +export SUDO_ASKPASS=$HOME/.local/bin/askpass-bemenu + # Source configuration files if any config="$HOME/.config/app_containers/$CONTAINER_NAME.sh" if [ -f "$config" ]; then @@ -15,8 +30,8 @@ if [ -f "$config" ]; then fi # Create a XDG_RUNTIME_DIR for guest on host -container_xdg_runtime="$(mktemp -d)" -trap 'rm -rf -- "$container_xdg_runtime"' EXIT +container_xdg_runtime="$(mktemp -d -p /var/tmp)" +trap cleanup EXIT # Link the current wayland session to the container's xdg runtime # Note that the session itself must be bind-mounted first @@ -37,17 +52,52 @@ fi [ -S $host_pulse ] || die "PulseAudio UNIX socket not found" -# Default username (assume `user` always has the same uid as the host user) -run_as=user -homedir=/home/user +# Default user +run_as=$UID if [ "$CONTAINER_RUN_AS_ROOT" = true ]; then - run_as=root - homedir=/root + run_as=0 fi -SUDO_ASKPASS=$HOME/.local/bin/askpass-bemenu sudo -A systemd-nspawn -M $CONTAINER_NAME \ - `# This doesn't provide userns isolation, but it does provide capability isolation` \ - --private-users=identity \ +homedir=/ +for line in $(sudo -A cat /var/lib/machines/$CONTAINER_NAME/etc/passwd); do + if [ "$(echo "$line" | cut -d: -f3)" == "$run_as" ]; then + homedir="$(echo "$line" | cut -d: -f6)" + break + fi +done + +# Userns-related config +# Default to identity mapping, which does not provide uid isolation but does for capabilities +private_users=identity +bind_opts="" +user_on_host=$run_as +if [ "$CONTAINER_USE_USERNS" = true ]; then + private_users=$(shuf -i 65536-$((2147483647 - 65536)) -n1) # Pick a random starting offset + bind_opts="idmap" + user_on_host=$((private_users + run_as)) +fi + +# Grant the user inside the container access to the Wayland / Xorg display +# For the Wayland socket, a simple facl rule would suffice +# For Xorg, we need to use the `xhost` facilities +setfacl -m u:$user_on_host:rwx $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY +xhost +si:localuser:\#$user_on_host + +# Grant the user inside the container access to input devices +# Note: any new device plugged in when the container is running would not +# be added properly here. +for input in $(find /dev/input -type c); do + sudo -A setfacl -m u:$user_on_host:rw- $input +done + +# Bind-mounts defined by the user (possibly in the container-specific config file) +# Format should be "src:target". target cannot be omitted +for mount in ${CONTAINER_BIND_MOUNTS[@]}; do + SYSTEMD_NSPAWN_EXTRA_ARGS+=" --bind=$mount:$bind_opts" +done + +sudo -A systemd-nspawn -M $CONTAINER_NAME \ + --private-users=$private_users --private-users-ownership=map \ `# DNS (when containers do not have their own netns)` \ --bind-ro=/run/systemd/resolve/stub-resolv.conf:/etc/resolv.conf \ `# GPU` \ @@ -57,7 +107,7 @@ SUDO_ASKPASS=$HOME/.local/bin/askpass-bemenu sudo -A systemd-nspawn -M $CONTAINE --bind-ro=/dev/input \ --property=DeviceAllow='char-input r' \ `# Xdg runtime` \ - --bind=$container_xdg_runtime:/run/xdg \ + --bind=$container_xdg_runtime:/run/xdg:$bind_opts \ --setenv=XDG_RUNTIME_DIR=/run/xdg \ `# Xorg / Xwayland` \ --bind=/tmp/.X11-unix \