Remove components not needed for PaaS-focused infrastructure:
- argo-rollouts: only used by arch-docs canary, convert to plain Deployment
- oauth2-proxy: was for dev/staging auth (removed in Phase 18)
- nginx-test: test deployment, not needed
- kube-bench: CIS benchmark scanner, not needed for PaaS
- trivy-operator: vulnerability scanner, not needed for PaaS
- drift-check RBAC: drift-check service being removed
arch-docs-prod: rollout.enabled=false → Helm uses Deployment template
- Add control-plane toleration to trivy nodeCollector so it can
schedule on k8s-master (was stuck Pending indefinitely)
- Add ignoreDifferences for CRDs + ServerSideApply to argo-rollouts
to resolve perpetual OutOfSync caused by Helm CRD management gap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SSA causes perpetual OutOfSync on CRDs due to field manager conflicts.
Client-side apply works correctly for Helm charts with CRDs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use jqPathExpressions to ignore entire .metadata and .spec.versions
schema sections on CRDs, which drift due to ServerSideApply field
manager changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ignoreDifferences for CRDs (metadata labels/annotations drift
caused by ServerSideApply field managers) and RespectIgnoreDifferences
sync option.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use keycloak-config-cli env var substitution $(env:VAR_NAME) to inject
user passwords from K8s Secret instead of hardcoding them in ConfigMap.
- realm-configmap.yaml: passwords replaced with $(env:KC_INFRA_ADMIN_PASSWORD)
and $(env:KC_INFRA_CLAUDE_PASSWORD)
- keycloak ArgoCD app: added keycloakConfigCli.extraEnvVarsSecret
- Secrets sourced from OpenBao via create-keycloak-secrets.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bitnami Keycloak chart auto-sets KC_HOSTNAME from ingress.hostname.
Override with empty string via extraEnvVars so Keycloak derives URLs
from request headers (X-Forwarded-* via ingress, Host via NodePort).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
With KC_HOSTNAME set, Keycloak always redirects to the configured
hostname in login form actions, breaking OAuth when accessed via
NodePort (127.0.0.1:30880). Without KC_HOSTNAME, Keycloak derives
URLs from request headers:
- Ingress: X-Forwarded-Host/Proto → https://keycloak.georgepet...
- NodePort: Host header → http://127.0.0.1:30880
KC_PROXY_HEADERS=xforwarded is kept to trust ingress-nginx headers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When accessing Keycloak via NodePort (127.0.0.1:30880), strict hostname
forces redirects to keycloak.georgepet.duckdns.org which is unreachable
from local browser. With strict=false, Keycloak uses the request's host
header for redirects when accessed via NodePort, while still using the
configured hostname for ingress access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add infra-operators group to Keycloak realm
- Add K8s RBAC: operators get full CRUD in dev/staging, readonly in prod,
cluster-level readonly for nodes/namespaces/storage, no infra ns access
- Update ArgoCD RBAC: operators → role:readonly
- Update oauth2-proxy: allow infra-operators group
- Add PostgreSQL NodePort (35432) for OpenBao Database engine access
- Update NetworkPolicy: allow NodePort traffic from node CIDR
- Extend keycloak-secrets-manager Role: statefulset get/patch for rotation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create least-privilege ServiceAccounts for k8s-audit, drift-check,
and keycloak-secrets-manager instead of sharing admin kubeconfig.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keycloak sends groups as 'infra-admins' but oauth2-proxy config
expected '/infra-admins'. Accept both formats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Internal redeem_url caused context canceled errors because
KC_HOSTNAME_STRICT rejects mismatched hostnames. Use public
HTTPS URLs for all OIDC endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin console is protected by Keycloak's own auth. No need for
external path blocking — brute-force protection is built-in.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Enable Keycloak ingress at keycloak.georgepet.duckdns.org with TLS
- Update KC_HOSTNAME to public domain, add KC_PROXY_HEADERS=xforwarded
- Remove KC_HOSTNAME_PORT and KC_HTTP_ENABLED (standard HTTPS via ingress)
- Block /admin and /realms/master externally via server-snippet
- Update oauth2-proxy login_url and oidc_issuer_url to public HTTPS URL
- Keep redeem/jwks/profile URLs internal (keycloak.keycloak.svc:8080)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Override all image references to use bitnamilegacy/* since Bitnami
removed all free Docker images from docker.io/bitnami/* on 2025-08-28.
Images overridden:
- bitnamilegacy/keycloak:26.3.3-debian-12-r0
- bitnamilegacy/keycloak-config-cli:6.4.0-debian-12-r11
- bitnamilegacy/postgresql:17.6.0-debian-12-r0
- bitnamilegacy/os-shell:12-debian-12-r50
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bitnami Keycloak chart 25.x (Keycloak 26.x) deprecated the 'proxy' parameter.
Production mode requires proxyHeaders to be set instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>