Add Headlamp (kubernetes-sigs/headlamp Helm chart) at headlamp.home.keithjackson.dev with native Keycloak OIDC login and PER-USER RBAC: Headlamp forwards each user's id_token to the k3s apiserver, so the existing oidc:* group bindings (platform-admins->cluster-admin, viewers->view) apply per user. The ServiceAccount is deliberately NOT bound to cluster-admin (clusterRoleBinding.create=false), since user requests ride the forwarded token. Key integration nuance: the apiserver is configured oidc-client-id=kubernetes, so it only accepts tokens whose aud contains `kubernetes`. The new `headlamp` Keycloak client carries an oidc-audience-mapper (included.client.audience= kubernetes) so tokens carry aud=[headlamp, kubernetes] -- accepted by both Headlamp's own verifier and the apiserver, leaving the apiserver config untouched. A groups mapper emits the `groups` claim the apiserver maps to RBAC. - platform/headlamp: namespace, HelmRepository/HelmRelease, SOPS OIDC secret (externalSecret envFrom), default-deny NetworkPolicy + Cilium ingress (gateway)/egress (apiserver+Keycloak) policies, README. Wired into platform/kustomization.yaml (platform tier). - platform/headlamp/httproute.yaml wired into platform-config (Gateway API CR, dependsOn platform), modeled on grafana-httproute. - keycloak-realm-import: add `headlamp` confidential client + groups/audience mappers + HEADLAMP_CLIENT_SECRET placeholder. Client also created LIVE via kcadm (realm import no-ops on the existing realm). - keycloak-realm-clients.sops: add headlamp-client-secret (mirrored into headlamp-oidc-credentials clientSecret). Both encrypted to the repo age key. Co-Authored-By: Claude Opus 4.8 <[email protected]>
33 lines
2.0 KiB
YAML
33 lines
2.0 KiB
YAML
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: headlamp-oidc
|
|
namespace: headlamp
|
|
type: Opaque
|
|
stringData:
|
|
clientID: ENC[AES256_GCM,data:PXrq7Qc830U=,iv:t/cXsXYyQGT7zDUL2LT0cJX/B0a69Efr9wz//wacEzw=,tag:Pnqx2MQvebI6mHXTfd4IvQ==,type:str]
|
|
issuerURL: ENC[AES256_GCM,data:A6YtkQCpqwEeDInEKZgW0E/7WiNiVHxR2rvcv0ZWuuy25N1v9xOMAiaELS55DvJF+JvMiyc=,iv:9VVQoAz27X8eW1XsSUdZAY9tdlSnCfXHnYqYH5/N5gs=,tag:gLZGYm4n1pxAT0EezWY0sg==,type:str]
|
|
scopes: ENC[AES256_GCM,data:bps1T/YTCRN44u+ERVP2vJ2r1PE=,iv:4+6KdXbfVQUp4NYgAkHFRyIpcr9YoQw91G8o/UmEDTA=,tag:bs0JKsveYekxHB45d0Qk0g==,type:str]
|
|
callbackURL: ENC[AES256_GCM,data:ViCO0/9pAxzIN3wzQ7MeOJaZEc3yddQ56SD7/ie3IaL0LP7zkjSIlkZ2hP9wJMqokmETCA==,iv:1X+DbHxnN4yEFzQBjIymOfGPGRbFA+OlJ+bUZ1ZLD0Q=,tag:3UAcnbXdoN0Fed74zNg5bg==,type:str]
|
|
clientSecret: ENC[AES256_GCM,data:SXzue9IzhyNsr2MFxDfEBNCgOTL6XLYqv0E0Rl6YOO62+B0b0V8dVmLKjBsK7VS+P6pXJQNZNDfFfRFC/3f9aA==,iv:sChj2ZDG2XapK7G9xIZ4qH8PvJbSjWVQqy3CEoD8LDU=,tag:3y4EbrlFt1EmzAadMTBN4g==,type:str]
|
|
sops:
|
|
kms: []
|
|
gcp_kms: []
|
|
azure_kv: []
|
|
hc_vault: []
|
|
age:
|
|
- recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg
|
|
enc: |
|
|
-----BEGIN AGE ENCRYPTED FILE-----
|
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3RjJ5MGJ1cEJxMXRXTkZ5
|
|
YURYaU5LTk1rT0kvOE5xMTZMcDVmVjA0TTFVCm1rTzBYaktSVE02TGdKZjBCaEda
|
|
cFQzZjZUTmZEWUs1cTljZk5NUDdRSjgKLS0tIFBjNXlNbFhSMy9yRm1TOE0yeUxr
|
|
dXlZUHdoVWpDS2I3TFBsOVdxc0NadDAKqemweg0FDcUxc93I8t6+o708TWjseh6f
|
|
W0r+NjeEYwcb9iJBGqkO04jP6Z0+QGFdAuQZB8w8AsPYMNTRNTwDwg==
|
|
-----END AGE ENCRYPTED FILE-----
|
|
lastmodified: "2026-08-28T16:39:25Z"
|
|
mac: ENC[AES256_GCM,data:QtrqHMgaldW05Zag5AuH44+zBcyDJXTWhnuDVoV6UveMGSep/u4L/paMvA6ubuyZN07qfEv0SZVgybFh2EWKw/7dhE4Wbp0sttnrZ62J3TTnE3U6y9MbkLIuaM5+19Wfi7xVQ9Ovb9c1gELgEpCIo5KDSquA3yEvu1JBwHwmA2s=,iv:CAR7LqRItL5czUeCmr1QtTKlEqAFoiHBtHq24e6zNZs=,tag:YAciIDYGryK8kXQpay+etg==,type:str]
|
|
pgp: []
|
|
encrypted_regex: ^(data|stringData)$
|
|
version: 3.9.4
|