From 652cef7ffe69a776ebd36229618ebf58a275c4f7 Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Mon, 18 May 2026 22:41:12 -0400 Subject: [PATCH] fix(docs/deploy): harden public docs deployment and pin images Add a pod-level security context to the nginx container in the public docs deployment (non-root uid 101, dropped capabilities, read-only root filesystem, RuntimeDefault seccomp) and rebind it to unprivileged port 8080 so it does not need CAP_NET_BIND_SERVICE. The nginx PID and proxy temp paths move under a tmpfs-backed emptyDir so the read-only root filesystem does not break startup. Replace the mutable :main tags on both containers with immutable sha256 digests and switch imagePullPolicy to IfNotPresent so each rollout references an auditable artifact instead of whatever happens to be tagged :main when the pod starts. The docs-deploy workflow now overlays the freshly built docs digest via `kustomize edit set image` so the manifest stays accurate without a manual edit on each push to main. The docs Dockerfile pins its node and nginx-micro base images to specific versions for the same reason. Ref: AWOO-011, AWOO-012 Assisted-by: Claude Opus 4.7 via Claude Code Signed-off-by: Xe Iaso --- .github/actions/spelling/expect.txt | 1 + .github/workflows/docs-deploy.yml | 9 +++++++++ docs/Dockerfile | 4 ++-- docs/docs/CHANGELOG.md | 2 ++ docs/manifest/cfg/nginx/nginx.conf | 10 ++++++++-- docs/manifest/deployment.yaml | 31 +++++++++++++++++++++-------- docs/manifest/service.yaml | 2 +- 7 files changed, 46 insertions(+), 13 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index df1f78b5..a0d7fff7 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -318,6 +318,7 @@ screenshots searchbot searx sebest +seccomp secretplans selfsigned Semrush diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index bcf9b6f9..d733c18c 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -52,6 +52,15 @@ jobs: platforms: linux/amd64 push: true + - name: Pin docs image to built digest + working-directory: docs/manifest + env: + DIGEST: ${{ steps.build.outputs.digest }} + run: | + KUSTOMIZE_VERSION=v5.4.3 + curl -fsSL "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64.tar.gz" | tar -xz + ./kustomize edit set image "ghcr.io/techarohq/anubis/docs=ghcr.io/techarohq/anubis/docs@${DIGEST}" + - name: Apply k8s manifests to limsa lominsa uses: actions-hub/kubectl@934aaa4354bbbc3d2176ae8d7cae92d515032dff # v1.35.3 env: diff --git a/docs/Dockerfile b/docs/Dockerfile index 298b3777..898204cd 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,11 +1,11 @@ -FROM docker.io/library/node:lts AS build +FROM docker.io/library/node:22.22.3-alpine AS build WORKDIR /app COPY . . RUN npm ci && npm run build -FROM ghcr.io/xe/nginx-micro +FROM ghcr.io/xe/nginx-micro:v1.29.0 COPY --from=build /app/build /www COPY ./manifest/cfg/nginx/nginx.conf /conf LABEL org.opencontainers.image.source="https://github.com/TecharoHQ/anubis" \ No newline at end of file diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md index 98b40b3f..0aea81d0 100644 --- a/docs/docs/CHANGELOG.md +++ b/docs/docs/CHANGELOG.md @@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix an obscure case where Anubis in subrequest mode could allow redirects to invalid domains with strange instructions. - Fix `path_regex` and CEL `path` rules not matching when using Traefik `forwardAuth` middleware. Anubis now checks `X-Forwarded-Uri` (Traefik) in addition to `X-Original-URI` (nginx) when resolving the request path in subrequest mode ([#1628](https://github.com/TecharoHQ/anubis/issues/1628)). - Validate bounds in the CEL `randInt` helper so non-positive or platform-overflowing arguments surface a typed CEL error instead of an evaluator panic. +- Harden the public docs deployment: add a pod-level security context to the nginx container (non-root uid 101, dropped capabilities, read-only root filesystem, `RuntimeDefault` seccomp) and rebind it to unprivileged port `8080`. +- Pin docs deployment images to immutable digests with `imagePullPolicy: IfNotPresent`, and have the docs-deploy workflow overlay the just-built digest via `kustomize edit set image` so each rollout references an auditable artifact instead of a floating `:main` tag. The docs `Dockerfile` now pins `node` and `nginx-micro` base images to specific versions. ## v1.25.0: Necron diff --git a/docs/manifest/cfg/nginx/nginx.conf b/docs/manifest/cfg/nginx/nginx.conf index 8ff13af7..1ea97dee 100644 --- a/docs/manifest/cfg/nginx/nginx.conf +++ b/docs/manifest/cfg/nginx/nginx.conf @@ -1,7 +1,7 @@ user nginx; worker_processes 2; error_log /dev/stdout warn; -pid /nginx.pid; +pid /tmp/nginx.pid; events { worker_connections 1024; @@ -12,11 +12,17 @@ http { default_type application/octet-stream; access_log /dev/stdout; + client_body_temp_path /tmp/client_body; + proxy_temp_path /tmp/proxy; + fastcgi_temp_path /tmp/fastcgi; + uwsgi_temp_path /tmp/uwsgi; + scgi_temp_path /tmp/scgi; + sendfile on; keepalive_timeout 65; server { - listen 80 default_server; + listen 8080 default_server; server_name _; error_page 404 /404.html; diff --git a/docs/manifest/deployment.yaml b/docs/manifest/deployment.yaml index 4e357cec..68aea0f5 100644 --- a/docs/manifest/deployment.yaml +++ b/docs/manifest/deployment.yaml @@ -20,10 +20,12 @@ spec: name: nginx-cfg - name: temporary-data emptyDir: {} + - name: nginx-tmp + emptyDir: {} containers: - name: anubis-docs - image: ghcr.io/techarohq/anubis/docs:main - imagePullPolicy: Always + image: ghcr.io/techarohq/anubis/docs@sha256:f13a7c862bbcba8e19feba9f157120c6f03e23b03367ace4ca55da69dc894e12 + imagePullPolicy: IfNotPresent resources: limits: memory: "128Mi" @@ -34,23 +36,36 @@ spec: volumeMounts: - name: nginx mountPath: /conf + - name: nginx-tmp + mountPath: /tmp ports: - - containerPort: 80 + - containerPort: 8080 readinessProbe: httpGet: path: / - port: 80 + port: 8080 initialDelaySeconds: 1 periodSeconds: 10 livenessProbe: httpGet: path: / - port: 80 + port: 8080 initialDelaySeconds: 10 periodSeconds: 20 + securityContext: + runAsUser: 101 + runAsGroup: 101 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault - name: anubis - image: ghcr.io/techarohq/anubis:main - imagePullPolicy: Always + image: ghcr.io/techarohq/anubis@sha256:533e57956ae3afd1612dab16f02dd4db937ca14fad5399208f403686e14feed5 + imagePullPolicy: IfNotPresent env: - name: "BIND" value: ":8081" @@ -65,7 +80,7 @@ spec: - name: "SERVE_ROBOTS_TXT" value: "false" - name: "TARGET" - value: "http://localhost:80" + value: "http://localhost:8080" # - name: "SLOG_LEVEL" # value: "debug" volumeMounts: diff --git a/docs/manifest/service.yaml b/docs/manifest/service.yaml index 1acdc45d..615fe5f6 100644 --- a/docs/manifest/service.yaml +++ b/docs/manifest/service.yaml @@ -7,7 +7,7 @@ spec: app: anubis-docs ports: - port: 80 - targetPort: 80 + targetPort: 8080 name: http - port: 8081 targetPort: 8081