Skip to content

feat: harden Docker image — distroless, rootless, multi-arch#212

Open
nayrosk wants to merge 4 commits intomultiversx:mainfrom
nayrosk:main
Open

feat: harden Docker image — distroless, rootless, multi-arch#212
nayrosk wants to merge 4 commits intomultiversx:mainfrom
nayrosk:main

Conversation

@nayrosk
Copy link

@nayrosk nayrosk commented Feb 16, 2026

Summary

Overhaul the Docker build pipeline to produce a hardened, minimal container image.

Changes

Dockerfile, rewritten as a 3-stage multi-arch build:

  • Runtime base: ubuntu:22.04 → gcr.io/distroless/cc-debian13:nonroot (glibc + libstdc++ only, no shell, no package manager)
  • Rootless: runs as nonroot (UID 65532), no root process in the container
  • Build-time config fetch: a dedicated config-fetcher stage runs on $BUILDPLATFORM to pre-fetch node/proxy configs, removes the git/curl runtime dependency
  • Wasmer lib extraction: auto-detects target arch via dpkg --print-architecture instead of copying both arches
  • Smaller binary: -ldflags="-s -w" -trimpath strips debug symbols

Makefile, new targets and configurable variables:

  • docker-build: single-arch build for current platform
  • docker-build-push: multi-arch (amd64 + arm64) build + push to registry
  • docker-run: starts the container in --read-only mode with --tmpfs /tmp
  • docker-stop: stop and remove container
  • qemu-setup: register QEMU user-mode emulation for cross-platform builds
  • docker-info: Image metadata
  • docker-scan: Trivy security scan
  • help: list all available targets

README

  • Updated Docker documentation to reflect new workflow.

Why

Reduced attack surface: distroless has no shell, no package manager, no unnecessary binaries. Smaller CVE footprint.
Non-root by default: follows container security best practices (CIS Docker Benchmark, Pod Security Standards restricted).
Read-only filesystem: runtime container is immutable, limits impact of potential exploits.
Smaller image: distroless/cc (~30MB) vs ubuntu:22.04 + git + curl (~150MB+).

Breaking changes

None. The entrypoint, exposed port (8085), and CLI flags remain identical. Existing docker-compose setups and CI pipelines work without modification.

Notes

A fully static build (CGO_ENABLED=0, scratch base) is not possible because the binary depends on Wasmer VM shared libraries (.so) loaded via CGo at runtime. distroless/cc is the smallest viable base that provides the required glibc + libstdc++.

- Switch runtime base from ubuntu:22.04 to gcr.io/distroless/cc-debian13:nonroot
- Run container as non-root user (UID 65532)
- Use multi-stage build: config-fetcher (pre-fetches configs at build time),
  builder (compiles binary + extracts Wasmer libs), distroless runtime
- Use --platform=$BUILDPLATFORM on config-fetcher stage to fix cross-arch builds
- Auto-detect target arch for Wasmer .so extraction via dpkg --print-architecture
- Add OCI labels (title, description, source, license)
- Remove runtime git/curl dependency (configs embedded at build time)
- Strip debug symbols (-ldflags="-s -w" -trimpath) for smaller binary
- Add docker-build-push target for multi-arch (amd64/arm64) with --push
- Add qemu-setup target to register QEMU for cross-platform builds
- Add docker-run target with --read-only and --tmpfs /tmp
- Add docker-stop target for cleanup
- Add docker-info and docker-scan (trivy) targets
- Add configurable variables (IMAGE_NAME, REGISTRY, PLATFORMS)
- Add help target listing all available commands
- Add build target with -ldflags="-s -w" -trimpath
- Add fetch-configs target
- Update prerequisites to include Docker with BuildKit and Buildx
- Add Makefile build alternative in Install section
- Replace Docker section with new build/push/run instructions
- Document distroless base image, non-root user, and read-only mode
- Document QEMU setup for cross-platform builds
- Reference make help for full list of targets
feat: harden Docker image, distroless, rootless, multi-arch
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