WIP keycloak stack (gate probe)
This commit is contained in:
28 files changed
+776
-102
No files matched your search
@@ -1,40 +0,0 @@
|
||||
---
|
||||
# Keycloak — platform IdP (OIDC + SAML), config-as-code, PostgreSQL on Longhorn (ADR-0014).
|
||||
# Phase 0 STUB. DR-CRITICAL: DB backed up off-cluster; documented break-glass local-admin path.
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: keycloak
|
||||
spec:
|
||||
interval: 30m
|
||||
chart:
|
||||
spec:
|
||||
chart: keycloak
|
||||
version: ">=24.0.0 <25.0.0" # TODO(WS-SEC): pin at bring-up; Renovate maintains
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: bitnami
|
||||
namespace: keycloak
|
||||
install:
|
||||
remediation:
|
||||
retries: 3
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 3
|
||||
values:
|
||||
replicaCount: ${KEYCLOAK_REPLICAS} # overlay knob: dev 1 / prod 2 (HA) — R-ENV-02
|
||||
production: true # TLS/proxy-aware; served behind the Gateway (public)
|
||||
proxyHeaders: xforwarded
|
||||
# PostgreSQL on Longhorn (ADR-0014) — DR-critical, backed up off-cluster (velero/).
|
||||
postgresql:
|
||||
enabled: true
|
||||
primary:
|
||||
persistence:
|
||||
storageClass: longhorn
|
||||
# Admin + DB credentials: SOPS-encrypted secrets referenced BY NAME only (WS-SEC, ADR-0007).
|
||||
auth:
|
||||
existingSecret: keycloak-admin-credentials
|
||||
# TODO(WS-SEC, Phase 6): realms/clients/groups as code (keycloak-config-cli / realm-import);
|
||||
# MFA for privileged groups; break-glass local-admin path (e.g. on Gitea). Onboarded EARLY
|
||||
# (with PiHole/Gitea) so later services adopt SSO as they come up (roadmap Phase 6).
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
# TODO(WS-SEC): the ADR permits Keycloak Operator OR Helm for the instance. This stub uses a Helm
|
||||
# chart source; swap to the Keycloak Operator if WS-SEC prefers CR-driven config. (ADR-0014)
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: bitnami
|
||||
namespace: keycloak
|
||||
spec:
|
||||
interval: 1h
|
||||
url: https://charts.bitnami.com/bitnami
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
# North-south ingress for Keycloak: attaches to the shared Gateway (docs/09) at
|
||||
# keycloak.home.keithjackson.dev. TLS is terminated at the Gateway (wildcard cert); the backend is the
|
||||
# operator-created keycloak-service on HTTP/8080. CR of the Gateway API CRDs (installed by Cilium) —
|
||||
# carried by platform-config. Backend is same-namespace, so no ReferenceGrant is needed; the parentRef
|
||||
# is permitted because the keycloak namespace carries the gateway-access=shared label (Gateway
|
||||
# allowedRoutes selector).
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: keycloak
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: shared
|
||||
namespace: gateway
|
||||
sectionName: https
|
||||
hostnames:
|
||||
- keycloak.home.keithjackson.dev
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: keycloak-service
|
||||
port: 8080
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
# Keycloak instance (CR of the Operator CRD installed by platform/keycloak). CARRIED BY
|
||||
# platform-config (dependsOn platform) — F-MON1 controllers/configs split.
|
||||
#
|
||||
# Served behind the shared Gateway at keycloak.home.keithjackson.dev (public, hardened): TLS is
|
||||
# terminated by the Gateway wildcard cert, so Keycloak runs HTTP internally + trusts X-Forwarded-*
|
||||
# (production/proxy-aware, docs/15). Admin (break-glass) credential comes from the SOPS-encrypted
|
||||
# keycloak-admin-credentials Secret; realm/clients/groups are reconciled by the KeycloakRealmImport.
|
||||
apiVersion: k8s.keycloak.org/v2alpha1
|
||||
kind: Keycloak
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: keycloak
|
||||
spec:
|
||||
instances: ${KEYCLOAK_REPLICAS} # overlay knob (dev 1 / prod 2 HA) — R-ENV-02
|
||||
# --- Database: CloudNativePG cluster (platform/keycloak/postgres-cluster.yaml). The read-write
|
||||
# service keycloak-db-rw + the CNPG-generated keycloak-db-app secret (keys username/password). ---
|
||||
db:
|
||||
vendor: postgres
|
||||
host: keycloak-db-rw
|
||||
port: 5432
|
||||
database: keycloak
|
||||
usernameSecret:
|
||||
name: keycloak-db-app
|
||||
key: username
|
||||
passwordSecret:
|
||||
name: keycloak-db-app
|
||||
key: password
|
||||
poolMinSize: 5
|
||||
poolMaxSize: 20
|
||||
# --- Exposure: TLS at the Gateway; Keycloak serves HTTP and trusts the proxy headers. ---
|
||||
hostname:
|
||||
hostname: https://keycloak.home.keithjackson.dev
|
||||
http:
|
||||
httpEnabled: true # TLS is terminated at the Gateway (edge), not in Keycloak
|
||||
proxy:
|
||||
headers: xforwarded # trust X-Forwarded-* from the shared Gateway
|
||||
ingress:
|
||||
enabled: false # exposed via Gateway API HTTPRoute (httproute.yaml), not the
|
||||
# operator's built-in Ingress
|
||||
# --- Hardening + observability options (docs/15). Brute-force detection is a REALM setting
|
||||
# (bruteForceProtected in the realm import); these enable health + metrics endpoints. ---
|
||||
additionalOptions:
|
||||
- name: metrics-enabled
|
||||
value: "true"
|
||||
- name: health-enabled
|
||||
value: "true"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 768Mi
|
||||
limits:
|
||||
memory: 1280Mi
|
||||
# --- Admin bootstrap credential (R-IDP-04, break-glass R-IDP-05) ---
|
||||
# The provisioned SOPS secret keycloak-admin-credentials carries keys admin-user/admin-password.
|
||||
# The operator's supported spec.bootstrapAdmin.user.secret expects keys username/password, so — to
|
||||
# consume the already-provisioned secret WITHOUT re-keying it (must not touch its encrypted data) —
|
||||
# the credential is injected via the podTemplate escape hatch as KC_BOOTSTRAP_ADMIN_* env, which
|
||||
# Keycloak reads to create the temporary bootstrap/break-glass admin on first start.
|
||||
unsupported:
|
||||
podTemplate:
|
||||
spec:
|
||||
containers:
|
||||
- name: keycloak
|
||||
env:
|
||||
- name: KC_BOOTSTRAP_ADMIN_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: keycloak-admin-credentials
|
||||
key: admin-user
|
||||
- name: KC_BOOTSTRAP_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: keycloak-admin-credentials
|
||||
key: admin-password
|
||||
@@ -0,0 +1,37 @@
|
||||
# =============================================================================
|
||||
# TEMPLATE — NOT APPLIED, NOT ENCRYPTED (.yaml.example is ignored by kustomize and never
|
||||
# auto-encrypted by SOPS). Reference scaffold + OPERATOR HAND-OFF.
|
||||
#
|
||||
# WHAT THIS IS: the OIDC client secrets for the two confidential clients the realm import
|
||||
# (keycloak-realm-import.yaml) provisions — grafana and oauth2-proxy. The realm import injects
|
||||
# these values via spec.placeholders ($(env:GRAFANA_CLIENT_SECRET) / $(env:OAUTH2_PROXY_CLIENT_SECRET)),
|
||||
# so the client secrets live ONLY here (SOPS-encrypted), never in the realm YAML (R-SEC-01 / R-IDP-04).
|
||||
#
|
||||
# HAND-OFF STEPS (operator):
|
||||
# (a) Generate two client secrets:
|
||||
# openssl rand -hex 32 # -> grafana-client-secret
|
||||
# openssl rand -hex 32 # -> oauth2-proxy-client-secret
|
||||
# (b) Copy to: platform/keycloak/keycloak-realm-clients.sops.yaml
|
||||
# (c) Fill the two values below; encrypt:
|
||||
# sops -e -i platform/keycloak/keycloak-realm-clients.sops.yaml
|
||||
# (d) Add under `resources:` in platform/keycloak/kustomization.yaml:
|
||||
# - keycloak-realm-clients.sops.yaml
|
||||
# (e) THE SAME two values must also go into the CONSUMING apps' SOPS secrets so both sides match:
|
||||
# - oauth2-proxy-client-secret -> platform/oauth2-proxy/oauth2-proxy-credentials.sops.yaml
|
||||
# (key: client-secret) [oauth2-proxy namespace]
|
||||
# - grafana-client-secret -> platform/monitoring/grafana-oidc-credentials.sops.yaml
|
||||
# (key: client-secret) [monitoring namespace]
|
||||
# (Cross-namespace Secrets cannot be shared, hence one value stored in two encrypted files.)
|
||||
#
|
||||
# Until this file is filled + added to the kustomization, the KeycloakRealmImport stays PENDING
|
||||
# (placeholder unresolved) — the documented bring-up gate for the SSO clients.
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: keycloak-realm-clients
|
||||
namespace: keycloak
|
||||
type: Opaque
|
||||
stringData:
|
||||
grafana-client-secret: REPLACE-ME
|
||||
oauth2-proxy-client-secret: REPLACE-ME
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
# 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-SEC-01 / R-IDP-04).
|
||||
# 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
|
||||
@@ -1,7 +1,28 @@
|
||||
---
|
||||
# Keycloak CONTROLLERS half (F-MON1 split): the official Keycloak Operator (quay.io images) + its
|
||||
# CRDs + the SOPS-encrypted admin credential. The Keycloak/KeycloakRealmImport CUSTOM RESOURCES live
|
||||
# in ../../platform-config (dependsOn platform) so the CRDs registered here exist first.
|
||||
#
|
||||
# IMAGE STRATEGY (DECISIONS-LOG F-KY1): the old stub used the Bitnami Keycloak chart + bundled Bitnami
|
||||
# PostgreSQL; Bitnami's free Docker Hub images were purged -> ImagePullBackOff. Replaced with the
|
||||
# official Keycloak Operator (quay.io/keycloak/keycloak-operator + quay.io/keycloak/keycloak) and
|
||||
# CloudNativePG (ghcr.io) for the DB (platform/cnpg) — both registries unaffected by the purge, and
|
||||
# the Operator gives declarative Keycloak/realm CRs (R-IDP-03) + CNPG gives DR backup hooks (R-IDP-05).
|
||||
#
|
||||
# The operator manifests are pinned upstream RELEASE files pulled as remote bases (the same pattern the
|
||||
# kubevirt stub documents). Renovate maintains the version. Offline `kubectl kustomize` fetches them;
|
||||
# Flux fetches them at reconcile.
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# Force the operator's namespaced resources (ServiceAccount/Role/RoleBinding/Service/Deployment, which
|
||||
# ship without an explicit namespace) into `keycloak`. CRDs + ClusterRoles are cluster-scoped and pass
|
||||
# through unchanged; the ClusterRoleBinding subject already targets namespace keycloak.
|
||||
namespace: keycloak
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- helmrepository.yaml
|
||||
- helmrelease.yaml
|
||||
# --- Keycloak Operator v26.0.6 (pinned upstream release manifests) ---
|
||||
- https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.0.6/kubernetes/keycloaks.k8s.keycloak.org-v1.yml
|
||||
- https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.0.6/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml
|
||||
- https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.0.6/kubernetes/kubernetes.yml
|
||||
# --- Admin (break-glass) credential — SOPS-encrypted, referenced by name (ADR-0007, R-IDP-04) ---
|
||||
- keycloak-admin-credentials.sops.yaml
|
||||
@@ -3,3 +3,12 @@ apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: keycloak
|
||||
labels:
|
||||
# Opt this namespace's HTTPRoute in to the shared Gateway (docs/09). Keycloak is served at
|
||||
# keycloak.home.keithjackson.dev, TLS terminated by the Gateway's wildcard cert.
|
||||
gateway-access: "shared"
|
||||
# NOTE: intentionally NOT homelab.keithjackson.dev/app-namespace=true — Keycloak/CNPG are
|
||||
# PLATFORM components (like monitoring/longhorn), so Kyverno does not generate the app default-deny
|
||||
# baseline here and the auth-mode/host-namespace app policies do not apply (they scope to
|
||||
# app-namespaces). Keycloak is still an ordinary workload under Cilium's cluster-wide posture
|
||||
# (docs/15); the app->Keycloak / proxy->Keycloak / Keycloak->DB flows are the explicit allows.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
# Keycloak's PostgreSQL — a CloudNativePG Cluster (CR of the CNPG CRDs installed by platform/cnpg).
|
||||
# CARRIED BY platform-config (dependsOn platform) so the postgresql.cnpg.io CRDs exist first (F-MON1).
|
||||
#
|
||||
# DR-critical (R-IDP-05): storage on the 3-replica `longhorn` StorageClass (ADR-0003) — the data
|
||||
# survives node loss. CNPG creates the keycloak-db-app Secret (keys username/password) that the
|
||||
# Keycloak CR consumes, plus the keycloak-db-rw/-ro/-r Services. Keycloak is stateless and rebuilds
|
||||
# from this DB + the git-managed realm import (docs/15).
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: keycloak-db
|
||||
namespace: keycloak
|
||||
spec:
|
||||
# Single Postgres instance on 3-replica Longhorn is acceptable for the home scale (ADR-0014:
|
||||
# "single replica with DB on Longhorn + backups is acceptable"). Raise to 3 for Postgres-level HA
|
||||
# as a future hardening (prod) — the storage is already 3-way replicated by Longhorn.
|
||||
instances: 1
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
|
||||
storage:
|
||||
storageClass: longhorn
|
||||
size: 5Gi
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: keycloak
|
||||
owner: keycloak
|
||||
# Emit Postgres metrics so Prometheus scrapes DB health (R-OBS-01). The CNPG operator creates the
|
||||
# PodMonitor at runtime (CRD already present from Phase-4 monitoring) — no kustomize CRD-ordering hazard.
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
memory: 512Mi
|
||||
# --- Off-cluster DR backup (R-IDP-05) — OPERATOR HAND-OFF (OQ-BKP, shared with Velero, Phase 4) ---
|
||||
# When the off-cluster backup target is chosen, add a Barman object store + a ScheduledBackup:
|
||||
# spec.backup.barmanObjectStore = { destinationPath, endpointURL, s3Credentials {…SOPS…} }
|
||||
# + a postgresql.cnpg.io/v1 ScheduledBackup (cron) referencing this Cluster.
|
||||
# Until then the DB is protected by 3-replica Longhorn + (Phase-4) Velero PVC backups of the namespace.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
# ServiceMonitor for Keycloak (R-OBS-01, docs/10). CR of monitoring.coreos.com (kube-prometheus-stack,
|
||||
# Phase 4) — CARRIED BY platform-config so it is not applied in the same pass as the CRD-installing
|
||||
# HelmRelease (F-MON1). Prometheus discovers ServiceMonitors in all namespaces
|
||||
# (serviceMonitorSelectorNilUsesHelmValues=false).
|
||||
#
|
||||
# Keycloak 26 exposes health + Prometheus metrics on the MANAGEMENT interface (port 9000, path
|
||||
# /metrics) once metrics-enabled/health-enabled are set (see keycloak-instance.yaml additionalOptions).
|
||||
# We select the operator-created keycloak-service (which selects the Keycloak pods) and scrape the pods'
|
||||
# management port by number via targetPort.
|
||||
# TODO(WS-SEC, bring-up): confirm the management port number/name the operator exposes (9000 default)
|
||||
# and adjust targetPort if the pinned operator changes it. The KeycloakDown / KeycloakDatabaseDown
|
||||
# alerts (platform/monitoring/alerts.yaml) key on kube-state-metrics and fire regardless of this SM.
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: keycloak
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- keycloak
|
||||
selector:
|
||||
matchLabels:
|
||||
app: keycloak
|
||||
endpoints:
|
||||
- targetPort: 9000
|
||||
path: /metrics
|
||||
scheme: http
|
||||
interval: 30s
|
||||
@@ -1,8 +0,0 @@
|
||||
# Keycloak Helm values — PLACEHOLDER (Phase 0). Authoritative values inline in helmrelease.yaml.
|
||||
#
|
||||
# replicaCount: ${KEYCLOAK_REPLICAS} # overlay knob (dev 1 / prod 2 HA)
|
||||
# postgresql on longhorn # DR-critical (ADR-0014), backed up off-cluster
|
||||
# auth.existingSecret: keycloak-admin-credentials (SOPS-encrypted, WS-SEC — name only)
|
||||
# realm/clients/groups as code # keycloak-config-cli / realm-import (Phase 6)
|
||||
# public (hardened) exposure so login flows work for internet-exposed services (ADR-0014).
|
||||
# Exceptions (do NOT force SSO): Plex/Seerr (Plex auth), Minecraft (Mojang), UniFi (native).
|
||||
Reference in new issue
Block a user