From 260c107db281dbff94f374d93ad32871d9b94f50 Mon Sep 17 00:00:00 2001 From: Diego Braga Date: Thu, 13 Aug 2026 23:14:31 +0200 Subject: [PATCH] security: fix Trivy pod-security findings (harden securityContext, Dockerfile) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves the Trivy `misconfig` HIGH findings reported (report-only) by the shared security workflow, and hardens the shipped Helm chart which ships the same posture that the scanner cannot see through templating. HIGH (fixed): - DS-0029 Dockerfile builder: `apt-get install --no-install-recommends`. - KSV-0014 Root filesystem now read-only. - KSV-0118 Explicit pod + container securityContext (was the helm-create `{}` default). Chart (helm/authn/values.yaml) — the real prod artifact, previously shipped podSecurityContext:{} / securityContext:{}. Now hardened by default: pod: runAsNonRoot, runAs{User,Group}/fsGroup 65532, seccompProfile RuntimeDefault container: allowPrivilegeEscalation false, readOnlyRootFilesystem true, capabilities drop ALL, runAsNonRoot, seccompProfile RuntimeDefault Safe because the image is gcr.io/distroless/static:nonroot (UID/GID 65532), authn persists kubeconfigs as Secrets via the API (never local disk), and it listens on an unprivileged port. Fields are open `object` in values.schema.json — no schema change. The dev manifest (manifests/deploy.local.yaml) mirrors the same context. MEDIUM (accepted, documented in .trivyignore.yaml, below the CI HIGH/CRITICAL gate): - KSV-0013 dev manifest authn:latest (local build, imagePullPolicy: Never). - KSV-0113 authn's SA must manage namespace secrets — its core function. - KSV-0111 testdata admin-access mapping — a test fixture, never deployed. Verified: `trivy fs --scanners misconfig` shows 0 HIGH/CRITICAL after the fixes; helm lint clean; chart renders the hardened contexts. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01LJsLqtryCgWwEt8FnPE1se --- .trivyignore.yaml | 26 +++++++++++++++++++++++ go/authn/Dockerfile | 2 +- go/authn/manifests/deploy.local.yaml | 18 ++++++++++++++++ helm/authn/values.yaml | 31 +++++++++++++++++++--------- 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 .trivyignore.yaml diff --git a/.trivyignore.yaml b/.trivyignore.yaml new file mode 100644 index 0000000..93b6b2c --- /dev/null +++ b/.trivyignore.yaml @@ -0,0 +1,26 @@ +# Trivy misconfiguration exceptions — intentional, security-reviewed accepts. +# +# HIGH/CRITICAL findings are FIXED in-tree, never ignored: +# - DS-0029 : Dockerfile builder now uses `apt-get install --no-install-recommends`. +# - KSV-0014 : readOnlyRootFilesystem: true on the chart default + dev manifest. +# - KSV-0118 : explicit pod + container securityContext on the chart default + dev manifest. +# +# Only these MEDIUM, by-design findings are accepted, each scoped to its file with a rationale. +misconfigurations: + - id: KSV-0013 + paths: + - "go/authn/manifests/deploy.local.yaml" + statement: >- + Local-only dev manifest: authn:latest is built locally and run with imagePullPolicy: Never. + Not a shipped artifact — the Helm chart resolves a real, pinned image tag. + - id: KSV-0113 + paths: + - "go/authn/manifests/deploy.local.yaml" + statement: >- + authn's core function is issuing and persisting kubeconfigs as Kubernetes Secrets, so its + ServiceAccount must manage namespace secrets. This is a required capability, not a misconfig. + - id: KSV-0111 + paths: + - "go/authn/testdata/ldap-forumsys.yaml" + statement: >- + Test fixture exercising an admin-access user mapping. testdata only, never deployed. diff --git a/go/authn/Dockerfile b/go/authn/Dockerfile index 5efa768..804a58c 100644 --- a/go/authn/Dockerfile +++ b/go/authn/Dockerfile @@ -7,7 +7,7 @@ ARG DEBIAN_FRONTEND=noninteractive SHELL ["/bin/bash", "-o", "pipefail", "-c"] # hadolint ignore=DL3008 -RUN apt-get update && apt-get install -y ca-certificates openssl git tzdata && \ +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates openssl git tzdata && \ update-ca-certificates && \ rm -rf /var/lib/apt/lists/* diff --git a/go/authn/manifests/deploy.local.yaml b/go/authn/manifests/deploy.local.yaml index 18e7430..6161334 100644 --- a/go/authn/manifests/deploy.local.yaml +++ b/go/authn/manifests/deploy.local.yaml @@ -73,6 +73,15 @@ spec: app: authn spec: serviceAccountName: authn + # Mirror the chart's hardened defaults so local dev matches prod posture. The image is + # distroless/static:nonroot (UID/GID 65532); authn writes kubeconfigs as Secrets via the API. + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault volumes: # The RSA private key authn signs RS256 JWTs with. Create it first: # openssl genrsa -out private.pem 2048 @@ -89,6 +98,15 @@ spec: #image: kind.local/authn:latest image: authn:latest imagePullPolicy: Never + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault args: - --debug=true - --kubeconfig-server-url=https://127.0.0.1:57456 diff --git a/helm/authn/values.yaml b/helm/authn/values.yaml index f051e1f..45f0959 100644 --- a/helm/authn/values.yaml +++ b/helm/authn/values.yaml @@ -35,16 +35,27 @@ serviceAccount: podAnnotations: {} podLabels: {} -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 +# Hardened by default. The shipped image is gcr.io/distroless/static:nonroot (USER nonroot, +# UID/GID 65532); authn persists kubeconfigs as Kubernetes Secrets via the API (never the local +# filesystem) and listens on an unprivileged port, so runAsNonRoot + readOnlyRootFilesystem are +# safe. Override per-field if you run a custom image with a different user. +podSecurityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault service: annotations: {}