diff --git a/Dockerfile.template b/Dockerfile.template index bcdedbb..d2539b2 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -39,8 +39,6 @@ COPY ./src /usr/src/app/ RUN chmod +x ./*.sh -ENV UDEV=1 - RUN mkdir -p /etc/chromium/policies RUN mkdir -p /etc/chromium/policies/recommended COPY ./policy.json /etc/chromium/policies/recommended/my_policy.json diff --git a/balena.yml b/balena.yml index ed3a54c..ea0dafc 100644 --- a/balena.yml +++ b/balena.yml @@ -21,7 +21,7 @@ post-provisioning: >- browser: image: bh.cr/balenasolutions/browser- # one of raspberrypi3-64, raspberrypi4-64, raspberrypi5, generic-aarch64, generic-amd64 - privileged: true # required for UDEV to find plugged in peripherals such as a USB mouse + privileged: true # required for sound (/dev/snd) and V4L2 video-decode (/dev/video*) device access depends_on: - display environment: diff --git a/docker-compose.yml b/docker-compose.yml index f496026..833451c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: browser: build: . - privileged: true # required for UDEV to find plugged in peripherals such as a USB mouse + privileged: true # required for sound (/dev/snd) and V4L2 video-decode (/dev/video*) device access network_mode: host volumes: - 'settings:/data' # Only required if using PERSISTENT flag (see below) \ No newline at end of file diff --git a/docs/todo-non-privileged.md b/docs/todo-non-privileged.md new file mode 100644 index 0000000..049af29 --- /dev/null +++ b/docs/todo-non-privileged.md @@ -0,0 +1,31 @@ +# TODO: Run the browser block without privileged mode + +The examples run the browser with `privileged: true`. It would be nice to document a +non-privileged setup as an advanced option. + +## What needs privileged mode now + +Input doesn't. The display block's compositor handles input devices and Chromium gets +events over the Wayland socket, so the browser no longer runs udev. + +From `src/start.sh`: + +- `/dev/snd/*` for ALSA audio and `/dev/video*` for V4L2 decode on Pi 3/4. Only + `/dev/dri` is mapped in the compose file, so these are only visible when privileged. +- `sysctl -w user.max_user_namespaces=10000` and the CPU governor write. Both fail + silently (`|| true`) without privileges. + +Not checked: Chromium's sandbox is still on (`src/server.js`) and uses user namespaces, +which the default seccomp profile may block. + +## Things to try + +- Pass `/dev/snd` and the Pi decoder nodes with `devices:`. The `/dev/video*` numbers + differ between device types. +- See if `start.sh`'s GID mapping still works for nodes passed this way. +- If Chromium's sandbox fails, find the smallest `cap_add` / `security_opt` that fixes + it. Don't use `--no-sandbox`. +- Check which of these compose fields the balena Supervisor supports. + +USB sound cards plugged in after the container starts won't show up without privileged +mode. Probably fine for an opt-in setup, but worth noting in the readme. diff --git a/readme.md b/readme.md index 0b6cbb9..22bbde9 100644 --- a/readme.md +++ b/readme.md @@ -50,7 +50,7 @@ services: browser: image: bh.cr/balenasolutions/browser- # e.g. raspberrypi4-64, raspberrypi5, generic-amd64 - privileged: true # required for UDEV to find plugged in peripherals such as a USB mouse + privileged: true # required for sound (/dev/snd) and V4L2 video-decode (/dev/video*) device access depends_on: - display environment: diff --git a/src/entry.sh b/src/entry.sh deleted file mode 100644 index 4e0baa4..0000000 --- a/src/entry.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash - -# This command only works in privileged container -tmp_mount='/tmp/_balena' -mkdir -p "$tmp_mount" -if mount -t devtmpfs none "$tmp_mount" &> /dev/null; then - PRIVILEGED=true - umount "$tmp_mount" -else - PRIVILEGED=false -fi -rm -rf "$tmp_mount" - -function mount_dev() -{ - tmp_dir='/tmp/tmpmount' - mkdir -p "$tmp_dir" - mount -t devtmpfs none "$tmp_dir" - mkdir -p "$tmp_dir/shm" - mount --move /dev/shm "$tmp_dir/shm" - mkdir -p "$tmp_dir/mqueue" - mount --move /dev/mqueue "$tmp_dir/mqueue" - mkdir -p "$tmp_dir/pts" - mount --move /dev/pts "$tmp_dir/pts" - touch "$tmp_dir/console" - mount --move /dev/console "$tmp_dir/console" - umount /dev || true - mount --move "$tmp_dir" /dev - - # Since the devpts is mounted with -o newinstance by Docker, we need to make - # /dev/ptmx point to its ptmx. - # ref: https://www.kernel.org/doc/Documentation/filesystems/devpts.txt - ln -sf /dev/pts/ptmx /dev/ptmx - - # When using io.balena.features.sysfs the mount point will already exist - # we need to check the mountpoint first. - sysfs_dir='/sys/kernel/debug' - - if ! mountpoint -q "$sysfs_dir"; then - mount -t debugfs nodev "$sysfs_dir" - fi - -} - -function start_udev() -{ - if [ "$UDEV" == "on" ]; then - if $PRIVILEGED; then - mount_dev - if command -v udevd &>/dev/null; then - unshare --net udevd --daemon &> /dev/null - else - unshare --net /lib/systemd/systemd-udevd --daemon &> /dev/null - fi - udevadm trigger &> /dev/null - else - echo "Unable to start udev, container must be run in privileged mode to start udev!" - fi - fi -} - -function init() -{ - # echo error message, when executable file is passed but doesn't exist. - if [ -n "$1" ]; then - if CMD=$(command -v "$1" 2>/dev/null); then - shift - exec "$CMD" "$@" - else - echo "Command not found: $1" - exit 1 - fi - fi -} - -UDEV=$(echo "$UDEV" | awk '{print tolower($0)}') - -case "$UDEV" in - '1' | 'true') - UDEV='on' - ;; -esac - -start_udev -init "$@"