Hardening
Chromium’s process sandbox is the main barrier between a hostile page and the host, and the ZShot Docker images keep it on. Under Docker it needs user namespaces, which the default seccomp profile blocks, so run the image with the profile that ships inside it:
docker run --rm --entrypoint cat scaleassembly/zshot /usr/share/zshot/zshot-seccomp.json > zshot-seccomp.json
docker run --rm --security-opt seccomp=zshot-seccomp.json \
scaleassembly/zshot https://zshot-cli.com/example_assets/hello_zshot/ -f out.pngThe profile is Docker’s default plus clone, unshare, and chroot, which the sandbox uses to put each renderer in its own user, PID, and network namespace. Without it the image renders without the sandbox and says so:
WARNING: Chromium's sandbox cannot start here: the runtime blocks unprivileged user namespaces. Container mode is running without it; see https://zshot-cli.com/reference/linux/#hardening--quiet silences the warning, and --diagnostics reports the verdict as a Chromium namespace sandbox line.
Recommended Runtime Settings
The profile supports the strictest runtime settings, and every one of them applies:
docker run --rm \
--security-opt seccomp=zshot-seccomp.json \
--cap-drop=ALL --security-opt no-new-privileges \
--read-only --tmpfs /tmp --pids-limit 512 \
scaleassembly/zshot https://zshot-cli.com/example_assets/hello_zshot/ -f out.pngThe same settings as a Compose service, published as docker-compose.yml beside the profile; both are exercised against every release:
services:
zshot:
image: scaleassembly/zshot-server
environment:
ZSHOT_LICENSE: MY-ZSHOT-LICENSE-KEY
ports: ["3000:3000"]
security_opt:
- seccomp=./zshot-seccomp.json
- no-new-privileges=true
cap_drop: [ALL]
read_only: true
tmpfs: [/tmp]
pids_limit: 512Runtime Notes
- Keep the default AppArmor profile. On Ubuntu 24.04 and later,
--security-opt apparmor=unconfinedblocks user namespaces and the sandbox with them. - Podman’s default seccomp profile already permits the sandbox. The profile above still matters with
--cap-drop=ALL, which removes the capability Podman’schrootrule keys on. - Kubernetes runs pods with no seccomp filter unless the namespace requires one.
Confirming the Sandbox
A sandboxed renderer shows two seccomp filters and a nested PID namespace in /proc/PID/status:
Seccomp_filters: 2
NSpid: 92 5 1