diff --git a/jarrs-system/jarr-dev.yaml b/jarrs-system/jarr-dev.yaml index 85ac4b5..5bebfbd 100644 --- a/jarrs-system/jarr-dev.yaml +++ b/jarrs-system/jarr-dev.yaml @@ -440,6 +440,38 @@ spec: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app --- +# ============================================================================= +# PATCH: jarr-dev.yaml — Ingress section replacement +# ============================================================================= +# Replace the existing `# Ingress` block (from line 473 to end of file) with +# the block below. +# +# What changed: +# Added nginx.ingress.kubernetes.io/configuration-snippet with security +# headers. These apply to ALL responses (SPA root + API routes) at the +# nginx layer, which is the correct place since the SPA at / is outside +# Hono's /v1 basePath middleware chain. +# +# PREREQUISITE — check snippet annotations are allowed in your cluster: +# kubectl -n ingress-nginx get configmap ingress-nginx-controller -o yaml | grep allow-snippet +# If not present or set to "false", add it: +# kubectl -n ingress-nginx edit configmap ingress-nginx-controller +# → add under data: allow-snippet-annotations: "true" +# +# NOTE: HSTS (strict-transport-security) is intentionally NOT in the snippet — +# it is already applied by nginx-ingress automatically when TLS is configured. +# Adding it here would produce a duplicate header. +# +# NOTE on CSP: This CSP is tuned for a Vite/React SPA. +# After applying, open https://dev.jarrs.eu in browser DevTools → Console +# and check for any CSP violations. If you see violations, report them +# and update the policy before pushing to production. +# +# APPLY: +# kubectl apply -f jarr-dev.yaml +# kubectl -n jarrs-system rollout status deployment/dev-jarr +# ============================================================================= + # Ingress apiVersion: networking.k8s.io/v1 kind: Ingress @@ -454,6 +486,14 @@ metadata: cert-manager.io/cluster-issuer: letsencrypt-prod external-dns.alpha.kubernetes.io/hostname: dev.jarrs.eu nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/configuration-snippet: | + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + add_header Cross-Origin-Opener-Policy "same-origin" always; + add_header Cross-Origin-Resource-Policy "same-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always; spec: ingressClassName: nginx-internal rules: