Run the container as root so a bind-mounted /out works (issue #7)#53
Conversation
The image dropped to a fixed non-root user (uid 10001) and pointed HOME at /out. On native Linux Docker a bind-mounted /out is owned by whoever created it on the host, so uid 10001 cannot write into it. Two things then failed: kage's output and resume state under $HOME/data/kage hit "mkdir /out: permission denied", and Chrome launched chrome_crashpad_handler with an empty crash database path, which aborts the whole browser with "chrome_crashpad_handler: --database is required" and fails every render. The earlier attempt set HOME=/out, but that only helps when /out is writable, which it is not for a non-root uid against a host-owned mount. The crash-reporter flags in the launcher did not help either: they do not stop Chrome from spawning the handler, so the abort stayed. Run as root instead. Container root writes a host-owned bind mount whatever its ownership, so both /out and HOME stay writable and the documented one-liner just works. This does not loosen the sandbox: Chrome's sandbox is already off inside any container (kage drops it on container detection), so root here changes nothing that was holding. Verified end to end in an Alpine + chromium container: the non-root image reproduces both the crashpad abort and the permission-denied exactly as reported, and the root image clones example.com cleanly, writing index.html and resume state into a host-owned mounted volume.
Tested on a real-root Linux VMThe earlier note pointed out that rootless podman cannot reproduce native bind-mount permissions, since it remaps uids. The mount source was owned by uid 1000, mode 755, like a normal
Running Current image (uid 10001) reproduces the bug: Nothing is written to the mount. Fixed image (root) works:
SELinux noteThe VM was Fedora CoreOS with SELinux enforcing. The fixed image clones cleanly that way too. |
Tested on a real Ubuntu Docker host with the released imageRan this on a bare-metal Ubuntu box: kernel 6.8.0-107-generic, x86_64, Docker 29.2.1, AppArmor and no SELinux. Before, with the released image ( Nothing written to the mount, the same as the report. After, with an image built from this PR's Dockerfile and the same command and mount:
One thing to be aware of: because the container runs as root, the files in the mount come out owned by root on the host, and Chrome leaves a couple of dotdirs ( |
Problem
The Docker image still failed on Linux with the command from the README:
It printed two errors (issue #7):
Root cause
Both errors come from the same place.
The image ran as a fixed non-root user (uid 10001) with HOME set to /out.
When you bind-mount a host directory onto /out, that directory is owned by whoever created it on the host, so uid 10001 cannot write into it.
That breaks two things at once.
kage writes its output and resume state under $HOME/data/kage, so the write fails with
mkdir /out: permission denied.Chrome derives its crash-database path from $HOME, and with $HOME unwritable that path comes out empty, so it launches chrome_crashpad_handler without
--database, the handler aborts, and that brings the whole browser down on every render.The earlier attempt set HOME to /out, but that only helps when /out is writable, which it is not for a non-root uid against a host-owned mount.
The
--disable-crash-reporterand--disable-breakpadflags did not help either, because they do not stop Chrome from spawning the handler in the first place.Fix
Run the container as root.
Container root can write a host-owned bind mount whatever it is owned by, so /out and HOME both stay writable and the README command works.
This does not weaken anything: Chrome's sandbox is already off inside a container (kage drops it on container detection), so running as root there changes nothing that was still holding.
I also fixed the stale comment and dropped the two ineffective flags in browser/pool.go.
go build ./...,go vet ./browser/, andgo test ./browser/pass.The end-to-end testing on real Docker hosts is in the comments below.