Skip to content

Commit 2dabb93

Browse files
authored
Run the container as root so a bind-mounted /out works (issue #7) (#53)
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.
1 parent 8bc9c8b commit 2dabb93

2 files changed

Lines changed: 20 additions & 15 deletions

File tree

Dockerfile

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,28 @@ ARG TARGETPLATFORM
1717
# chromium for rendering; ca-certificates for HTTPS; tzdata for sane timestamps;
1818
# the font package so rendered pages have glyphs to lay out.
1919
RUN apk add --no-cache chromium ca-certificates tzdata font-noto \
20-
&& adduser -D -H -u 10001 kage \
21-
&& mkdir -p /out \
22-
&& chown kage:kage /out
20+
&& mkdir -p /out
2321

2422
COPY $TARGETPLATFORM/kage /usr/bin/kage
2523

26-
USER kage
2724
WORKDIR /out
2825

2926
# Point kage at the bundled Chromium and write mirrors under /out by default:
3027
#
3128
# docker run -v "$PWD/out:/out" ghcr.io/tamnd/kage clone example.com
3229
#
33-
# The kage user has no home directory of its own, so HOME points at the mounted
34-
# /out volume. That keeps two things writable: kage's default output and resume
35-
# state (it lands under $HOME/data/kage), and Chrome's profile and crash
36-
# database. Without this both fail with a permission error in the container
37-
# (issue #7), and the mounted volume captures nothing.
30+
# The container runs as root, and that is deliberate (issue #7). A bind-mounted
31+
# /out is owned by whoever created it on the host, so only root can reliably
32+
# write into it; a fixed non-root uid cannot, and both kage's output and resume
33+
# state (under $HOME/data/kage) then fail with "mkdir /out: permission denied".
34+
# The same unwritable HOME also breaks Chrome: it launches chrome_crashpad_handler
35+
# with an empty crash database path, which aborts the whole browser with
36+
# "chrome_crashpad_handler: --database is required" and fails every render.
37+
# Running as root keeps /out and HOME writable whatever the host owns, so the
38+
# one-liner above just works. This costs nothing in the sandbox: Chrome's sandbox
39+
# is already off inside any container (kage drops it on container detection), so
40+
# root here does not loosen a boundary that was holding. HOME points at /out so
41+
# the default output and Chrome's writable state both land in the mounted volume.
3842
ENV KAGE_CHROME=/usr/bin/chromium-browser \
3943
HOME=/out
4044

browser/pool.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,14 @@ func (p *Pool) getBrowser() (*rod.Browser, error) {
183183
// In a container, the default /dev/shm is only 64 MB, too small for
184184
// Chrome's renderer on large pages, so steer it to a temp file instead.
185185
// Outside a container /dev/shm is roomy and faster, so leave it alone.
186-
// Chrome's crashpad handler also aborts with "--database is required" in a
187-
// minimal container, which fails the whole launch (issue #7), so turn the
188-
// crash reporter off there. kage never uploads Chrome crash dumps anyway.
186+
//
187+
// The "chrome_crashpad_handler: --database is required" abort seen in
188+
// containers (issue #7) is not fixed here: the crash-reporter flags do not
189+
// stop Chrome from spawning the handler. Its real cause is an unwritable
190+
// HOME, which leaves the crash database path empty; the image keeps HOME
191+
// writable instead (see the Dockerfile).
189192
if inContainer() {
190-
l = l.Set("disable-dev-shm-usage", "").
191-
Set("disable-crash-reporter", "").
192-
Set("disable-breakpad", "")
193+
l = l.Set("disable-dev-shm-usage", "")
193194
}
194195

195196
if bin := p.chromeBin(); bin != "" {

0 commit comments

Comments
 (0)