Skip to content

Image hardening - #30

Open
KushagarGoel1 wants to merge 4 commits into
mainfrom
harden-image
Open

KushagarGoel1 wants to merge 4 commits into
mainfrom
harden-image

Conversation

@KushagarGoel1

@KushagarGoel1 KushagarGoel1 commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Hardens the server image, as part of the image hardening checklist (streamgres was listed on debian:bookworm-slim). It follows the same approach as jamsocket/y-sweet#435: Google's distroless runtime, which needs no registry login.

Changes

  • Base images: rust:1.89-trixie to build, gcr.io/distroless/cc-debian13:nonroot to run. Both are Debian 13, so the binary runs on the glibc it was linked against. The runtime has no shell, no package manager and no coreutils.
  • Nothing installed at runtime: curl and ca-certificates are removed. The binary links only libc, libm and libgcc_s, which cc provides, and TLS is rustls with its roots compiled in (webpki-roots).
  • No Docker HEALTHCHECK: there's no shell to run one, and Kubernetes ignores it anyway. Readiness and liveness come from HTTP probes on /healthz and /readyz, which return 503 until the feed is past its first snapshot. The CI end-to-end job still waits on /health with curl from the runner.
  • User: runs as distroless's nonroot (uid 65532) instead of a streamgres user created with useradd (uid 10001). The binary is root-owned and read-only to the server; only /var/lib/streamgres (the plan file) is writable. The leftover /var/lib/xyne-sync is dropped.
  • Build args: BASE_REGISTRY is replaced by whole-image BUILD_IMAGE and RUNTIME_IMAGE, because the two stages now come from different registries.
  • Publishing: the pushed image carries an SBOM and provenance.
  • Docs: docs/deploy.md is updated to match.

Results (local build; CVEs counted with a one-off Trivy 0.58.1 scan, not part of CI)

main this PR
Size 283 MB 179 MB
User streamgres (10001) 65532:65532
Shell yes none (exec: "sh": not found)
Known CVEs 1 critical, 67 high, 120 medium, 150 low 0 critical, 0 high, 23 medium, 8 low
Vulnerable packages 55 6

The image starts and fails only on missing config (STREAMGRES_PG_DSN … must name the database), as expected. No Rust code changes.

Before merging

  • Deployment: check anything tied to uid 10001 (securityContext.runAsUser, ownership of a plan-file volume; use fsGroup: 65532). If the root filesystem is read-only, mount an emptyDir at /tmp for heap profiles when Pyroscope is on. Confirm the deployment has HTTP readiness and liveness probes.

🤖 Generated with Claude Code

Build and run the server image on Docker Hardened Images: dhi.io/rust to
build, dhi.io/debian-base (debian-13) to run. The runtime has no shell and
no package manager, so:

- curl and ca-certificates are no longer installed: TLS roots are compiled
  into the binary (rustls with webpki-roots).
- The Docker HEALTHCHECK is gone; the platform probes /healthz and /readyz
  over HTTP, which answer 503 until the server is ready.
- The server runs as the image's nonroot user (uid 65532) instead of a
  streamgres user made with useradd (uid 10001). The binary is root-owned;
  only /var/lib/streamgres is writable. /var/lib/xyne-sync is dropped.
- BASE_REGISTRY is replaced by whole-image BUILD_IMAGE and RUNTIME_IMAGE
  arguments, since DHI image names differ from the official ones.

CI logs in to dhi.io (pulls need an account), and publishing now builds,
scans with Trivy (failing on fixable critical or high findings), then
pushes with an SBOM and provenance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread .github/workflows/ci-pr.yml Fixed
Comment thread .github/workflows/docker-publish.yml Fixed
Comment thread .github/workflows/docker-publish.yml Fixed
Comment thread .github/workflows/docker-publish.yml Fixed
Kushagar Goel and others added 3 commits October 29, 2026 20:42
dhi.io refuses anonymous pulls, so every build would need a Docker account.
Google's distroless gives the same runtime properties (no shell, no package
manager, nonroot uid 65532) from a public registry: run on
gcr.io/distroless/cc-debian13:nonroot, built on rust:1.89-trixie so both
stages share Debian 13's glibc. The dhi.io login steps are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants