Skip to content

Run as a non-root user in containers - #581

Draft
emersonian wants to merge 2 commits into
zcash:masterfrom
zecrocks:be/docker-nonroot
Draft

Run as a non-root user in containers#581
emersonian wants to merge 2 commits into
zcash:masterfrom
zecrocks:be/docker-nonroot

Conversation

@emersonian

@emersonian emersonian commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

The lightwalletd Dockerfile adds a user account that it does not use, instead running lightwalletd as root.

Prerequisite: Alpine PR #578

This PR runs lightwalletd as a non-root user inside containers for additional security.

This will be a breaking change for container operators due to permissions variances on the data directory across container runtimes, and as such should cause a minor version bump of lightwalletd if merged in my opinion.

The image was built from golang:1.25 and shipped as-is, so it carried the
entire Go toolchain and a copy of the source tree into production, and
re-downloaded dependencies on every source change.

Build in a golang:1.25-alpine stage and copy only the resulting binary into
an alpine:3.21 runtime, which takes the image from ~1.9 GB to ~51 MB. Copy
go.mod/go.sum ahead of the source so module downloads are cached in their
own layer.

Runtime behaviour is deliberately unchanged: the container still runs as
root, and the lightwalletd user keeps uid/gid 2002 and ownership of
/var/lib/lightwalletd/db, exactly as before. Running as that user is a
worthwhile hardening but would break existing deployments whose data
directories were written as root, so it is left to a separate change.

The build still runs from a full checkout, so the git describe and
git rev-parse in the Makefile continue to stamp version metadata into the
binary.

One visible difference: an Alpine base has no bash, so shells into the
container need sh.
The image has always created a lightwalletd user (uid/gid 2002) and given it
ownership of /var/lib/lightwalletd/db, but never set USER, so the container
ran as root and the user went unused.

Set USER lightwalletd, so a compromise of the process does not start out
with root in the container.

This is a breaking change for existing deployments, and is kept separate
from the Alpine image rework for that reason. A data directory created by an
earlier root-running container is owned by root, so the unprivileged process
cannot write its block cache and fails with permission denied. Operators must
chown the directory to 2002:2002, or run with --user 0:0 to keep the previous
behaviour. Deployments on a fresh volume are unaffected, since a new volume
inherits the image's 2002:2002 ownership. CHANGELOG.md carries the migration
step.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant