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 <me@xeiaso.net>
This commit is contained in:
Xe Iaso
2026-05-18 22:41:12 -04:00
parent 97d15cd803
commit 652cef7ffe
7 changed files with 46 additions and 13 deletions
+1
View File
@@ -318,6 +318,7 @@ screenshots
searchbot searchbot
searx searx
sebest sebest
seccomp
secretplans secretplans
selfsigned selfsigned
Semrush Semrush
+9
View File
@@ -52,6 +52,15 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true 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 - name: Apply k8s manifests to limsa lominsa
uses: actions-hub/kubectl@934aaa4354bbbc3d2176ae8d7cae92d515032dff # v1.35.3 uses: actions-hub/kubectl@934aaa4354bbbc3d2176ae8d7cae92d515032dff # v1.35.3
env: env:
+2 -2
View File
@@ -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 WORKDIR /app
COPY . . COPY . .
RUN npm ci && npm run build 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 --from=build /app/build /www
COPY ./manifest/cfg/nginx/nginx.conf /conf COPY ./manifest/cfg/nginx/nginx.conf /conf
LABEL org.opencontainers.image.source="https://github.com/TecharoHQ/anubis" LABEL org.opencontainers.image.source="https://github.com/TecharoHQ/anubis"
+2
View File
@@ -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 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)). - 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. - 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 ## v1.25.0: Necron
+8 -2
View File
@@ -1,7 +1,7 @@
user nginx; user nginx;
worker_processes 2; worker_processes 2;
error_log /dev/stdout warn; error_log /dev/stdout warn;
pid /nginx.pid; pid /tmp/nginx.pid;
events { events {
worker_connections 1024; worker_connections 1024;
@@ -12,11 +12,17 @@ http {
default_type application/octet-stream; default_type application/octet-stream;
access_log /dev/stdout; 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; sendfile on;
keepalive_timeout 65; keepalive_timeout 65;
server { server {
listen 80 default_server; listen 8080 default_server;
server_name _; server_name _;
error_page 404 /404.html; error_page 404 /404.html;
+23 -8
View File
@@ -20,10 +20,12 @@ spec:
name: nginx-cfg name: nginx-cfg
- name: temporary-data - name: temporary-data
emptyDir: {} emptyDir: {}
- name: nginx-tmp
emptyDir: {}
containers: containers:
- name: anubis-docs - name: anubis-docs
image: ghcr.io/techarohq/anubis/docs:main image: ghcr.io/techarohq/anubis/docs@sha256:f13a7c862bbcba8e19feba9f157120c6f03e23b03367ace4ca55da69dc894e12
imagePullPolicy: Always imagePullPolicy: IfNotPresent
resources: resources:
limits: limits:
memory: "128Mi" memory: "128Mi"
@@ -34,23 +36,36 @@ spec:
volumeMounts: volumeMounts:
- name: nginx - name: nginx
mountPath: /conf mountPath: /conf
- name: nginx-tmp
mountPath: /tmp
ports: ports:
- containerPort: 80 - containerPort: 8080
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /
port: 80 port: 8080
initialDelaySeconds: 1 initialDelaySeconds: 1
periodSeconds: 10 periodSeconds: 10
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /
port: 80 port: 8080
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 20 periodSeconds: 20
securityContext:
runAsUser: 101
runAsGroup: 101
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
- name: anubis - name: anubis
image: ghcr.io/techarohq/anubis:main image: ghcr.io/techarohq/anubis@sha256:533e57956ae3afd1612dab16f02dd4db937ca14fad5399208f403686e14feed5
imagePullPolicy: Always imagePullPolicy: IfNotPresent
env: env:
- name: "BIND" - name: "BIND"
value: ":8081" value: ":8081"
@@ -65,7 +80,7 @@ spec:
- name: "SERVE_ROBOTS_TXT" - name: "SERVE_ROBOTS_TXT"
value: "false" value: "false"
- name: "TARGET" - name: "TARGET"
value: "http://localhost:80" value: "http://localhost:8080"
# - name: "SLOG_LEVEL" # - name: "SLOG_LEVEL"
# value: "debug" # value: "debug"
volumeMounts: volumeMounts:
+1 -1
View File
@@ -7,7 +7,7 @@ spec:
app: anubis-docs app: anubis-docs
ports: ports:
- port: 80 - port: 80
targetPort: 80 targetPort: 8080
name: http name: http
- port: 8081 - port: 8081
targetPort: 8081 targetPort: 8081