feat(keycloak): install keycloak with official operator
This commit is contained in:
2
keycloak/.gitignore
vendored
2
keycloak/.gitignore
vendored
@@ -1 +1 @@
|
|||||||
keycloak-values.yaml
|
keycloak-cr.yaml
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
set fallback := true
|
set fallback := true
|
||||||
|
|
||||||
# Keycloak Helm chart info:
|
# Keycloak Operator info:
|
||||||
# helm show chart oci://registry-1.docker.io/bitnamicharts/keycloak
|
# https://www.keycloak.org/operator/installation
|
||||||
|
|
||||||
export KEYCLOAK_NAMESPACE := env("KEYCLOAK_NAMESPACE", "keycloak")
|
export KEYCLOAK_NAMESPACE := env("KEYCLOAK_NAMESPACE", "keycloak")
|
||||||
export KEYCLOAK_CHART_VERSION := env("KEYCLOAK_CHART_VERSION", "25.0.2")
|
export KEYCLOAK_OPERATOR_VERSION := env("KEYCLOAK_OPERATOR_VERSION", "26.3.4")
|
||||||
export KEYCLOAK_REALM := env("KEYCLOAK_REALM", "")
|
export KEYCLOAK_REALM := env("KEYCLOAK_REALM", "")
|
||||||
export KEYCLOAK_HOST := env("KEYCLOAK_HOST", "")
|
export KEYCLOAK_HOST := env("KEYCLOAK_HOST", "")
|
||||||
export K8S_OIDC_CLIENT_ID := env('K8S_OIDC_CLIENT_ID', "k8s")
|
export K8S_OIDC_CLIENT_ID := env('K8S_OIDC_CLIENT_ID', "k8s")
|
||||||
@@ -52,23 +52,37 @@ create-credentials:
|
|||||||
echo "Waiting for ExternalSecret to sync..."
|
echo "Waiting for ExternalSecret to sync..."
|
||||||
kubectl wait --for=condition=Ready externalsecret/keycloak-credentials \
|
kubectl wait --for=condition=Ready externalsecret/keycloak-credentials \
|
||||||
-n ${KEYCLOAK_NAMESPACE} --timeout=60s
|
-n ${KEYCLOAK_NAMESPACE} --timeout=60s
|
||||||
|
|
||||||
|
# Create bootstrap admin secret for Keycloak Operator (username/password format)
|
||||||
|
kubectl delete secret keycloak-bootstrap-admin -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
|
kubectl create secret generic keycloak-bootstrap-admin -n ${KEYCLOAK_NAMESPACE} \
|
||||||
|
--from-literal=username="${admin_user}" \
|
||||||
|
--from-literal=password="${password}"
|
||||||
else
|
else
|
||||||
echo "External Secrets Operator not found. Creating secret directly..."
|
echo "External Secrets Operator not found. Creating secrets directly..."
|
||||||
if kubectl get secret keycloak-credentials -n ${KEYCLOAK_NAMESPACE} &>/dev/null; then
|
# Delete existing secrets
|
||||||
kubectl delete --ignore-not-found secret keycloak-credentials -n ${KEYCLOAK_NAMESPACE}
|
kubectl delete secret keycloak-credentials -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
fi
|
kubectl delete secret keycloak-bootstrap-admin -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
|
|
||||||
|
# Create keycloak-credentials secret (admin-user/password format for legacy scripts)
|
||||||
kubectl create secret generic keycloak-credentials -n ${KEYCLOAK_NAMESPACE} \
|
kubectl create secret generic keycloak-credentials -n ${KEYCLOAK_NAMESPACE} \
|
||||||
--from-literal=admin-user="${admin_user}" \
|
--from-literal=admin-user="${admin_user}" \
|
||||||
--from-literal=password="${password}"
|
--from-literal=password="${password}"
|
||||||
|
|
||||||
|
# Create bootstrap admin secret for Keycloak Operator (username/password format)
|
||||||
|
kubectl create secret generic keycloak-bootstrap-admin -n ${KEYCLOAK_NAMESPACE} \
|
||||||
|
--from-literal=username="${admin_user}" \
|
||||||
|
--from-literal=password="${password}"
|
||||||
|
|
||||||
if helm status vault -n ${K8S_VAULT_NAMESPACE} &>/dev/null; then
|
if helm status vault -n ${K8S_VAULT_NAMESPACE} &>/dev/null; then
|
||||||
just put-admin-credentials-to-vault "${admin_user}" "${password}"
|
just put-admin-credentials-to-vault "${admin_user}" "${password}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Delete Keycloak secret
|
# Delete Keycloak secrets
|
||||||
delete-credentials:
|
delete-credentials:
|
||||||
@kubectl delete secret keycloak-credentials -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
@kubectl delete secret keycloak-credentials -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
|
@kubectl delete secret keycloak-bootstrap-admin -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
@kubectl delete externalsecret keycloak-credentials -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
@kubectl delete externalsecret keycloak-credentials -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
|
|
||||||
# Create Keycloak database secret
|
# Create Keycloak database secret
|
||||||
@@ -89,31 +103,51 @@ create-database-secret:
|
|||||||
delete-database-secret:
|
delete-database-secret:
|
||||||
@kubectl delete secret database-config -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
@kubectl delete secret database-config -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
|
|
||||||
# Install Keycloak
|
# Install Keycloak Operator
|
||||||
|
install-operator:
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
just create-namespace
|
||||||
|
echo "Installing Keycloak Operator CRDs..."
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/${KEYCLOAK_OPERATOR_VERSION}/kubernetes/keycloaks.k8s.keycloak.org-v1.yml
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/${KEYCLOAK_OPERATOR_VERSION}/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml
|
||||||
|
echo "Installing Keycloak Operator in ${KEYCLOAK_NAMESPACE} namespace..."
|
||||||
|
kubectl apply -n ${KEYCLOAK_NAMESPACE} -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/${KEYCLOAK_OPERATOR_VERSION}/kubernetes/kubernetes.yml
|
||||||
|
kubectl wait --for=condition=available deployment/keycloak-operator -n ${KEYCLOAK_NAMESPACE} --timeout=300s
|
||||||
|
|
||||||
|
# Install Keycloak instance
|
||||||
install:
|
install:
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# Setup vault environment once at the beginning if vault is enabled
|
just install-operator
|
||||||
just create-credentials
|
just create-credentials
|
||||||
just postgres::create-db keycloak
|
just postgres::create-db keycloak
|
||||||
just create-database-secret
|
just create-database-secret
|
||||||
|
just create-namespace
|
||||||
KEYCLOAK_ADMIN_USER=$(just admin-username) \
|
KEYCLOAK_ADMIN_USER=$(just admin-username) \
|
||||||
gomplate -f keycloak-values.gomplate.yaml -o keycloak-values.yaml
|
gomplate -f keycloak-cr.gomplate.yaml -o keycloak-cr.yaml
|
||||||
helm upgrade --cleanup-on-fail --install \
|
kubectl apply -f keycloak-cr.yaml
|
||||||
keycloak oci://registry-1.docker.io/bitnamicharts/keycloak \
|
kubectl wait --for=condition=Ready keycloak/keycloak -n ${KEYCLOAK_NAMESPACE} --timeout=600s
|
||||||
--version ${KEYCLOAK_CHART_VERSION} -n ${KEYCLOAK_NAMESPACE} --wait \
|
|
||||||
-f keycloak-values.yaml
|
|
||||||
|
|
||||||
# Uninstall Keycloak
|
# Uninstall Keycloak instance
|
||||||
uninstall delete-db='true':
|
uninstall delete-db='true':
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
helm uninstall keycloak -n ${KEYCLOAK_NAMESPACE} --ignore-not-found --wait
|
kubectl delete keycloak keycloak -n ${KEYCLOAK_NAMESPACE} --ignore-not-found
|
||||||
|
kubectl wait --for=delete keycloak/keycloak -n ${KEYCLOAK_NAMESPACE} --timeout=300s || true
|
||||||
just delete-namespace
|
just delete-namespace
|
||||||
if [ "{{ delete-db }}" = "true" ]; then
|
if [ "{{ delete-db }}" = "true" ]; then
|
||||||
just postgres::delete-db keycloak
|
just postgres::delete-db keycloak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Uninstall Keycloak Operator
|
||||||
|
uninstall-operator:
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
kubectl delete -n ${KEYCLOAK_NAMESPACE} -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/${KEYCLOAK_OPERATOR_VERSION}/kubernetes/kubernetes.yml --ignore-not-found
|
||||||
|
kubectl delete -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/${KEYCLOAK_OPERATOR_VERSION}/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml --ignore-not-found
|
||||||
|
kubectl delete -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/${KEYCLOAK_OPERATOR_VERSION}/kubernetes/keycloaks.k8s.keycloak.org-v1.yml --ignore-not-found
|
||||||
|
|
||||||
# Create Keycloak realm
|
# Create Keycloak realm
|
||||||
create-realm create-client-for-k8s='true' access_token_lifespan='3600' refresh_token_lifespan='14400' sso_session_idle_timeout='7200':
|
create-realm create-client-for-k8s='true' access_token_lifespan='3600' refresh_token_lifespan='14400' sso_session_idle_timeout='7200':
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|||||||
96
keycloak/keycloak-cr.gomplate.yaml
Normal file
96
keycloak/keycloak-cr.gomplate.yaml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
apiVersion: k8s.keycloak.org/v2alpha1
|
||||||
|
kind: Keycloak
|
||||||
|
metadata:
|
||||||
|
name: keycloak
|
||||||
|
namespace: {{ .Env.KEYCLOAK_NAMESPACE }}
|
||||||
|
spec:
|
||||||
|
instances: 1
|
||||||
|
image: quay.io/keycloak/keycloak:26.3.4
|
||||||
|
startOptimized: false
|
||||||
|
|
||||||
|
# Database configuration for external PostgreSQL
|
||||||
|
db:
|
||||||
|
vendor: postgres
|
||||||
|
host: postgres-cluster-rw.postgres
|
||||||
|
port: 5432
|
||||||
|
database: keycloak
|
||||||
|
usernameSecret:
|
||||||
|
name: database-config
|
||||||
|
key: user
|
||||||
|
passwordSecret:
|
||||||
|
name: database-config
|
||||||
|
key: password
|
||||||
|
|
||||||
|
# Hostname configuration
|
||||||
|
hostname:
|
||||||
|
hostname: {{ .Env.KEYCLOAK_HOST }}
|
||||||
|
strict: false
|
||||||
|
strictBackchannel: false
|
||||||
|
|
||||||
|
# HTTP configuration
|
||||||
|
http:
|
||||||
|
httpEnabled: true
|
||||||
|
httpPort: 8080
|
||||||
|
httpsPort: 8443
|
||||||
|
|
||||||
|
# Proxy configuration for edge proxy
|
||||||
|
proxy:
|
||||||
|
headers: xforwarded
|
||||||
|
|
||||||
|
# Additional options and admin configuration
|
||||||
|
additionalOptions:
|
||||||
|
- name: http-enabled
|
||||||
|
value: "true"
|
||||||
|
- name: hostname-strict
|
||||||
|
value: "false"
|
||||||
|
- name: hostname-strict-https
|
||||||
|
value: "false"
|
||||||
|
- name: proxy
|
||||||
|
value: edge
|
||||||
|
|
||||||
|
# Bootstrap admin configuration
|
||||||
|
bootstrapAdmin:
|
||||||
|
user:
|
||||||
|
secret: keycloak-bootstrap-admin
|
||||||
|
|
||||||
|
# Resources
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "1.5Gi"
|
||||||
|
cpu: "500m"
|
||||||
|
limits:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "1000m"
|
||||||
|
|
||||||
|
# Ingress configuration (disabled - using separate Ingress resource)
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
---
|
||||||
|
# Separate Ingress resource for custom configuration
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: keycloak-ingress
|
||||||
|
namespace: {{ .Env.KEYCLOAK_NAMESPACE }}
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: traefik
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- {{ .Env.KEYCLOAK_HOST }}
|
||||||
|
secretName: keycloak-tls
|
||||||
|
rules:
|
||||||
|
- host: {{ .Env.KEYCLOAK_HOST }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: keycloak-service
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
global:
|
|
||||||
security:
|
|
||||||
allowInsecureImages: true
|
|
||||||
|
|
||||||
production: true
|
|
||||||
|
|
||||||
# Enable HTTP for health checks in production mode
|
|
||||||
extraEnvVars:
|
|
||||||
- name: KC_HTTP_ENABLED
|
|
||||||
value: "true"
|
|
||||||
|
|
||||||
auth:
|
|
||||||
adminUser: {{ .Env.KEYCLOAK_ADMIN_USER }}
|
|
||||||
existingSecret: keycloak-credentials
|
|
||||||
passwordSecretKey: password
|
|
||||||
|
|
||||||
postgresql:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
externalDatabase:
|
|
||||||
host: postgres-cluster-rw.postgres
|
|
||||||
port: 5432
|
|
||||||
database: keycloak
|
|
||||||
existingSecret: database-config
|
|
||||||
existingSecretUserKey: user
|
|
||||||
existingSecretPasswordKey: password
|
|
||||||
|
|
||||||
tls:
|
|
||||||
enabled: true
|
|
||||||
autoGenerated:
|
|
||||||
enabled: true
|
|
||||||
engine: helm
|
|
||||||
|
|
||||||
# Keycloak pod may not start with the default memory limits
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 2Gi
|
|
||||||
requests:
|
|
||||||
memory: 1.5Gi
|
|
||||||
|
|
||||||
image:
|
|
||||||
registry: docker.io
|
|
||||||
repository: bitnamilegacy/keycloak
|
|
||||||
# tag: 26.0.5-debian-12-r0
|
|
||||||
# debug: true
|
|
||||||
|
|
||||||
# logging:
|
|
||||||
# level: DEBUG
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
ingressClassName: traefik
|
|
||||||
hostname: {{ .Env.KEYCLOAK_HOST }}
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: traefik
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
tls: true
|
|
||||||
Reference in New Issue
Block a user