fix: switch final stage to debian-slim so file caps survive Kaniko #60

Merged
gofix merged 1 commit from fix/debian-slim-final-stage into master 2026-05-03 17:40:52 +00:00
Owner

Closes #54 (properly this time).

Background

The v2.1.8+rs.1 retag actually built and pushed successfully after #59, so the registry has code.rly.best/gofix/portal-tunnel-rs:v2.1.8-rs.1. Pulling and inspecting the binary inside that image:

$ getcap /tmp/cap-prod
(empty)
$ getfattr -n security.capability /tmp/cap-prod
(no such attribute)

No file caps. #55's capstamp pattern preserves caps under BuildKit (which is what I verified locally) but Kaniko strips the security.capability xattr from every cross-stage COPY, with or without --chown. The fix in #55 was real for BuildKit-based pipelines but didn't actually fix the published Kaniko-built image.

Fix

Give up on the distroless final stage and use debian:bookworm-slim. Run setcap directly in the final stage so there is no cross-stage COPY of the cap-bearing binary, and Kaniko has no opportunity to drop the xattr.

Diff:

  • Drop gcr.io/distroless/cc-debian12:nonroot, drop the capstamp intermediate stage.
  • Final stage: debian:bookworm-slim + apt-get install -y ca-certificates libcap2-bin.
  • Recreate the uid 65532 nonroot user via useradd.
  • COPY --from=build brings in the binary and /portal-certs directory.
  • RUN setcap ... && chown -R 65532:65532 /portal-certs applies caps in-stage.

Image size goes from ~50 MiB (distroless) to ~80 MiB. Acceptable for the relay's deployment.

Validation

Local Rust CI matrix:

  • cargo fmt --check clean
  • cargo test --locked 86 passed, 3 ignored
  • cargo clippy --locked --all-targets -- -D warnings clean

After merge, retag v2.1.8+rs.1 (currently in draft) on the new HEAD, watch the workflow, pull the registry image, verify getcap returns the expected caps, redeploy rly.best from the registry image. Promote the Forgejo Release back from draft to published only after that end-to-end passes.

Closes #54 (properly this time). ## Background The v2.1.8+rs.1 retag actually built and pushed successfully after #59, so the registry has `code.rly.best/gofix/portal-tunnel-rs:v2.1.8-rs.1`. Pulling and inspecting the binary inside that image: ``` $ getcap /tmp/cap-prod (empty) $ getfattr -n security.capability /tmp/cap-prod (no such attribute) ``` No file caps. #55's capstamp pattern preserves caps under BuildKit (which is what I verified locally) but Kaniko strips the `security.capability` xattr from every cross-stage COPY, with or without `--chown`. The fix in #55 was real for BuildKit-based pipelines but didn't actually fix the published Kaniko-built image. ## Fix Give up on the distroless final stage and use `debian:bookworm-slim`. Run `setcap` directly in the final stage so there is no cross-stage COPY of the cap-bearing binary, and Kaniko has no opportunity to drop the xattr. Diff: - Drop `gcr.io/distroless/cc-debian12:nonroot`, drop the `capstamp` intermediate stage. - Final stage: `debian:bookworm-slim` + `apt-get install -y ca-certificates libcap2-bin`. - Recreate the uid 65532 nonroot user via `useradd`. - `COPY --from=build` brings in the binary and `/portal-certs` directory. - `RUN setcap ... && chown -R 65532:65532 /portal-certs` applies caps in-stage. Image size goes from ~50 MiB (distroless) to ~80 MiB. Acceptable for the relay's deployment. ## Validation Local Rust CI matrix: - `cargo fmt --check` clean - `cargo test --locked` 86 passed, 3 ignored - `cargo clippy --locked --all-targets -- -D warnings` clean After merge, retag `v2.1.8+rs.1` (currently in draft) on the new HEAD, watch the workflow, pull the registry image, verify `getcap` returns the expected caps, redeploy rly.best from the registry image. Promote the Forgejo Release back from draft to published only after that end-to-end passes.
fix: switch final stage to debian-slim so file caps survive Kaniko
All checks were successful
Rust CI / Format, lint, and test (pull_request) Successful in 1m7s
749f8b035e
Kaniko strips the `security.capability` xattr from any cross-stage
COPY, regardless of `--chown`. The capstamp pattern from #55 only
preserved caps in BuildKit; the published Kaniko-built image of
v2.1.8+rs.1 still ships without `cap_net_admin` / `cap_net_bind_service`,
which makes the relay crash-loop with `Operation not permitted` when
configuring the WireGuard overlay as the non-root user.

Replace the distroless final stage with `debian:bookworm-slim` and run
`setcap` directly inside the final stage. No cross-stage COPY of the
binary means no xattr loss. The image grows ~30 MiB (~50 MiB → ~80 MiB)
but is functionally identical.

The new final stage needs `ca-certificates` for HTTPS (distroless
included these; debian-slim does not). The non-root user (uid 65532) is
recreated via `useradd` to match the previous distroless layout the
existing rly.best deployment expects.

Closes #54.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gofix merged commit bdc58a8305 into master 2026-05-03 17:40:52 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gofix/portal-tunnel-rs!60
No description provided.