Namespaced RBAC Implementation¶
This document describes the namespace-scoped RBAC implementation for the Keycloak operator, which provides explicit opt-in access control for secrets and resources.
Overview¶
The operator now implements a least-privilege RBAC model where:
- Minimal cluster-wide permissions: The operator has minimal read-only ClusterRole permissions
- Namespace-scoped management: Full resource management only in the operator's own namespace
- Explicit opt-in: Teams must create a RoleBinding to grant operator access to their namespace
- Secret label requirement: Secrets must be explicitly labeled for operator access
Architecture¶
┌──────────────────────────────────────────────────────────────┐
│ Operator Namespace (e.g., keycloak-system) │
│ │
│ ├── Operator Deployment │
│ │ └── Watches: All CRDs cluster-wide (list/watch only) │
│ │ │
│ ├── Keycloak Instance (StatefulSet/Deployment) │
│ │ └── Runs in same namespace as operator │
│ │ │
│ └── Full RBAC (Role) - Can manage all resources here │
└──────────────────────────────────────────────────────────────┘
↓ watches CRDs
┌──────────────────────────────────────────────────────────────┐
│ Team Namespace (e.g., team-a) │
│ │
│ ├── KeycloakRealm / KeycloakClient CRDs │
│ ├── Secrets (with required label) │
│ └── RoleBinding (opt-in for operator access) │
└──────────────────────────────────────────────────────────────┘
RBAC Components¶
1. ClusterRole: keycloak-operator-core¶
Purpose: Minimal cluster-wide permissions for CRD watching and status updates.
Key Permissions: - List/watch CRDs across all namespaces (read-only) - Update CRD status and finalizers - Namespace discovery for validation - Leader election (cluster-wide leases) - Events creation - SubjectAccessReview for permission checks
Does NOT include: - Full CRUD on CRDs (only list/watch) - Secret access - ConfigMap access - Workload management (Deployments, StatefulSets, etc.)
2. Role: keycloak-operator-manager¶
Purpose: Full resource management in the operator's own namespace.
Scope: Operator namespace only
Key Permissions: - Full CRUD on CRDs in operator namespace - Full CRUD on Kubernetes resources (Deployments, StatefulSets, Services, etc.) - Full CRUD on Secrets and ConfigMaps in operator namespace - Database management (CNPG clusters) - Ingress and certificate management
3. ClusterRole: keycloak-operator-namespace-access (Template)¶
Purpose: Template role for teams to grant operator access to their namespace.
Key Permissions: - Read CRDs (KeycloakRealm, KeycloakClient) - Read Secrets (with label validation enforced in code) - Create Events for status reporting
Usage: Teams create a RoleBinding in their namespace referencing this ClusterRole.
Secret Label Requirement¶
All secrets used by the operator MUST have the following label:
Why This Matters¶
This provides an explicit opt-in mechanism where: 1. Users must intentionally label secrets before the operator can read them 2. Prevents accidental exposure of unrelated secrets 3. Makes access control auditable and transparent 4. Follows the principle of least surprise
Example¶
# Create a secret
kubectl create secret generic smtp-password \
--from-literal=password='mypassword' \
-n my-team
# Label it for operator access
kubectl label secret smtp-password \
vriesdemichael.github.io/allow-operator-read=true \
-n my-team
Operator Deployment (Helm Chart)¶
Install Operator¶
helm install keycloak-operator charts/keycloak-operator \
--namespace keycloak-system \
--create-namespace \
--set keycloak.enabled=true \
--set keycloak.database.cnpg.enabled=true
What Gets Created¶
- Namespace:
keycloak-system(ifnamespace.create=true) - ServiceAccount:
keycloak-operator - ClusterRole:
keycloak-operator-core(minimal permissions) - ClusterRole:
keycloak-operator-namespace-access(template for teams) - Role:
keycloak-operator-manager(in operator namespace) - ClusterRoleBinding: Links ServiceAccount to core ClusterRole
- RoleBinding: Links ServiceAccount to manager Role
- Operator Deployment: Watches CRDs cluster-wide
- Keycloak Instance: Runs in operator namespace
Realm Deployment (Helm Chart)¶
Deploy Realm to Team Namespace¶
# Create namespace
kubectl create namespace my-team
# Create and label secrets
kubectl create secret generic smtp-password \
--from-literal=password='mypassword' \
-n my-team
kubectl label secret smtp-password \
vriesdemichael.github.io/allow-operator-read=true \
-n my-team
# Deploy realm (creates RoleBinding automatically)
helm install my-realm charts/keycloak-realm \
--namespace my-team \
--set realmName=my-team \
--set operatorRef.namespace=keycloak-system \
--set rbac.create=true \
--set smtpServer.enabled=true \
--set smtpServer.host=smtp.example.com \
--set smtpServer.from=noreply@my-team.com \
--set smtpServer.passwordSecret.name=smtp-password
What Gets Created¶
- KeycloakRealm CR: Defines the realm configuration
- RoleBinding:
my-realm-operator-access(grants operator access) - The operator reconciles the realm in the Keycloak instance
Verify Access¶
# Check RoleBinding
kubectl get rolebinding my-realm-operator-access -n my-team
# Check realm status
kubectl get keycloakrealm my-realm -n my-team
# View realm details
kubectl describe keycloakrealm my-realm -n my-team
Client Deployment (Helm Chart)¶
Deploy Client to Team Namespace¶
# Deploy client (creates RoleBinding automatically)
helm install my-client charts/keycloak-client \
--namespace my-team \
--set clientId=my-client \
--set realmRef.name=my-realm \
--set realmRef.namespace=my-team \
--set rbac.create=true \
--set rbac.operatorNamespace=keycloak-system
What Gets Created¶
- KeycloakClient CR: Defines the client configuration
- RoleBinding:
my-client-operator-access(grants operator access) - The operator reconciles the client in the specified realm
Manual RoleBinding Creation¶
If you prefer to create RoleBindings manually (or rbac.create=false):
kubectl create rolebinding keycloak-operator-access \
--clusterrole=keycloak-operator-namespace-access \
--serviceaccount=keycloak-system:keycloak-operator \
-n my-team
Error Handling¶
Missing RoleBinding¶
Error: Operator does not have access to namespace 'my-team'
Solution: Create the RoleBinding:
kubectl create rolebinding keycloak-operator-access \
--clusterrole=keycloak-operator-namespace-access \
--serviceaccount=keycloak-system:keycloak-operator \
-n my-team
Missing Secret Label¶
Error: Secret 'smtp-password' in namespace 'my-team' is missing required label 'vriesdemichael.github.io/allow-operator-read=true'
Solution: Label the secret:
Secret Not Found¶
Error: Secret 'smtp-password' not found in namespace 'my-team'
Solution: Create the secret:
kubectl create secret generic smtp-password \
--from-literal=password='mypassword' \
-n my-team
kubectl label secret smtp-password \
vriesdemichael.github.io/allow-operator-read=true \
-n my-team
Revoking Access¶
To revoke operator access to a namespace:
Note: Existing resources will continue to work, but the operator will not be able to reconcile changes.
Auditing¶
Find Namespaces with Operator Access¶
kubectl get rolebindings -A \
-o json | jq -r '.items[] | select(.subjects[]?.name == "keycloak-operator") | "\(.metadata.namespace)/\(.metadata.name)"'
Find Labeled Secrets¶
kubectl get secrets -A \
-l vriesdemichael.github.io/allow-operator-read=true \
-o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name
Check Operator Permissions¶
# Check if operator can read secrets in a namespace
kubectl auth can-i get secrets \
--as=system:serviceaccount:keycloak-system:keycloak-operator \
-n my-team
Testing¶
Run the integration tests:
This validates: - Operator deployment with minimal RBAC - Realm creation in different namespace - Client creation in different namespace - Secret label validation - RoleBinding creation - Reconciliation with proper permissions
Troubleshooting¶
Operator Logs¶
Check RBAC Resources¶
# Check ClusterRoles
kubectl get clusterrole | grep keycloak-operator
# Check Role in operator namespace
kubectl get role -n keycloak-system
# Check RoleBindings in a namespace
kubectl get rolebinding -n my-team
Verify Secret Label¶
References¶
- Operator Chart:
charts/keycloak-operator/ - Realm Chart:
charts/keycloak-realm/ - Client Chart:
charts/keycloak-client/ - Integration Tests:
tests/integration/