The runtime image was `gcr.io/distroless/cc-debian12` with no `USER`, so the
process ran as root. Obscura executes untrusted page JavaScript in-process
through V8, so a V8 exploit lands with the process's privileges, and there is
no reason for those to be root's -- the image binds an unprivileged port, reads
the CA bundle, and writes only to the storage dir and a temp dir.
Switch to the `:nonroot` tag.
Verified against a locally built image rather than assumed:
Config.User -> 65532
--version -> obscura 0.1.0
fetch https://example.com -> succeeds, so the CA bundle is readable
fetch --storage-dir /tmp/obs -> succeeds
The tag is deliberately not pinned to a digest. distroless is rebuilt often
with base-layer security patches and tracking the tag picks those up; a digest
pin freezes them until someone bumps it, which for a *base* image trades a real
ongoing risk for a theoretical one. Reasoning recorded in the Dockerfile so a
reviewer can disagree knowingly.
Docs: the three `docker run` examples already published to host loopback
(`-p 127.0.0.1:9222:9222`), so that part needed no change. What did need
documenting is the consequence of dropping root -- **a mounted `--storage-dir`
must be writable by uid 65532** -- because that is the one thing this change
breaks, and it breaks quietly. Verified: with an unwritable storage dir the run
completes, exits 0, and prints no warning; the cookie jar simply never
persists. `save_to_file` errors are swallowed. Noted in the docs with a
`install -d -o 65532` line, and worth a separate silent-failure fix.
The in-container `--host 0.0.0.0` is left alone and now explains itself: a
container-loopback bind is unreachable through `-p`, so it is required for the
published port to work at all. It is not a licence to expose the port.
2.5 KiB
Linux x86_64
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-linux.tar.gz
tar xzf obscura-x86_64-linux.tar.gz
./obscura --version
Linux ARM64
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-linux.tar.gz
tar xzf obscura-aarch64-linux.tar.gz
./obscura --version
Linux builds target Ubuntu 22.04 and require glibc 2.35+.
macOS Apple Silicon
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-macos.tar.gz
tar xzf obscura-aarch64-macos.tar.gz
./obscura --version
macOS Intel
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-macos.tar.gz
tar xzf obscura-x86_64-macos.tar.gz
./obscura --version
Windows
Download the .zip from Releases, extract, run obscura.exe --version.
Arch Linux (AUR)
yay -S obscura-browser
Docker
docker run -d --name obscura -p 127.0.0.1:9222:9222 h4ckf0r0day/obscura
Image: h4ckf0r0day/obscura. Built on distroless/cc:nonroot, with no shell or package manager in the runtime image, running as uid 65532. Note the -p 127.0.0.1:... above: it publishes the port to host loopback only. A mounted --storage-dir must be writable by uid 65532 — see Run in production at scale.
Official archives and the Docker image include the rendering engine. Source
builders must pass --features render; see Build from source.
From source
See Build from source.
What's in the archive
obscura: CLI and CDP server.obscura-worker: helper for the parallelscrapecommand. Keep both in the same directory.
Archive suffixes identify the feature set: no suffix includes rendering,
-stealth includes rendering and stealth, -no-render includes neither, and
-no-render-stealth includes stealth without rendering.
Smoke test
./obscura fetch https://example.com --eval "document.title"
./obscura fetch https://example.com --screenshot smoke.png
Expected output: "Example Domain", followed by a nonempty PNG at smoke.png.
Troubleshooting
cannot execute binary file: wrong arch. Check uname -m.
GLIBC_2.35 not found: distro is older than Ubuntu 22.04. Use Docker or build from source.
macOS Gatekeeper warning: xattr -d com.apple.quarantine ./obscura.