Files
homelab-frontend/k8s/network-policy.yaml
T
Admin Bot 8a3a1c447c feat(auth): validate access tokens with the ZITADEL Go SDK
Replace the handwritten Authentik JWT checks with github.com/zitadel/zitadel-go/v3.

What the SDK now owns:
- OIDC discovery (client.Discover) to find issuer and JWKS URI
- remote, auto-refreshing JWKS (rp.NewRemoteKeySet)
- signature, expiry and issuer checks (op.NewAccessTokenVerifier,
  op.VerifyAccessToken)
- the verified claim set and role lookup (oauth.IntrospectionContext,
  IsGrantedRole)

What stays local:
- a multi-audience allow-list, because this gateway is a shared resource
  server and the SDK's oauth.WithJWT accepts exactly one client ID
- an adapter from IntrospectionContext to jwt.MapClaims so the rest of the
  gateway keeps one claim representation

Other changes:
- issuer is configuration-driven; the hardcoded authentik.riotpiao.com
  check is gone
- aud may be a string or an array (ZITADEL sends an array)
- CheckPermissions also reads urn:zitadel:iam:org:project:roles, the claim
  ZITADEL uses for project roles
- requiredCapability accepts a comma-separated list, so llm:inference and
  llm-inference both satisfy the LLM gate
- a five-segment token is reported as an encrypted JWE with the fix in the
  message, instead of failing as a bad signature

Config: auth.zitadelDomain turns the SDK path on; jwksUrl stays supported for
the legacy path and for tests.
2026-10-26 10:23:00 -07:00

188 lines
5.1 KiB
YAML

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-gateway
namespace: api
labels:
app: api-gateway
spec:
podSelector:
matchLabels:
app: api-gateway
policyTypes:
- Ingress
- Egress
ingress:
# Allow from ingress-nginx controller (from ingress-nginx namespace)
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
# Allow from Prometheus scraping (monitoring namespace)
# MUST select on kubernetes.io/metadata.name, not a plain `name:` label.
# Only ingress-nginx carries a plain `name` label in this cluster; every
# other namespace has just kubernetes.io/metadata.name, so `name: monitoring`
# matched nothing and silently denied every Prometheus scrape of :8080
# (surfaced as context-deadline-exceeded on all 3 gateway replicas while the
# ServiceMonitor itself was discovered and healthy).
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 8080
# Allow from poimen namespace (orchestrator & worker pods)
# Enable Poimen workflows to call the LLM API gateway
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: poimen
ports:
- protocol: TCP
port: 8080
# Allow from portfolio namespace (riotpiao.com chat terminal)
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: portfolio
ports:
- protocol: TCP
port: 8080
# Allow from paperless namespace (paperless-ai document auto-tagging)
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: paperless
ports:
- protocol: TCP
port: 8080
egress:
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# Allow to upstreams (LLM services in llm-serving namespace)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: llm-serving
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 8000
- protocol: TCP
port: 8001
# Allow to other upstreams if needed (embeddings, reranker, etc.)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: llm-serving
ports:
- protocol: TCP
port: 8080
# Allow to atlas (riotpiao-backend) for /cluster/* routes
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: atlas
ports:
- protocol: TCP
port: 8080
# Allow to IAM (Zitadel) for JWKS fetch + API
# Zitadel API listens on 8080 (h2c/plaintext in-cluster); the login UI
# listens on 3000. Public access goes through ingress-nginx on 443 (already
# covered by the 10.96.0.0/12 CIDR below), but 8080 keeps direct in-cluster
# calls working if CoreDNS rewrite is bypassed.
# NetworkPolicy matches destination pod port, not service port.
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: iam
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 3000
# Allow to SQS (queue management service)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: sqs
ports:
- protocol: TCP
port: 9090
# Allow to Temporal (workflow engine gRPC)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: temporal
ports:
- protocol: TCP
port: 7233
# Allow to Poimen (memory:8080 + graphql:8090 + redis:6379 + fleet-controller:8788)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: poimen
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 8090
- protocol: TCP
port: 6379
- protocol: TCP
port: 8788
# Allow to Gotify (push notifications)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: notifications
ports:
- protocol: TCP
port: 80
# Allow to MinIO (S3-compatible storage)
# Service `minio` listens on port 80 (targetPort 9000).
# Headless `minio-cluster-hl` is 9000. Allow both.
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: storage
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 9000
# Allow to Forgejo registry v2 API (image digest lookup for promotion)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: cicd
ports:
- protocol: TCP
port: 3000
# Allow to kubernetes API server (ArgoCD Application patching via in-cluster SA).
# kube-apiserver runs on host network (192.168.1.162/166:6443) — must use ipBlock.
# ClusterIP 10.96.0.1:443 also routes there, covered by the 10.96.0.0/12 CIDR.
- to:
- ipBlock:
cidr: 10.96.0.0/12
- ipBlock:
cidr: 192.168.1.0/24
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 6443