Multi-Tenant Configuration Guide¶
Configure the operator for multi-tenant environments where multiple teams manage their own realms and clients independently.
Architecture¶
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#00b8d9','primaryTextColor':'#fff','primaryBorderColor':'#0097a7','lineColor':'#00acc1','secondaryColor':'#006064','tertiaryColor':'#fff'}}}%%
graph LR
subgraph platform["👥 Platform Team"]
deploy["Deploy Operator"]
instance["Create Keycloak Instance"]
rbac["Grant RBAC Permissions"]
end
subgraph app["👥 Application Teams"]
realms["Create Realms<br/><small>(RBAC-controlled)</small>"]
grants["Set clientAuthorizationGrants"]
clients["Manage Clients<br/><small>(grant list-controlled)</small>"]
end
deploy --> instance
instance --> rbac
rbac -.->|authorize| realms
realms --> grants
grants --> clients
style platform fill:#263238,stroke:#00acc1,stroke-width:2px,color:#fff
style app fill:#263238,stroke:#00acc1,stroke-width:2px,color:#fff
style deploy fill:#00838f,stroke:#006064,color:#fff
style instance fill:#00838f,stroke:#006064,color:#fff
style rbac fill:#00838f,stroke:#006064,color:#fff
style realms fill:#00838f,stroke:#006064,color:#fff
style grants fill:#00838f,stroke:#006064,color:#fff
style clients fill:#00838f,stroke:#006064,color:#fff
Key Concepts:
- Realm Creation: Controlled by Kubernetes RBAC (RoleBinding)
- Client Creation: Controlled by realm's
clientAuthorizationGrantslist - Declarative: All authorization via manifest fields, no secrets/tokens
- GitOps-Friendly: Everything in version control
Platform Team Setup¶
1. Deploy Shared Keycloak¶
apiVersion: vriesdemichael.github.io/v1
kind: Keycloak
metadata:
name: keycloak
namespace: platform
spec:
replicas: 3
database:
type: cnpg
cluster: keycloak-db
namespace: platform
ingress:
enabled: true
hostname: keycloak.company.com
2. Create Namespaces for Teams¶
# Create namespaces
kubectl create namespace team-alpha
kubectl create namespace team-beta
kubectl create namespace team-gamma
# Label for organization
kubectl label namespace team-alpha team=alpha env=prod
kubectl label namespace team-beta team=beta env=prod
kubectl label namespace team-gamma team=gamma env=prod
3. Grant Realm Creation Permissions¶
Create ClusterRole for realm management:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: keycloak-realm-manager
rules:
- apiGroups: ["vriesdemichael.github.io"]
resources: ["keycloakrealms"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["vriesdemichael.github.io"]
resources: ["keycloakclients"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
Grant to each team:
# Team Alpha
kubectl create rolebinding realm-manager-alpha \
--clusterrole=keycloak-realm-manager \
--serviceaccount=team-alpha:default \
--namespace=team-alpha
# Team Beta
kubectl create rolebinding realm-manager-beta \
--clusterrole=keycloak-realm-manager \
--serviceaccount=team-beta:default \
--namespace=team-beta
# Team Gamma
kubectl create rolebinding realm-manager-gamma \
--clusterrole=keycloak-realm-manager \
--serviceaccount=team-gamma:default \
--namespace=team-gamma
4. Grant Operator Namespace Access¶
The operator needs to read secrets in each team namespace:
# Create RoleBinding for each team namespace
for TEAM in team-alpha team-beta team-gamma; do
kubectl create rolebinding keycloak-operator-access \
--clusterrole=keycloak-operator-namespace-access \
--serviceaccount=platform:keycloak-operator \
--namespace=$TEAM
done
Application Team Usage¶
Create a Realm¶
Each team creates realms in their own namespace:
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: alpha-production
namespace: team-alpha
spec:
realmName: alpha-production
operatorRef:
namespace: platform
# Grant these namespaces permission to create clients
clientAuthorizationGrants:
- team-alpha # Our namespace
- team-alpha-dev # Our dev namespace
- partner-namespace # External partner (if needed)
security:
registrationAllowed: false
resetPasswordAllowed: true
verifyEmail: true
smtp:
host: smtp.company.com
from: noreply@team-alpha.com
credentialsSecret: smtp-credentials
Create Clients in Authorized Namespaces¶
Team Alpha can create clients in their namespace:
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakClient
metadata:
name: alpha-app
namespace: team-alpha
spec:
clientId: alpha-app
realmRef:
name: alpha-production
namespace: team-alpha
redirectUris:
- https://app.team-alpha.com/callback
publicClient: false
Cross-Namespace Client Creation¶
If a realm grants access, other namespaces can create clients:
# In team-alpha-dev namespace
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakClient
metadata:
name: dev-app
namespace: team-alpha-dev
spec:
clientId: dev-app
realmRef:
name: alpha-production
namespace: team-alpha # Cross-namespace reference
redirectUris:
- https://dev.team-alpha.com/callback
Multi-Realm Scenarios¶
Shared Platform Realm¶
Platform team creates a central realm that all teams can use:
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: company-sso
namespace: platform
spec:
realmName: company-sso
operatorRef:
namespace: platform
# Grant all team namespaces access
clientAuthorizationGrants:
- team-alpha
- team-beta
- team-gamma
- "*-dev" # All dev namespaces
security:
registrationAllowed: false
resetPasswordAllowed: true
All teams can now create clients in this realm.
Team-Specific Realms with Selective Sharing¶
Team creates realm and selectively grants access:
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: beta-services
namespace: team-beta
spec:
realmName: beta-services
operatorRef:
namespace: platform
# Only beta team and specific partners
clientAuthorizationGrants:
- team-beta
- team-beta-staging
- partner-integration-team # External partner
Security Model¶
Realm Creation Authorization¶
Who can create realms?
- Users/ServiceAccounts with RoleBinding granting create permission on KeycloakRealm
- Platform team controls via RBAC policies
Verification:
# Check if team can create realms
kubectl auth can-i create keycloakrealms.vriesdemichael.github.io \
--as=system:serviceaccount:team-alpha:default \
--namespace=team-alpha
Client Creation Authorization¶
Who can create clients?
- Namespaces listed in realm's clientAuthorizationGrants
- Defined by realm owner (application team)
- Fully declarative (no secret distribution)
Verification:
# Check realm's grants
kubectl get keycloakrealm alpha-production -n team-alpha \
-o jsonpath='{.spec.clientAuthorizationGrants}' | jq
Namespace Isolation¶
- Teams cannot access other teams' namespaces (Kubernetes RBAC)
- Teams cannot modify other teams' realms (resource ownership)
- Clients can only reference realms that grant their namespace access
- Operator enforces authorization at reconciliation time
GitOps Workflow¶
ArgoCD Application Structure¶
gitops-repo/
├── platform/
│ ├── keycloak-operator.yaml
│ └── shared-realms/
│ └── company-sso.yaml
├── team-alpha/
│ ├── realms/
│ │ └── alpha-production.yaml
│ └── clients/
│ ├── web-app.yaml
│ └── mobile-app.yaml
└── team-beta/
├── realms/
│ └── beta-services.yaml
└── clients/
└── api-gateway.yaml
Updating Authorization Grants¶
To grant a new namespace access:
# Update realm manifest
kubectl patch keycloakrealm alpha-production -n team-alpha --type=merge -p '
spec:
clientAuthorizationGrants:
- team-alpha
- team-alpha-dev
- new-namespace # Add new namespace
'
# Or update in Git and let ArgoCD sync
Changes apply immediately - new namespace can create clients.
Common Patterns¶
Development/Staging/Production Separation¶
# Production realm - strict grants
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: prod-realm
namespace: team-prod
spec:
clientAuthorizationGrants:
- team-prod # Only production namespace
---
# Staging realm - more permissive
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: staging-realm
namespace: team-staging
spec:
clientAuthorizationGrants:
- team-staging
- team-dev
- qa-team # QA can create test clients
Partner Integration¶
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: partner-api
namespace: platform
spec:
clientAuthorizationGrants:
- partner-a
- partner-b
- internal-gateway # Our API gateway
# Strict security for external partners
security:
registrationAllowed: false
bruteForceProtected: true
Troubleshooting¶
Permission Denied Creating Realm¶
# Check RBAC permissions
kubectl auth can-i create keycloakrealms.vriesdemichael.github.io \
--namespace=team-alpha
# Check RoleBindings
kubectl get rolebinding -n team-alpha \
-o json | jq '.items[] | select(.subjects[]?.kind=="ServiceAccount")'
Solution: Create RoleBinding granting realm creation permission.
Client Creation Fails - Not Authorized¶
# Check realm's grants
kubectl get keycloakrealm <realm> -n <realm-namespace> \
-o jsonpath='{.spec.clientAuthorizationGrants[*]}'
# Check operator logs
kubectl logs -n platform -l app=keycloak-operator \
| grep -i "authorization\|grant"
Solution: Add client's namespace to realm's clientAuthorizationGrants.
Operator Can't Read Secrets¶
# Check operator has access to namespace
kubectl auth can-i get secrets \
--as=system:serviceaccount:platform:keycloak-operator \
--namespace=team-alpha
Solution: Create RoleBinding for operator in the namespace.
Best Practices¶
✅ Use least-privilege grants - Only grant namespaces that need access
✅ Document grants - Comment why each namespace is granted
✅ Review regularly - Audit clientAuthorizationGrants periodically
✅ Separate environments - Different realms for dev/staging/prod
✅ Use GitOps - All changes via PR workflow
✅ Monitor authorization - Alert on denied client creation attempts