Security audit: network policy & infrastructure#215
Conversation
Comprehensive defensive audit of K8s deployment, container security, Redis TLS, CORS, rate limiting, security headers, and CI/CD pipeline. Identifies 2 critical (missing NetworkPolicy, missing securityContext), 4 high, 5 medium, and 4 low findings with remediation code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@claude code review |
|
Claude finished @RafaelPo's task in 2m 52s —— View job Code Review: Network Policy & Infrastructure Audit
Overall: This is a thorough and well-structured security audit. I cross-referenced all 15 findings against the actual codebase and every file path and line number reference is accurate. The severity ratings are reasonable, the remediation priority matrix is practical, and the "Security Strengths" section provides valuable balance. A few issues with the remediation snippets are worth fixing before merge. Issues in Remediation Code1. C1 — NetworkPolicy ingress allows from wrong namespace ( The # Current (may not work):
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: cert-manager
# Should be verified against actual proxy namespace:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: <actual-gateway-proxy-namespace>Suggest adding a note that the namespace selector needs to be validated against the cluster's gateway implementation, e.g.: "Verify which namespace runs the gateway proxy pods: 2. C2 — The Dockerfile creates a dedicated # Current recommendation (will break):
runAsUser: 65534
# Should match the Dockerfile's mcp user UID:
runAsUser: 999 # or whatever UID was assigned to 'mcp' - check with: docker run <image> id mcpSimilarly 3. H3 — Trivy action version not pinned to SHA ( The audit recommends # Current:
uses: aquasecurity/trivy-action@0.28.0
# Should be SHA-pinned per L2:
uses: aquasecurity/trivy-action@<commit-sha> # v0.28.0Minor Suggestions4. C1 — DNS egress could be tighter ( The DNS egress rule uses - to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns5. L4 — PDB with A 6. M3 — CORS wildcard is intentionally justified ( The audit recommends restricting CORS to What's Done Well
Verdict: The audit is high quality and ready to merge after fixing issues #1 and #2 above (incorrect gateway namespace and UID mismatch), as these could lead to broken deployments if the remediation is copy-pasted as-is. | |
C1: Add networkpolicy.yaml with default-deny-all, gateway ingress allowlist, and egress restricted to kube-dns, Redis, and external HTTPS. Gateway namespace and Redis selector are configurable via values.yaml. C2: Add pod and container securityContext to deployment.yaml: runAsNonRoot, runAsUser/Group 10000, readOnlyRootFilesystem, allowPrivilegeEscalation: false, drop ALL capabilities, Seccomp RuntimeDefault. Dockerfile updated to pin mcp user to UID/GID 10000. Also: dedicated ServiceAccount with automountServiceAccountToken: false (H2), and audit doc updated per code review feedback (gateway namespace note, UID fix, DNS egress tightened, PDB caveat, CORS severity downgrade, Trivy SHA-pin note). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The infra fixes (NetworkPolicy, securityContext, ServiceAccount) stand on their own — the audit document is internal-only and does not belong in the repository. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@claude code review |
|
Claude finished @RafaelPo's task in 1m 50s —— View job Code Review: Network Policy & Infrastructure Security
Overall: Solid hardening PR. The previous review's critical issues (UID mismatch, gateway namespace, DNS egress scope) have all been properly addressed. The UID is now pinned to 10000 in both Dockerfile and deployment spec, the gateway namespace is configurable via values, and DNS egress is scoped to kube-system. I found a few remaining issues worth addressing. Issues1.
# Stage 2: Slim runtime
FROM python:3.13-slim
+ENV PYTHONDONTWRITEBYTECODE=1
+ENV PYTHONUNBUFFERED=1
+
RUN groupadd -r -g 10000 mcp && useradd -r -u 10000 -g mcp -d /app -s /sbin/nologin mcp2. NetworkPolicy names will collide if multiple releases share a namespace — The policies use static names ( # Current:
name: default-deny-all
# Suggested:
name: {{ .Release.Name }}-default-deny-allSame for 3.
4. Redis egress may need a The Redis selector uses only # values.yaml
redisSelector:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: redis # or same-namespace if co-located
podSelector:
matchLabels:
app: redisThis may not be an issue if Redis is indeed co-located, but worth verifying. Minor5. Redundant Set at both the ServiceAccount level and the pod spec level. Not harmful (belt-and-suspenders), but one location is sufficient. The pod spec takes precedence. 6. ServiceAccount missing standard Helm labels — The ServiceAccount doesn't include What's Done Well
Verdict: Address issue #1 ( | |
Multi-stage builds don't inherit ENVs. Without this, Python tries to write .pyc files into the read-only /app/.venv/ directory, causing PermissionError with readOnlyRootFilesystem: true. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Production and staging use Redis Sentinel (REDIS_SENTINEL_ENDPOINTS), which means Redis is cross-namespace or external — a podSelector in the same namespace would silently block all Redis traffic. Switch to ipBlock CIDRs (redisCIDRs in values.yaml). Defaults to 10.0.0.0/8 with a TODO to narrow once Sentinel IPs are confirmed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Key Findings
NETWORK_POLICY_AUDIT.mdTest plan
🤖 Generated with Claude Code