Adds rate-limit Middleware (100 req/s sustained, burst 200), keyed off the real client IP via sourceCriterion.ipStrategy.excludedIPs (same trusted pod CIDRs already used by forwardedHeaders/crowdsec-bouncer) so it doesn't collapse every external client onto one shared budget behind the netbird-ingress proxy.
Wires both as websecure entrypoint defaults via helmrelease.yamladditionalArguments — the same mechanism crowdsec-middleware.yaml documents for its own (still-detached) re-enable — so every router gets them without per-Ingress annotations.
Why these defaults
CSP is deliberately limited to frame-ancestors 'self' rather than a full default-src policy: apps behind this entrypoint (Paperless, Immich, Grafana, Gitea, Home Assistant, ...) have very different script/style/font sources, and a strict default-src would need per-app tuning to avoid breaking them. Verified no existing iframe/CSP/X-Frame-Options usage in the repo that this could conflict with.
Rate-limit values are generous by design (baseline abuse/brute-force backstop, not traffic shaping) given this applies globally with no per-app tuning yet (Loki pushes, Immich uploads, git-over-https, long-lived websockets all share this entrypoint).
Explicitly out of scope
CrowdSec enforcement re-enable is independent, already-documented work (disabled since the 2026-06-27 IPv6 mass-ban outage) — not touched here.
Test plan
kubectl kustomize kubernetes/infrastructure/traefik renders all 4 manifests (HelmRelease with new additionalArguments, HelmRepository, both new Middlewares) without error.
After Flux reconciles: confirm curl -I against a websecure host shows Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, Content-Security-Policy headers, and that normal app traffic (Loki push, Immich upload, git-over-https, HA websocket) is unaffected.
Fixes #205
## Summary
- Adds `security-headers` Middleware (HSTS, nosniff, xss-filter, referrer-policy, permissions-policy, conservative `frame-ancestors 'self'` CSP + `X-Frame-Options: SAMEORIGIN`).
- Adds `rate-limit` Middleware (100 req/s sustained, burst 200), keyed off the real client IP via `sourceCriterion.ipStrategy.excludedIPs` (same trusted pod CIDRs already used by `forwardedHeaders`/`crowdsec-bouncer`) so it doesn't collapse every external client onto one shared budget behind the netbird-ingress proxy.
- Wires both as **websecure entrypoint defaults** via `helmrelease.yaml` `additionalArguments` — the same mechanism `crowdsec-middleware.yaml` documents for its own (still-detached) re-enable — so every router gets them without per-Ingress annotations.
## Why these defaults
CSP is deliberately limited to `frame-ancestors 'self'` rather than a full `default-src` policy: apps behind this entrypoint (Paperless, Immich, Grafana, Gitea, Home Assistant, ...) have very different script/style/font sources, and a strict `default-src` would need per-app tuning to avoid breaking them. Verified no existing iframe/CSP/X-Frame-Options usage in the repo that this could conflict with.
Rate-limit values are generous by design (baseline abuse/brute-force backstop, not traffic shaping) given this applies globally with no per-app tuning yet (Loki pushes, Immich uploads, git-over-https, long-lived websockets all share this entrypoint).
## Explicitly out of scope
CrowdSec enforcement re-enable is independent, already-documented work (disabled since the 2026-06-27 IPv6 mass-ban outage) — not touched here.
## Test plan
- [x] `kubectl kustomize kubernetes/infrastructure/traefik` renders all 4 manifests (HelmRelease with new `additionalArguments`, HelmRepository, both new Middlewares) without error.
- [ ] After Flux reconciles: confirm `curl -I` against a websecure host shows `Strict-Transport-Security`, `X-Content-Type-Options`, `X-Frame-Options`, `Content-Security-Policy` headers, and that normal app traffic (Loki push, Immich upload, git-over-https, HA websocket) is unaffected.
Closes#205. The websecure entrypoint had zero response hardening and no
rate limiting — CrowdSec is detection-only since the 2026-06-27 IPv6
mass-ban outage, and no headers/rate-limit Middleware existed anywhere in
the cluster, so logins sat behind app-level throttling only.
Adds two Middlewares wired as entrypoint defaults via the same
additionalArguments mechanism the (still-detached) crowdsec-bouncer
Middleware documents for its own re-enable:
- security-headers: HSTS, nosniff, xss-filter, referrer-policy,
permissions-policy, and a conservative frame-ancestors 'self' CSP
(deliberately not a full default-src policy — apps here span Paperless,
Immich, Grafana, Gitea, Home Assistant, etc. with very different
script/style/font sources).
- rate-limit: generous defaults (100 req/s, burst 200) as a baseline
abuse/brute-force backstop rather than traffic shaping, keyed off the
real client IP via sourceCriterion.ipStrategy.excludedIPs (same trusted
pod CIDRs as forwardedHeaders/crowdsec-bouncer) so it doesn't collapse
every external client onto one shared budget — the same client-IP-
visibility gap that caused the CrowdSec outage this fix is otherwise
independent of.
CrowdSec enforcement itself stays disabled; that re-enable is separate,
documented, unaddressed work.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #205
Summary
security-headersMiddleware (HSTS, nosniff, xss-filter, referrer-policy, permissions-policy, conservativeframe-ancestors 'self'CSP +X-Frame-Options: SAMEORIGIN).rate-limitMiddleware (100 req/s sustained, burst 200), keyed off the real client IP viasourceCriterion.ipStrategy.excludedIPs(same trusted pod CIDRs already used byforwardedHeaders/crowdsec-bouncer) so it doesn't collapse every external client onto one shared budget behind the netbird-ingress proxy.helmrelease.yamladditionalArguments— the same mechanismcrowdsec-middleware.yamldocuments for its own (still-detached) re-enable — so every router gets them without per-Ingress annotations.Why these defaults
CSP is deliberately limited to
frame-ancestors 'self'rather than a fulldefault-srcpolicy: apps behind this entrypoint (Paperless, Immich, Grafana, Gitea, Home Assistant, ...) have very different script/style/font sources, and a strictdefault-srcwould need per-app tuning to avoid breaking them. Verified no existing iframe/CSP/X-Frame-Options usage in the repo that this could conflict with.Rate-limit values are generous by design (baseline abuse/brute-force backstop, not traffic shaping) given this applies globally with no per-app tuning yet (Loki pushes, Immich uploads, git-over-https, long-lived websockets all share this entrypoint).
Explicitly out of scope
CrowdSec enforcement re-enable is independent, already-documented work (disabled since the 2026-06-27 IPv6 mass-ban outage) — not touched here.
Test plan
kubectl kustomize kubernetes/infrastructure/traefikrenders all 4 manifests (HelmRelease with newadditionalArguments, HelmRepository, both new Middlewares) without error.curl -Iagainst a websecure host showsStrict-Transport-Security,X-Content-Type-Options,X-Frame-Options,Content-Security-Policyheaders, and that normal app traffic (Loki push, Immich upload, git-over-https, HA websocket) is unaffected.