Files
home-network-k3s-impl/platform/keycloak/keycloak-realm-import.yaml
T

172 lines
6.5 KiB
YAML

---
# Identity config as code (R-IDP-03): the `homelab` realm — groups, roles, OIDC clients, brute-force
# hardening, and a conditional MFA flow that REQUIRES OTP for platform-admins (R-IDP-04). Reconciled
# declaratively by the Keycloak Operator; CARRIED BY platform-config (dependsOn platform).
#
# Client secrets are NOT in this file: they are injected from the SOPS-encrypted keycloak-realm-clients
# Secret via spec.placeholders ($(env:...)) — so secrets exist only encrypted (R-IDP-04; SOPS, docs/07).
# Until that Secret is filled (operator hand-off, see keycloak-realm-clients.sops.yaml.example), this
# import stays PENDING on the unresolved placeholder.
apiVersion: k8s.keycloak.org/v2alpha1
kind: KeycloakRealmImport
metadata:
name: homelab
namespace: keycloak
spec:
keycloakCRName: keycloak
placeholders:
GRAFANA_CLIENT_SECRET:
secret:
name: keycloak-realm-clients
key: grafana-client-secret
OAUTH2_PROXY_CLIENT_SECRET:
secret:
name: keycloak-realm-clients
key: oauth2-proxy-client-secret
realm:
realm: homelab
enabled: true
displayName: "Homelab SSO"
loginWithEmailAllowed: true
registrationAllowed: false
resetPasswordAllowed: true
# Hardening (docs/15): brute-force detection ON; SSL required for external (edge-TLS) requests.
bruteForceProtected: true
permanentLockout: false
sslRequired: external
# --- Authorization model (R-IDP-06): groups drive access; platform-admins carry a realm role. ---
roles:
realm:
- name: platform-admin
description: "Privileged platform administrators — MFA required (R-IDP-04)."
groups:
- name: platform-admins
realmRoles:
- platform-admin
- name: media-users
- name: viewers
# --- OIDC clients (native SSO). Group membership is exposed as a `groups` claim so apps and
# oauth2-proxy can authorize by group (R-IDP-06). ---
clients:
# Grafana — the concrete native-OIDC SSO proof target (docs/10 / deliverable #4).
- clientId: grafana
name: Grafana
protocol: openid-connect
enabled: true
publicClient: false
standardFlowEnabled: true
directAccessGrantsEnabled: false
secret: "$(env:GRAFANA_CLIENT_SECRET)"
redirectUris:
- https://grafana.home.keithjackson.dev/login/generic_oauth
webOrigins:
- https://grafana.home.keithjackson.dev
defaultClientScopes:
- email
- profile
- roles
protocolMappers:
- name: groups
protocol: openid-connect
protocolMapper: oidc-group-membership-mapper
config:
"full.path": "false"
"id.token.claim": "true"
"access.token.claim": "true"
"userinfo.token.claim": "true"
"claim.name": groups
# oauth2-proxy — forward-auth for non-OIDC UIs (PiHole, *arr, Prometheus, Alertmanager, …).
- clientId: oauth2-proxy
name: oauth2-proxy (forward-auth)
protocol: openid-connect
enabled: true
publicClient: false
standardFlowEnabled: true
directAccessGrantsEnabled: false
secret: "$(env:OAUTH2_PROXY_CLIENT_SECRET)"
redirectUris:
- https://*.home.keithjackson.dev/oauth2/callback
- https://auth.home.keithjackson.dev/oauth2/callback
webOrigins:
- https://*.home.keithjackson.dev
defaultClientScopes:
- email
- profile
- roles
protocolMappers:
- name: groups
protocol: openid-connect
protocolMapper: oidc-group-membership-mapper
config:
"full.path": "false"
"id.token.claim": "true"
"access.token.claim": "true"
"userinfo.token.claim": "true"
"claim.name": groups
# --- MFA for platform-admins (R-IDP-04): a browser flow whose OTP step is CONDITIONAL on the
# platform-admin role. Non-admins log in without OTP; platform-admins must enroll/present TOTP.
# Set as the realm browserFlow below. VERIFY at bring-up (see runbook): a malformed flow fails
# the whole import — if so, the fallback is to comment this block + browserFlow and configure
# the conditional-OTP-by-role flow via the admin console. ---
authenticationFlows:
- alias: browser-mfa
description: "Browser flow with conditional OTP required for platform-admins."
providerId: basic-flow
topLevel: true
builtIn: false
authenticationExecutions:
- authenticator: auth-cookie
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 10
userSetupAllowed: false
- authenticator: identity-provider-redirector
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 20
userSetupAllowed: false
- authenticatorFlow: true
requirement: ALTERNATIVE
priority: 30
flowAlias: browser-mfa-forms
userSetupAllowed: false
- alias: browser-mfa-forms
description: "Username/password then conditional OTP."
providerId: basic-flow
topLevel: false
builtIn: false
authenticationExecutions:
- authenticator: auth-username-password-form
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: CONDITIONAL
priority: 20
flowAlias: browser-mfa-conditional-otp
userSetupAllowed: false
- alias: browser-mfa-conditional-otp
description: "Require OTP only for users with the platform-admin role."
providerId: basic-flow
topLevel: false
builtIn: false
authenticationExecutions:
- authenticator: conditional-user-role
authenticatorFlow: false
requirement: REQUIRED
priority: 10
authenticatorConfig: conditional-otp-platform-admin
userSetupAllowed: false
- authenticator: auth-otp-form
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
authenticatorConfig:
- alias: conditional-otp-platform-admin
config:
"condition.evaluated.user.role": platform-admin
negate: "false"
browserFlow: browser-mfa