Security hardening: TLS at origin, security headers, network policies, admin probe fix
Four related hardening changes made on the live cluster during this session. Each manifest captures the final working state so a fresh `kubectl apply` of the repo reproduces it. 1. Cloudflare Full (strict) TLS — ingresses now carry `tls:` blocks pointing at `cloudflare-origin-cert` secret (installed imperatively from the CF Origin CA PEM). CF SSL mode flipped from Flexible to Full (strict). CF↔origin is now HTTPS; origin serves a CF-issued cert that only CF can validate. 2. Traefik middleware attached to all three ingresses — `rate-limit` (100/min avg, 200 burst) and `security-headers` (frame-deny, nosniff, HSTS, referrer policy, permissions policy). `admin-auth` middleware was also defined in middleware.yaml but is not attached (needs an unset basic-auth secret) and was deleted at runtime. 3. `security-headers` middleware: stripped the Content-Security-Policy entry. The Go API sets its own CSP in internal/router/router.go that permits Google Fonts for the landing page. Two CSP headers combine via intersection (most restrictive wins), which would break the landing page. Next.js apps set their own CSP via middleware. Header kept documentation comments explain this. 4. NetworkPolicies — default-deny + explicit allows, applied. Added missing policies for `web`. Corrected the Traefik ingress rule: the scaffold used `namespaceSelector: kube-system`, but our Traefik runs as a DaemonSet with `hostNetwork: true`, so traffic arrives with the NODE IP as source. Fixed to an `ipBlock` list of the three node IPs plus the cluster pod CIDR (10.42.0.0/16). 5. admin livenessProbe path fix: was hitting /admin/ (404) which caused a 6-hour crashloop cycle (87 restarts) before the bug was caught. Fixed to / — matches the startupProbe and readinessProbe paths that were corrected earlier. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,10 +47,19 @@ spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
# Traefik runs as DaemonSet with hostNetwork=true, so traffic from it
|
||||
# arrives with the NODE IP as source (not a pod IP). The node pod CIDR
|
||||
# 10.42.0.0/16 covers any intra-cluster caller; the three node IPs
|
||||
# cover Traefik on hostNetwork.
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
- ipBlock:
|
||||
cidr: 178.105.32.198/32 # ubuntu-8gb-nbg1-1
|
||||
- ipBlock:
|
||||
cidr: 178.104.247.152/32 # ubuntu-8gb-nbg1-2
|
||||
- ipBlock:
|
||||
cidr: 178.104.249.189/32 # ubuntu-8gb-nbg1-3
|
||||
- ipBlock:
|
||||
cidr: 10.42.0.0/16 # cluster pod CIDR
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8000
|
||||
@@ -69,10 +78,17 @@ spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
# Traefik runs as DaemonSet with hostNetwork=true — see allow-ingress-to-api
|
||||
# for the rationale. Same ipBlock list.
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
- ipBlock:
|
||||
cidr: 178.105.32.198/32
|
||||
- ipBlock:
|
||||
cidr: 178.104.247.152/32
|
||||
- ipBlock:
|
||||
cidr: 178.104.249.189/32
|
||||
- ipBlock:
|
||||
cidr: 10.42.0.0/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3000
|
||||
@@ -200,3 +216,62 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8000
|
||||
|
||||
---
|
||||
# --- Web: allow ingress from Traefik (kube-system namespace) ---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-ingress-to-web
|
||||
namespace: honeydue
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: web
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
# Traefik runs as DaemonSet with hostNetwork=true — see allow-ingress-to-api
|
||||
# for the rationale. Same ipBlock list.
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 178.105.32.198/32
|
||||
- ipBlock:
|
||||
cidr: 178.104.247.152/32
|
||||
- ipBlock:
|
||||
cidr: 178.104.249.189/32
|
||||
- ipBlock:
|
||||
cidr: 10.42.0.0/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3000
|
||||
|
||||
---
|
||||
# --- Web: allow egress for the Next.js server-side proxy routes ---
|
||||
# Browser → app.myhoneydue.com → web pod (Node.js) → api.myhoneydue.com
|
||||
# The web pod resolves api.myhoneydue.com via public DNS and hits
|
||||
# Cloudflare (143.). We don't know which CF IP yet at policy time, so
|
||||
# allow HTTPS to public ipBlock (except private CIDRs).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-egress-from-web
|
||||
namespace: honeydue
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: web
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
# HTTPS to public (api.myhoneydue.com via CF, PostHog, any other remote)
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
|
||||
Reference in New Issue
Block a user