phase 14-15: docker build validation and helm deployment

This commit is contained in:
Celes Renata
2026-04-11 11:59:45 -07:00
parent 7394d241c9
commit ce10afa034
179 changed files with 32559 additions and 576 deletions
+212
View File
@@ -0,0 +1,212 @@
"""Tests for Kubernetes manifest security hardening.
Validates that all deployments in infra/k8s/ follow security best practices:
- Scoped secrets (no monolithic stonks-secrets)
- Pod security contexts (runAsNonRoot, seccompProfile)
- Container security contexts (no privilege escalation, drop ALL caps)
- automountServiceAccountToken disabled
- Broker secrets only on trading-tier pods
"""
from __future__ import annotations
import glob
from pathlib import Path
import yaml
K8S_DIR = Path("infra/k8s")
# Services that legitimately need broker secrets
BROKER_SECRET_ALLOWED = {"broker-adapter", "risk-engine"}
# Services that legitimately need market-data secrets
MARKET_SECRET_ALLOWED = {"ingestion-worker"}
def _load_deployments() -> list[tuple[str, dict]]:
"""Load all Deployment objects from infra/k8s/*.yaml."""
deployments = []
for path in sorted(K8S_DIR.glob("*.yaml")):
with open(path) as f:
for doc in yaml.safe_load_all(f):
if doc and doc.get("kind") == "Deployment":
name = doc["metadata"]["name"]
deployments.append((name, doc))
return deployments
def _get_secret_refs(spec: dict) -> list[str]:
"""Extract all secretRef names from a pod spec's envFrom."""
refs = []
for container in spec.get("containers", []):
for env_from in container.get("envFrom", []):
secret = env_from.get("secretRef", {})
if secret.get("name"):
refs.append(secret["name"])
return refs
class TestSecretScoping:
"""Verify that the monolithic stonks-secrets is no longer used."""
def test_no_monolithic_secret_ref(self):
"""No deployment should reference the old stonks-secrets."""
for name, dep in _load_deployments():
pod_spec = dep["spec"]["template"]["spec"]
refs = _get_secret_refs(pod_spec)
assert "stonks-secrets" not in refs, (
f"Deployment {name} still references monolithic stonks-secrets"
)
def test_broker_secrets_only_on_trading_tier(self):
"""Only broker-adapter and risk-engine should have broker secrets."""
for name, dep in _load_deployments():
pod_spec = dep["spec"]["template"]["spec"]
refs = _get_secret_refs(pod_spec)
if "stonks-broker-secrets" in refs:
assert name in BROKER_SECRET_ALLOWED, (
f"Deployment {name} has broker secrets but is not in "
f"allowed set {BROKER_SECRET_ALLOWED}"
)
def test_market_secrets_only_on_ingestion(self):
"""Only ingestion-worker should have market-data secrets."""
for name, dep in _load_deployments():
pod_spec = dep["spec"]["template"]["spec"]
refs = _get_secret_refs(pod_spec)
if "stonks-market-secrets" in refs:
assert name in MARKET_SECRET_ALLOWED, (
f"Deployment {name} has market secrets but is not in "
f"allowed set {MARKET_SECRET_ALLOWED}"
)
class TestPodSecurityContext:
"""Verify pod-level security settings."""
def test_run_as_non_root(self):
for name, dep in _load_deployments():
pod_sec = dep["spec"]["template"]["spec"].get("securityContext", {})
assert pod_sec.get("runAsNonRoot") is True, (
f"Deployment {name} missing runAsNonRoot: true"
)
def test_seccomp_profile(self):
for name, dep in _load_deployments():
pod_sec = dep["spec"]["template"]["spec"].get("securityContext", {})
seccomp = pod_sec.get("seccompProfile", {})
assert seccomp.get("type") == "RuntimeDefault", (
f"Deployment {name} missing seccompProfile RuntimeDefault"
)
def test_automount_service_account_disabled(self):
for name, dep in _load_deployments():
pod_spec = dep["spec"]["template"]["spec"]
assert pod_spec.get("automountServiceAccountToken") is False, (
f"Deployment {name} should set automountServiceAccountToken: false"
)
class TestContainerSecurityContext:
"""Verify container-level security settings."""
def test_no_privilege_escalation(self):
for name, dep in _load_deployments():
for container in dep["spec"]["template"]["spec"]["containers"]:
sec = container.get("securityContext", {})
assert sec.get("allowPrivilegeEscalation") is False, (
f"Deployment {name}, container {container['name']} "
f"missing allowPrivilegeEscalation: false"
)
def test_drop_all_capabilities(self):
for name, dep in _load_deployments():
for container in dep["spec"]["template"]["spec"]["containers"]:
sec = container.get("securityContext", {})
caps = sec.get("capabilities", {})
assert "ALL" in caps.get("drop", []), (
f"Deployment {name}, container {container['name']} "
f"should drop ALL capabilities"
)
class TestNetworkPolicies:
"""Verify network policy manifests exist and cover key patterns."""
def _load_netpols(self) -> list[dict]:
policies = []
for path in K8S_DIR.glob("*.yaml"):
with open(path) as f:
for doc in yaml.safe_load_all(f):
if doc and doc.get("kind") == "NetworkPolicy":
policies.append(doc)
return policies
def test_default_deny_exists(self):
policies = self._load_netpols()
deny_policies = [
p for p in policies
if p["metadata"]["name"] == "default-deny-ingress"
]
assert len(deny_policies) == 1, "Missing default-deny-ingress NetworkPolicy"
def test_broker_adapter_denied_ingress(self):
policies = self._load_netpols()
broker_policies = [
p for p in policies
if p["spec"].get("podSelector", {}).get("matchLabels", {}).get("app") == "broker-adapter"
]
assert len(broker_policies) >= 1, "Missing NetworkPolicy for broker-adapter"
# Should have empty ingress (deny all inbound)
for p in broker_policies:
assert p["spec"].get("ingress") == [] or p["spec"].get("ingress") is None, (
"broker-adapter should deny all ingress"
)
def test_risk_engine_restricted_ingress(self):
policies = self._load_netpols()
risk_policies = [
p for p in policies
if p["spec"].get("podSelector", {}).get("matchLabels", {}).get("app") == "risk-engine"
]
assert len(risk_policies) >= 1, "Missing NetworkPolicy for risk-engine"
class TestSecretsManifest:
"""Verify the secrets manifest uses scoped secrets."""
def _load_secrets(self) -> list[dict]:
secrets = []
path = K8S_DIR / "secrets.yaml"
with open(path) as f:
for doc in yaml.safe_load_all(f):
if doc and doc.get("kind") == "Secret":
secrets.append(doc)
return secrets
def test_scoped_secrets_exist(self):
secrets = self._load_secrets()
names = {s["metadata"]["name"] for s in secrets}
assert "stonks-core-secrets" in names
assert "stonks-broker-secrets" in names
assert "stonks-market-secrets" in names
assert "stonks-dashboard-secrets" in names
def test_no_monolithic_secret(self):
secrets = self._load_secrets()
names = {s["metadata"]["name"] for s in secrets}
assert "stonks-secrets" not in names, (
"Monolithic stonks-secrets should be replaced by scoped secrets"
)
def test_no_plaintext_defaults(self):
"""Secret values should be REPLACE_ME placeholders, not real defaults."""
secrets = self._load_secrets()
for secret in secrets:
for key, value in secret.get("stringData", {}).items():
if value: # skip empty strings (e.g. REDIS_PASSWORD)
assert value != "changeme", (
f"Secret {secret['metadata']['name']}.{key} "
f"still has 'changeme' default"
)