92959cac38
Part of the same security-hardening pass as the last three commits. Neither the Dockerfile nor the chart's Deployment set any securityContext at all, so the container ran as root by default — scratch has no /etc/passwd for a USER directive to resolve against, so nobody had set one. Dockerfile now ends with USER 65532:65532 (numeric, since scratch has no user database; 65532 is the common "nonroot" convention, distroless's own uid). The chart's Deployment adds a matching pod-level securityContext (runAsNonRoot, runAsUser/runAsGroup: 65532, seccompProfile: RuntimeDefault) plus per-container hardening (allowPrivilegeEscalation: false, capabilities dropped, readOnlyRootFilesystem: true) on both the app container and the wait-for-postgres init container — neither writes anything to disk, so the root filesystem can stay read-only. Verified with helm-lint and a manual `helm template` render of both the terdut-server and terdut-demo charts. Not yet verified: an actual pod starting with these in place — readOnlyRootFilesystem is exactly where a non-obvious write (a temp file, a cache dir) would surface as a crash rather than a lint error, so that needs a real rollout to confirm. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
34 lines
1.7 KiB
Docker
34 lines
1.7 KiB
Docker
# --platform=$BUILDPLATFORM pins the builder to the machine doing the building, so a
|
|
# multi-arch build compiles both targets natively instead of running an emulated arm64
|
|
# toolchain under QEMU. Go cross-compiles from TARGETOS/TARGETARCH, which BuildKit fills
|
|
# in per platform. The CI runner has no binfmt registration and no way to get one (the
|
|
# JS action that used to install it cannot run there), so this is not just an
|
|
# optimisation -- it is what makes the arm64 image buildable at all.
|
|
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
ARG VERSION=dev
|
|
ARG TARGETOS
|
|
ARG TARGETARCH
|
|
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
|
go build -ldflags="-w -s -X main.version=${VERSION}" -o /terdut ./cmd/terdut
|
|
|
|
FROM scratch
|
|
# scratch has no trust store, and a Go binary on it fails every HTTPS call with
|
|
# "x509: certificate signed by unknown authority". Nothing needed one until single
|
|
# sign-on: discovery and the token exchange are HTTPS calls to the identity provider.
|
|
# The bundle is the builder's, copied by name so a missing file fails the build
|
|
# rather than shipping an image that cannot sign anybody in.
|
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
|
COPY --from=builder /terdut /terdut
|
|
EXPOSE 8080
|
|
# Numeric, not a name: scratch has no /etc/passwd for one to resolve against,
|
|
# and Docker's USER accepts a bare UID:GID without it. 65532 is the common
|
|
# "nonroot" convention (distroless's own uid), chosen so the chart's pod
|
|
# securityContext (runAsNonRoot, runAsUser: 65532) matches what the image
|
|
# already runs as rather than fighting it.
|
|
USER 65532:65532
|
|
ENTRYPOINT ["/terdut"]
|