From e257e3895bca5296b6990c5d6263e45d9b524703 Mon Sep 17 00:00:00 2001 From: Aleksandr Bogomiakov Date: Mon, 5 Oct 2026 23:29:35 +0100 Subject: [PATCH] NG: added Keycloak --- k8s-ng/core-state/values.yaml | 5 + k8s-ng/core/cert-manager/issuer.yaml | 21 +- k8s-ng/core/keycloak/auth-proxy.yaml | 122 ++++++ .../core/keycloak/hacker-theme-configmap.yaml | 366 ++++++++++++++++++ k8s-ng/core/keycloak/kustomization.yaml | 16 + k8s-ng/core/keycloak/secrets.yaml | 80 ++++ k8s-ng/core/keycloak/values.yaml | 75 ++++ .../core/postgresql/databases/keycloak.yaml | 81 ++++ k8s-ng/core/postgresql/kustomization.yaml | 1 + 9 files changed, 764 insertions(+), 3 deletions(-) create mode 100644 k8s-ng/core/keycloak/auth-proxy.yaml create mode 100644 k8s-ng/core/keycloak/hacker-theme-configmap.yaml create mode 100644 k8s-ng/core/keycloak/kustomization.yaml create mode 100644 k8s-ng/core/keycloak/secrets.yaml create mode 100644 k8s-ng/core/keycloak/values.yaml create mode 100644 k8s-ng/core/postgresql/databases/keycloak.yaml diff --git a/k8s-ng/core-state/values.yaml b/k8s-ng/core-state/values.yaml index 5b2707a..339390d 100644 --- a/k8s-ng/core-state/values.yaml +++ b/k8s-ng/core-state/values.yaml @@ -48,3 +48,8 @@ apps: namespace: vaultwarden wave: 3 skipDryRunOnMissingResource: true + - name: keycloak + path: k8s-ng/core/keycloak + namespace: keycloak + wave: 3 + skipDryRunOnMissingResource: true # ExternalSecret/Certificate CRDs come from earlier waves diff --git a/k8s-ng/core/cert-manager/issuer.yaml b/k8s-ng/core/cert-manager/issuer.yaml index 57db047..154330b 100644 --- a/k8s-ng/core/cert-manager/issuer.yaml +++ b/k8s-ng/core/cert-manager/issuer.yaml @@ -1,7 +1,10 @@ -# Public certificates through DNS-01. The Cloudflare token Secret is created by hand -# (it will move to Vaultwarden + External Secrets): +# Public certificates through DNS-01. The credential Secrets are created by hand +# (they will move to Vaultwarden + External Secrets): # kubectl -n cert-manager create secret generic cloudflare-api-token --from-literal=apiToken= -# The token needs Zone:DNS:Edit on the btwiusearch.net zone. +# (Zone:DNS:Edit on the btwiusearch.net zone) +# kubectl -n cert-manager create secret generic aws-api-key-secret \ +# --from-literal=accessKey= --from-literal=secretKey= +# (Route53 access to the hexor.cy zone) apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: @@ -21,3 +24,15 @@ spec: apiTokenSecretRef: name: cloudflare-api-token key: apiToken + - selector: + dnsZones: + - hexor.cy + dns01: + route53: + region: eu-north-1 + accessKeyIDSecretRef: + name: aws-api-key-secret + key: accessKey + secretAccessKeySecretRef: + name: aws-api-key-secret + key: secretKey diff --git a/k8s-ng/core/keycloak/auth-proxy.yaml b/k8s-ng/core/keycloak/auth-proxy.yaml new file mode 100644 index 0000000..caa9f5d --- /dev/null +++ b/k8s-ng/core/keycloak/auth-proxy.yaml @@ -0,0 +1,122 @@ +# rsauth2-proxy: forward-auth for services that have no OIDC of their own. +# Needs, besides this file: +# - Secret auth-proxy-creds (AUTH_PROXY_CLIENT_ID, AUTH_PROXY_CLIENT_SECRET, +# AUTH_PROXY_COOKIE_SECRET); the client secret is the one of the Keycloak +# client "rsauth2-proxy". Created by hand until Vaultwarden is the source. +# - ConfigMap auth-proxy-routes, managed by Terraform (terraform/keycloak). +apiVersion: apps/v1 +kind: Deployment +metadata: + name: auth-proxy + labels: + app: auth-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: auth-proxy + template: + metadata: + labels: + app: auth-proxy + spec: + containers: + - name: auth-proxy + image: ultradesu/rsauth2-proxy:latest + ports: + - containerPort: 8080 + name: http + protocol: TCP + envFrom: + - secretRef: + name: auth-proxy-creds + env: + - name: AUTH_PROXY_OIDC_ISSUER + value: "https://auth.hexor.cy/auth/realms/hexor" + - name: AUTH_PROXY_COOKIE_DOMAIN + value: ".hexor.cy" + - name: AUTH_PROXY_CALLBACK_URL + value: "https://oauth.hexor.cy/callback" + - name: AUTH_PROXY_ROUTES_FILE + value: "/config/routes.yaml" + - name: AUTH_PROXY_LOG_LEVEL + value: "debug" + volumeMounts: + - name: routes + mountPath: /config + readOnly: true + resources: + requests: + cpu: 50m + memory: 32Mi + limits: + cpu: 200m + memory: 64Mi + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 30 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + capabilities: + drop: + - ALL + volumes: + - name: routes + configMap: + name: auth-proxy-routes +--- +apiVersion: v1 +kind: Service +metadata: + name: auth-proxy + labels: + app: auth-proxy +spec: + ports: + - name: http + port: 80 + targetPort: 8080 + protocol: TCP + selector: + app: auth-proxy +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: auth-proxy +spec: + entryPoints: + - websecure + routes: + - match: Host(`oauth.hexor.cy`) + kind: Rule + services: + - name: auth-proxy + port: 80 + tls: + secretName: auth-proxy-tls +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: auth-proxy-tls +spec: + secretName: auth-proxy-tls + issuerRef: + name: letsencrypt + kind: ClusterIssuer + dnsNames: + - oauth.hexor.cy diff --git a/k8s-ng/core/keycloak/hacker-theme-configmap.yaml b/k8s-ng/core/keycloak/hacker-theme-configmap.yaml new file mode 100644 index 0000000..223270d --- /dev/null +++ b/k8s-ng/core/keycloak/hacker-theme-configmap.yaml @@ -0,0 +1,366 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: keycloak-hacker-theme + namespace: keycloak +data: + theme.properties: | + parent=keycloak.v2 + import=common/keycloak + + styles=css/hacker.css + + hacker.css: | + /* ============================================================ + HEXOR — Hacker Terminal Theme for Keycloak + ============================================================ */ + + @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&display=swap'); + + :root { + --hk-bg: #0a0a0a; + --hk-panel: #0d0d0d; + --hk-border: #00ff4180; + --hk-green: #00ff41; + --hk-green-dim: #00cc33; + --hk-green-glow: rgba(0, 255, 65, 0.15); + --hk-green-glow-strong: rgba(0, 255, 65, 0.35); + --hk-cyan: #00e5ff; + --hk-red: #ff3333; + --hk-text: #b0ffb0; + --hk-text-dim: #5a8a5a; + --hk-input-bg: #050505; + --hk-font: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace; + } + + /* -- Force dark always --------------------------------------------------- */ + html.login-pf, + html.login-pf.pf-v5-theme-dark { + color-scheme: dark; + } + + /* -- Background: scanlines + CRT ---------------------------------------- */ + body#keycloak-bg { + font-family: var(--hk-font) !important; + background: var(--hk-bg) !important; + color: var(--hk-text) !important; + min-height: 100vh; + position: relative; + } + + body#keycloak-bg::before { + content: ''; + position: fixed; + inset: 0; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.03) 2px, + rgba(0, 255, 65, 0.03) 4px + ); + pointer-events: none; + z-index: 9999; + } + + /* -- Login container ----------------------------------------------------- */ + .pf-v5-c-login { + background: transparent !important; + } + + .pf-v5-c-login__container { + max-width: 480px !important; + } + + /* -- Header / Brand ------------------------------------------------------ */ + #kc-header-wrapper { + font-family: var(--hk-font) !important; + color: var(--hk-green) !important; + text-shadow: 0 0 10px var(--hk-green), 0 0 30px var(--hk-green-glow); + font-size: 1.5rem !important; + letter-spacing: 0.15em; + text-transform: uppercase; + } + + #kc-header-wrapper .kc-logo-text span::before { + content: '> '; + opacity: 0.6; + } + + /* -- Main card ----------------------------------------------------------- */ + .pf-v5-c-login__main { + background: var(--hk-panel) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + box-shadow: + 0 0 15px var(--hk-green-glow), + inset 0 0 30px rgba(0, 0, 0, 0.5) !important; + } + + /* -- Title --------------------------------------------------------------- */ + h1#kc-page-title, + .pf-v5-c-title { + font-family: var(--hk-font) !important; + color: var(--hk-green) !important; + text-shadow: 0 0 8px var(--hk-green-glow); + font-weight: 600 !important; + font-size: 1.25rem !important; + letter-spacing: 0.05em; + } + + /* -- Labels -------------------------------------------------------------- */ + .pf-v5-c-form__label-text { + font-family: var(--hk-font) !important; + color: var(--hk-green-dim) !important; + font-size: 0.85rem !important; + text-transform: uppercase; + letter-spacing: 0.08em; + } + + /* -- Input fields -------------------------------------------------------- */ + .pf-v5-c-form-control { + background-color: var(--hk-input-bg) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + font-size: 0.9rem !important; + caret-color: var(--hk-green); + transition: border-color 0.2s, box-shadow 0.2s; + } + + .pf-v5-c-form-control:focus-within, + .pf-v5-c-form-control:focus { + border-color: var(--hk-green) !important; + box-shadow: 0 0 8px var(--hk-green-glow), inset 0 0 4px var(--hk-green-glow) !important; + outline: none !important; + } + + .pf-v5-c-form-control input { + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + } + + .pf-v5-c-form-control input::placeholder { + color: var(--hk-text-dim) !important; + } + + /* -- Primary button (Sign In) -------------------------------------------- */ + .pf-v5-c-button.pf-m-primary { + background: transparent !important; + border: 1px solid var(--hk-green) !important; + border-radius: 0 !important; + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + font-weight: 600 !important; + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.9rem !important; + transition: all 0.2s; + position: relative; + overflow: hidden; + } + + .pf-v5-c-button.pf-m-primary:hover { + background: var(--hk-green-glow) !important; + box-shadow: 0 0 15px var(--hk-green-glow-strong), inset 0 0 15px var(--hk-green-glow) !important; + color: #fff !important; + text-shadow: 0 0 5px var(--hk-green); + } + + /* -- Secondary button (Try Another Way, Passkey) ------------------------- */ + .pf-v5-c-button.pf-m-secondary { + background: transparent !important; + border: 1px solid var(--hk-cyan) !important; + border-radius: 0 !important; + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem !important; + transition: all 0.2s; + } + + .pf-v5-c-button.pf-m-secondary:hover { + background: rgba(0, 229, 255, 0.1) !important; + box-shadow: 0 0 12px rgba(0, 229, 255, 0.3) !important; + color: #fff !important; + } + + /* -- Passkey authenticate button ----------------------------------------- */ + #authenticateWebAuthnButton { + background: transparent !important; + border: 1px solid var(--hk-cyan) !important; + border-radius: 0 !important; + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem !important; + padding: 0.75rem 1rem; + display: block; + width: 100%; + text-align: center; + text-decoration: none; + transition: all 0.2s; + margin-top: 1rem; + } + + #authenticateWebAuthnButton::before { + content: '\f084 '; + font-family: 'Font Awesome 6 Free', 'FontAwesome'; + font-weight: 900; + } + + #authenticateWebAuthnButton:hover { + background: rgba(0, 229, 255, 0.1) !important; + box-shadow: 0 0 15px rgba(0, 229, 255, 0.3), inset 0 0 10px rgba(0, 229, 255, 0.1) !important; + color: #fff !important; + text-shadow: 0 0 5px var(--hk-cyan); + } + + /* -- Show password button ------------------------------------------------ */ + .pf-v5-c-button.pf-m-control { + background: var(--hk-input-bg) !important; + border: 1px solid var(--hk-border) !important; + border-left: none !important; + border-radius: 0 !important; + color: var(--hk-text-dim) !important; + } + + .pf-v5-c-button.pf-m-control:hover { + color: var(--hk-green) !important; + } + + /* -- Social providers (Google, etc) -------------------------------------- */ + .pf-v5-c-login__main-footer { + border-top: 1px solid var(--hk-border) !important; + } + + .kc-social-section .pf-v5-c-button.pf-m-secondary { + border-color: var(--hk-text-dim) !important; + color: var(--hk-text) !important; + } + + .kc-social-section .pf-v5-c-button.pf-m-secondary:hover { + border-color: var(--hk-green) !important; + color: var(--hk-green) !important; + box-shadow: 0 0 10px var(--hk-green-glow) !important; + } + + /* -- Try another way link ------------------------------------------------ */ + #try-another-way { + color: var(--hk-cyan) !important; + text-decoration: none !important; + font-family: var(--hk-font) !important; + } + + #try-another-way:hover { + text-shadow: 0 0 8px rgba(0, 229, 255, 0.5); + } + + /* -- Links --------------------------------------------------------------- */ + a { + color: var(--hk-cyan) !important; + text-decoration: none; + transition: text-shadow 0.2s; + } + + a:hover { + text-shadow: 0 0 6px rgba(0, 229, 255, 0.4); + } + + /* -- Alerts / messages --------------------------------------------------- */ + .pf-v5-c-alert { + background: rgba(0, 255, 65, 0.05) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + font-family: var(--hk-font) !important; + } + + .pf-v5-c-alert.pf-m-danger { + border-color: var(--hk-red) !important; + background: rgba(255, 51, 51, 0.05) !important; + } + + .pf-v5-c-alert__title { + color: var(--hk-text) !important; + font-family: var(--hk-font) !important; + } + + /* -- Checkbox (Remember me) ---------------------------------------------- */ + .pf-v5-c-check__label { + color: var(--hk-text-dim) !important; + font-family: var(--hk-font) !important; + font-size: 0.8rem !important; + } + + /* -- Helper text (Forgot password, etc.) --------------------------------- */ + .pf-v5-c-helper-text .pf-v5-c-button.pf-m-link { + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + font-size: 0.8rem !important; + } + + /* -- Select auth list (Try another way options) -------------------------- */ + .select-auth-container { + background: var(--hk-panel) !important; + font-family: var(--hk-font) !important; + } + + .pf-v5-c-data-list__item { + border-color: var(--hk-border) !important; + background: transparent !important; + } + + .pf-v5-c-data-list__item:hover { + background: var(--hk-green-glow) !important; + } + + .select-auth-box-headline { + color: var(--hk-green) !important; + } + + .select-auth-box-desc { + color: var(--hk-text-dim) !important; + } + + /* -- Footer -------------------------------------------------------------- */ + .pf-v5-c-login__main-footer-band { + background: transparent !important; + border-top: 1px solid var(--hk-border) !important; + } + + /* -- Language selector --------------------------------------------------- */ + select#login-select-toggle { + background: var(--hk-input-bg) !important; + color: var(--hk-green) !important; + border-color: var(--hk-border) !important; + font-family: var(--hk-font) !important; + } + + /* -- Blinking cursor animation for header -------------------------------- */ + @keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } + } + + #kc-header-wrapper::after { + content: '_'; + animation: blink 1s step-end infinite; + color: var(--hk-green); + } + + /* -- Subtle CRT flicker ------------------------------------------------- */ + @keyframes flicker { + 0% { opacity: 0.97; } + 5% { opacity: 0.95; } + 10% { opacity: 0.98; } + 15% { opacity: 0.96; } + 20% { opacity: 0.99; } + 100% { opacity: 0.98; } + } + + .pf-v5-c-login__main { + animation: flicker 4s infinite; + } diff --git a/k8s-ng/core/keycloak/kustomization.yaml b/k8s-ng/core/keycloak/kustomization.yaml new file mode 100644 index 0000000..b50b6d9 --- /dev/null +++ b/k8s-ng/core/keycloak/kustomization.yaml @@ -0,0 +1,16 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - secrets.yaml + - hacker-theme-configmap.yaml + - auth-proxy.yaml +# ConfigMap auth-proxy-routes is managed by Terraform (terraform/keycloak, kubernetes_config_map_v1). + +helmCharts: + - name: keycloakx + repo: https://codecentric.github.io/helm-charts + version: 7.3.1 + releaseName: keycloak + namespace: keycloak + valuesFile: values.yaml diff --git a/k8s-ng/core/keycloak/secrets.yaml b/k8s-ng/core/keycloak/secrets.yaml new file mode 100644 index 0000000..9330815 --- /dev/null +++ b/k8s-ng/core/keycloak/secrets.yaml @@ -0,0 +1,80 @@ +# Read-only access to the keycloak-db Secret in the psql namespace +# (see postgresql/databases/keycloak.yaml for the other half). +apiVersion: v1 +kind: ServiceAccount +metadata: + name: db-credentials-reader +--- +apiVersion: external-secrets.io/v1 +kind: SecretStore +metadata: + name: psql-credentials +spec: + provider: + kubernetes: + remoteNamespace: psql + server: + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + auth: + serviceAccount: + name: db-credentials-reader +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycloak-creds +spec: + refreshInterval: 1h + secretStoreRef: + kind: SecretStore + name: psql-credentials + target: + name: keycloak-creds + template: + data: + KC_DB_USERNAME: "{{ .username }}" + KC_DB_PASSWORD: "{{ .password }}" + data: + - secretKey: username + remoteRef: + key: keycloak-db + property: username + - secretKey: password + remoteRef: + key: keycloak-db + property: password +--- +# Bootstrap admin: only used when the database has no admin user yet (a fresh +# install). With the restored database the existing admin accounts are kept. +apiVersion: generators.external-secrets.io/v1alpha1 +kind: Password +metadata: + name: keycloak-bootstrap +spec: + length: 40 + digits: 10 + symbols: 0 + noUpper: false + allowRepeat: true +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycloak-bootstrap +spec: + refreshPolicy: CreatedOnce + target: + name: keycloak-bootstrap + template: + data: + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: "{{ .password }}" + dataFrom: + - sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: Password + name: keycloak-bootstrap diff --git a/k8s-ng/core/keycloak/values.yaml b/k8s-ng/core/keycloak/values.yaml new file mode 100644 index 0000000..795760d --- /dev/null +++ b/k8s-ng/core/keycloak/values.yaml @@ -0,0 +1,75 @@ +replicas: 1 + +image: + repository: quay.io/keycloak/keycloak + tag: "26.5.6" + +command: + - "/opt/keycloak/bin/kc.sh" + - "start" + - "--http-port=8080" + - "--hostname-strict=false" + - "--proxy-headers=xforwarded" + +extraEnvFrom: | + - secretRef: + name: keycloak-creds + - secretRef: + name: keycloak-bootstrap + +extraEnv: | + - name: KC_HOSTNAME + value: auth.hexor.cy + - name: JAVA_OPTS_APPEND + value: "-Djgroups.dns.query=keycloak-keycloakx-headless.keycloak.svc" + +extraVolumes: | + - name: hacker-theme + configMap: + name: keycloak-hacker-theme + +extraVolumeMounts: | + - name: hacker-theme + mountPath: /opt/keycloak/themes/hacker/login/theme.properties + subPath: theme.properties + - name: hacker-theme + mountPath: /opt/keycloak/themes/hacker/login/css/hacker.css + subPath: hacker.css + +dbchecker: + enabled: true + +database: + vendor: postgres + hostname: main-rw.psql.svc + port: 5432 + database: keycloak + existingSecret: keycloak-creds + existingSecretKey: KC_DB_PASSWORD + +service: + type: ClusterIP + +# HTTP is redirected to HTTPS by the cluster-wide Traefik default (core/traefik-config). +ingress: + enabled: true + ingressClassName: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt + rules: + - host: auth.hexor.cy + paths: + - path: / + pathType: Prefix + tls: + - secretName: keycloak-tls + hosts: + - auth.hexor.cy + +resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: "3" + memory: 2Gi diff --git a/k8s-ng/core/postgresql/databases/keycloak.yaml b/k8s-ng/core/postgresql/databases/keycloak.yaml new file mode 100644 index 0000000..cc46799 --- /dev/null +++ b/k8s-ng/core/postgresql/databases/keycloak.yaml @@ -0,0 +1,81 @@ +# Database "keycloak": password, role, database and read access for the app. +# To add another database copy this file and replace the name. + +# Generated once; the role and the application both read this Secret. +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycloak-db + namespace: psql +spec: + refreshPolicy: CreatedOnce + target: + name: keycloak-db + template: + type: kubernetes.io/basic-auth + metadata: + labels: + cnpg.io/reload: "true" + data: + username: keycloak + password: "{{ .password }}" + dataFrom: + - sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: Password + name: db-password +--- +apiVersion: postgresql.cnpg.io/v1 +kind: DatabaseRole +metadata: + name: main-keycloak + namespace: psql +spec: + cluster: + name: main + name: keycloak + login: true + passwordSecret: + name: keycloak-db +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: main-keycloak + namespace: psql +spec: + cluster: + name: main + name: keycloak + owner: keycloak + databaseReclaimPolicy: retain +--- +# Lets the keycloak namespace read only its own credentials Secret. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: keycloak-db-reader + namespace: psql +rules: + - apiGroups: [""] + resources: [secrets] + resourceNames: [keycloak-db] + verbs: [get, list, watch] + - apiGroups: [authorization.k8s.io] + resources: [selfsubjectrulesreviews] + verbs: [create] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: keycloak-db-reader + namespace: psql +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: keycloak-db-reader +subjects: + - kind: ServiceAccount + name: db-credentials-reader + namespace: keycloak diff --git a/k8s-ng/core/postgresql/kustomization.yaml b/k8s-ng/core/postgresql/kustomization.yaml index 01d6550..da87b3c 100644 --- a/k8s-ng/core/postgresql/kustomization.yaml +++ b/k8s-ng/core/postgresql/kustomization.yaml @@ -5,3 +5,4 @@ resources: - cluster.yaml - password-generator.yaml - databases/vaultwarden.yaml + - databases/keycloak.yaml