1. Security Hardening Checklist
Before putting Keycloak into production, the following hardening measures need to be fully implemented:
| # | Item | Level | Status |
|---|---|---|---|
| 1 | HTTPS/TLS enforcement | Critical | ☐ |
| 2 | Change default Admin credentials | Critical | ☐ |
| 3 | Brute-force detection | High | ☐ |
| 4 | Password policies | High | ☐ |
| 5 | Session timeouts | High | ☐ |
| 6 | CSP headers | High | ☐ |
| 7 | Clickjacking protection | High | ☐ |
| 8 | CORS configuration | Medium | ☐ |
| 9 | Vault integration cho secrets | High | ☐ |
| 10 | Admin Console access restriction | High | ☐ |
| 11 | Disable unused features/endpoints | Medium | ☐ |
| 12 | Database encryption at rest | High | ☐ |
| 13 | Token security (short-lived) | High | ☐ |
| 14 | Audit logging enabled | High | ☐ |
2. Content Security Policy (CSP) Headers
CSP headers protect against XSS attacks by controlling which resources are allowed to load on the page.
2.1 Configure CSP in Keycloak
Keycloak configures CSP via Realm Settings → Security Defenses:
# Headers tab → Content-Security-Policy
frame-src 'self'; frame-ancestors 'self'; object-src 'none';
# Nâng cao: restrict thêm script-src, style-src
frame-src 'self'; frame-ancestors 'self'; object-src 'none'; \
script-src 'self' 'unsafe-inline'; \
style-src 'self' 'unsafe-inline';
2.2 Configuration via REST API
# Cập nhật Security Headers cho realm
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"browserSecurityHeaders": {
"contentSecurityPolicy": "frame-src '\''self'\''; frame-ancestors '\''self'\''; object-src '\''none'\'';",
"contentSecurityPolicyReportOnly": "",
"xContentTypeOptions": "nosniff",
"xRobotsTag": "none",
"xFrameOptions": "SAMEORIGIN",
"strictTransportSecurity": "max-age=31536000; includeSubDomains",
"xXSSProtection": "1; mode=block",
"referrerPolicy": "no-referrer"
}
}'
2.3 Important security headers
| Header | Recommended value | Purpose |
|---|---|---|
X-Frame-Options | SAMEORIGIN | Anti-clickjacking |
X-Content-Type-Options | nosniff | Anti-MIME type sniffing |
X-XSS-Protection | 1; mode=block | Enable browser XSS filter |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Force HTTPS (HSTS) |
Referrer-Policy | no-referrer | Do not send referrer header |
Content-Security-Policy | Restrict frame-src, object-src | Anti-XSS, data injection |
3. Brute-force Detection
Keycloak has built-in anti-brute-force mechanism for login attempts.
3.1 Brute-force configuration
Go to Realm Settings → Security Defenses → Brute Force Detection:
# Bật/tắt
Enabled: ON
# Lockout settings
Permanent Lockout: OFF # Có khóa vĩnh viễn không
Max Login Failures: 5 # Số lần thất bại tối đa trước khi lock
Wait Increment (seconds): 60 # Thời gian chờ tăng dần mỗi lần fail tiếp
Max Wait (seconds): 900 # Thời gian chờ tối đa (15 phút)
Quick Login Check (milliseconds): 1000 # Khoảng thời gian giữa 2 lần login nhanh
Minimum Quick Login Wait: 60 # Chờ tối thiểu khi quick login detected
Failure Reset Time (seconds): 43200 # Reset failure counter sau 12 giờ
3.2 Configuration via REST API
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bruteForceProtected": true,
"permanentLockout": false,
"maxFailureWaitSeconds": 900,
"minimumQuickLoginWaitSeconds": 60,
"waitIncrementSeconds": 60,
"quickLoginCheckMilliSeconds": 1000,
"maxDeltaTimeSeconds": 43200,
"failureFactor": 5
}'
3.3 How Brute-force Detection works
Lần fail thứ 1: Không lockout
Lần fail thứ 2: Không lockout
Lần fail thứ 3: Không lockout
Lần fail thứ 4: Không lockout
Lần fail thứ 5: Lockout 60 giây (waitIncrementSeconds)
Lần fail thứ 6: Lockout 120 giây (60 * 2)
Lần fail thứ 7: Lockout 240 giây (60 * 4)
...tiếp tục tăng...
Max lockout: 900 giây (15 phút)
Sau 12 giờ không fail: Reset counter về 0
3.4 Unlock User is locked
# Kiểm tra trạng thái brute-force cho user
curl -s "http://localhost:8080/admin/realms/my-realm/attack-detection/brute-force/users/$USER_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq .
# Response mẫu:
# {
# "numFailures": 6,
# "disabled": true,
# "lastIPFailure": "192.168.1.100",
# "lastFailure": 1710489045000
# }
# Unlock user cụ thể
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/attack-detection/brute-force/users/$USER_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# Unlock tất cả users
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/attack-detection/brute-force/users" \
-H "Authorization: Bearer $ACCESS_TOKEN"
4. Password Policies
Keycloak supports many password policies to ensure strong passwords.
4.1 Configure Password Policies
Go to Authentication → Policies → Password Policy and add policies:
| Policy | Description | Sample Value |
|---|---|---|
length | Minimum length | 12 |
maxLength | MaxLength | 128 |
digits | Minimum number of digits | 1 |
lowerCase | Minimum number of lowercase characters | 1 |
upperCase | Minimum number of uppercase characters | 1 |
specialChars | Minimum number of special characters | 1 |
notUsername | Password cannot be the same as username | (no value) |
notEmail | Password cannot match email | (no value) |
passwordHistory | Do not reuse the last N passwords | 5 |
hashAlgorithm | Password hash algorithm | pbkdf2-sha512 |
hashIterations | Number of hash iterations | 210000 |
forceExpiredPasswordChange | Force MK change after N days | 90 |
regulxExpression | Regex custom pattern | ^(?!.*(.)\1{2}).*$ |
4.2 Configuration via REST API
# Set password policies cho realm
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"passwordPolicy": "length(12) and digits(1) and lowerCase(1) and upperCase(1) and specialChars(1) and notUsername and notEmail and passwordHistory(5) and hashAlgorithm(pbkdf2-sha512) and hashIterations(210000) and forceExpiredPasswordChange(90) and maxLength(128)"
}'
4.3 Hash Algorithm Recommendations
| Algorithm | Iterations (recommended) | Notes |
|---|---|---|
pbkdf2-sha256 | 600,000 | OWASP 2023 recommendation |
pbkdf2-sha512 | 210,000 | OWASP 2023 recommendation |
argon2 | N/A (Keycloak 24+) | Memory-hard, recommended cho new deployments |
# Sử dụng Argon2 (Keycloak 24+)
# passwordPolicy: hashAlgorithm(argon2)
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"passwordPolicy": "length(12) and digits(1) and lowerCase(1) and upperCase(1) and specialChars(1) and hashAlgorithm(argon2)"
}'
5. Session Management
Strong session management is important for security.
5.1 Session Timeouts
Configure at Realm Settings → Sessions:
| Setting | Description | Recommended |
|---|---|---|
| SSO Session Idle | Maximum idle time for SSO session | 30 minutes |
| SSO Session Max | Maximum time for SSO session (regardless of activity) | 10 hours |
| SSO Session Idle Remember Me | Idle timeout when "Remember Me" enabled | 7 days |
| SSO Session Max Remember Me | Max lifetime when "Remember Me" enabled | 30 days |
| Client Session Idle | Idle timeout for client-specific session | 15 minutes |
| Client Session Max | Max lifetime for client-specific session | 8 hours |
| Offline Session Idle | Idle timeout for offline tokens | 30 days |
| Offline Session Max | Max limited lifetime for offline sessions | 60 days |
5.2 Detailed Sessions configuration
# Cấu hình session timeouts
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ssoSessionIdleTimeout": 1800,
"ssoSessionMaxLifespan": 36000,
"ssoSessionIdleTimeoutRememberMe": 604800,
"ssoSessionMaxLifespanRememberMe": 2592000,
"clientSessionIdleTimeout": 900,
"clientSessionMaxLifespan": 28800,
"offlineSessionIdleTimeout": 2592000,
"offlineSessionMaxLifespan": 5184000,
"offlineSessionMaxLifespanEnabled": true
}'
5.3 Session Limits
Limit the number of concurrent sessions per user:
# Authentication → Flows → Browser Flow
# Thêm step "User Session Limits"
Session Limits Configuration:
- Max concurrent sessions per user: 3
- Behavior on breach: "Terminate oldest session"
# Hoặc cấu hình qua Authentication Flow:
# 1. Copy "Browser" flow
# 2. Thêm execution "User Session Count Limiter"
# 3. Cấu hình max sessions
5.4 Test and Revoke Sessions
# List sessions của user
curl -s "http://localhost:8080/admin/realms/my-realm/users/$USER_ID/sessions" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq .
# Response mẫu:
# [
# {
# "id": "session-id",
# "username": "john",
# "userId": "user-uuid",
# "ipAddress": "192.168.1.100",
# "start": 1710489045,
# "lastAccess": 1710492645,
# "clients": { "client-uuid": "my-app" }
# }
# ]
# Revoke session cụ thể
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/sessions/$SESSION_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# Logout tất cả sessions của user
curl -X POST "http://localhost:8080/admin/realms/my-realm/users/$USER_ID/logout" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# Logout tất cả sessions trong realm
curl -X POST "http://localhost:8080/admin/realms/my-realm/logout-all" \
-H "Authorization: Bearer $ACCESS_TOKEN"
6. CORS Configuration
CORS (Cross-Origin Resource Sharing) is configured per client in Keycloak.
6.1 Configure CORS for Client
# Client Settings → Web Origins
# Cho phép specific origins
https://myapp.com
https://admin.myapp.com
# Cho phép tất cả redirect URIs (sử dụng "+")
+
# KHÔNG sử dụng "*" trong production
# "*" cho phép tất cả origins — nguy hiểm!
# Cấu hình CORS qua REST API
curl -X PUT "http://localhost:8080/admin/realms/my-realm/clients/$CLIENT_UUID" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webOrigins": [
"https://myapp.com",
"https://admin.myapp.com"
]
}'
6.2 CORS Best Practices
- Always specify specific origins — Do not use wildcards
* - Use
+— Automatically derive origins from Valid Redirect URIs - Separate client — Each frontend app should have its own client with its own CORS config
- Test thoroughly — Test CORS response headers with browser DevTools
7. Clickjacking Protection
Clickjacking attack embeds Keycloak login page into iframe to steal credentials.
7.1 Configure X-Frame-Options
# Realm Settings → Security Defenses → Headers
X-Frame-Options: SAMEORIGIN
# - DENY: Không cho phép iframe dưới bất kỳ điều kiện nào
# - SAMEORIGIN: Chỉ cho phép iframe từ cùng origin
# - ALLOW-FROM uri: (deprecated, dùng CSP frame-ancestors thay thế)
7.2 CSP frame-ancestors (preferred)
# Content-Security-Policy header
frame-ancestors 'self';
# Cho phép specific parent origins
frame-ancestors 'self' https://portal.mycompany.com;
8. HTTPS/TLS Enforcement
8.1 Hostname and TLS configuration
# Production: Strict HTTPS
bin/kc.sh start \
--hostname=auth.mycompany.com \
--hostname-strict=true \
--https-certificate-file=/etc/certs/tls.crt \
--https-certificate-key-file=/etc/certs/tls.key \
--https-port=8443 \
--http-enabled=false
8.2 TLS with Reverse Proxy (popular)
# Khi sử dụng reverse proxy (Nginx, HAProxy) terminate TLS
bin/kc.sh start \
--hostname=auth.mycompany.com \
--hostname-strict=true \
--proxy-headers=xforwarded \
--http-enabled=true \
--http-port=8080
# Nginx reverse proxy configuration
server {
listen 443 ssl http2;
server_name auth.mycompany.com;
ssl_certificate /etc/ssl/certs/auth.mycompany.com.crt;
ssl_certificate_key /etc/ssl/private/auth.mycompany.com.key;
# Strong TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_read_timeout 90s;
proxy_send_timeout 90s;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name auth.mycompany.com;
return 301 https://$host$request_uri;
}
8.3 Mutual TLS (mTLS)
# Bật mTLS cho client authentication
bin/kc.sh start \
--https-certificate-file=/etc/certs/tls.crt \
--https-certificate-key-file=/etc/certs/tls.key \
--https-trust-store-file=/etc/certs/truststore.jks \
--https-trust-store-password=changeit \
--https-client-auth=request
# --https-client-auth options:
# none: không yêu cầu client cert
# request: yêu cầu nhưng không bắt buộc
# required: bắt buộc client cert
8.4 Certificate Management
# Sử dụng cert-manager trong Kubernetes
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: keycloak-tls
namespace: keycloak
spec:
secretName: keycloak-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- auth.mycompany.com
renewBefore: 720h # Renew 30 ngày trước khi hết hạn
# Kubernetes Deployment sử dụng TLS secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
spec:
template:
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:26.1
args:
- start
- --hostname=auth.mycompany.com
- --https-certificate-file=/etc/certs/tls.crt
- --https-certificate-key-file=/etc/certs/tls.key
volumeMounts:
- name: tls-certs
mountPath: /etc/certs
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: keycloak-tls-secret
9. Vault Integration
Keycloak supports storing secrets (LDAP bind password, SMTP password, client secrets) in external vault instead of plaintext in database.
9.1 HashiCorp Vault Integration
# Cấu hình Keycloak sử dụng HashiCorp Vault
bin/kc.sh start \
--vault=hashicorp \
--vault-hashicorp-paths=/secret/data/keycloak \
--vault-hashicorp-address=https://vault.mycompany.com:8200 \
--vault-hashicorp-auth-method=token \
--vault-hashicorp-token=$VAULT_TOKEN
# Hoặc sử dụng AppRole authentication
bin/kc.sh start \
--vault=hashicorp \
--vault-hashicorp-paths=/secret/data/keycloak \
--vault-hashicorp-address=https://vault.mycompany.com:8200 \
--vault-hashicorp-auth-method=approle \
--vault-hashicorp-approle-role-id=$ROLE_ID \
--vault-hashicorp-approle-secret-id=$SECRET_ID
9.2 Save Secrets in HashiCorp Vault
# Cấu hình Vault KV secrets engine
vault secrets enable -path=secret kv-v2
# Lưu LDAP bind password
vault kv put secret/keycloak/my-realm \
ldap_bind_credential="LdapS3cur3P@ss!" \
smtp_password="SmtpP@ssw0rd!" \
my-client-secret="Cl13ntS3cr3t!"
# Cấu trúc path trong Vault:
# secret/data/keycloak/{realm-name}/{key}
9.3 Using Vault Reference in Keycloak
In Keycloak, use the syntax __P1_{{vault.key} instead of plaintext:
# LDAP User Federation
Bind Credential: ${vault.ldap_bind_credential}
# SMTP Settings
Password: ${vault.smtp_password}
# Client Secret
Client Secret: ${vault.my-client-secret}
9.4 Kubernetes/OpenShift Secrets Vault
# Sử dụng Kubernetes Secrets làm vault
bin/kc.sh start \
--vault=file \
--vault-dir=/mnt/secrets
# Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
name: keycloak-vault
namespace: keycloak
type: Opaque
stringData:
# Format: {realm-name}_{key}
my-realm_ldap_bind_credential: "LdapS3cur3P@ss!"
my-realm_smtp_password: "SmtpP@ssw0rd!"
my-realm_my-client-secret: "Cl13ntS3cr3t!"
# Mount Secret vào Keycloak Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
spec:
template:
spec:
containers:
- name: keycloak
args:
- start
- --vault=file
- --vault-dir=/mnt/secrets
volumeMounts:
- name: vault-secrets
mountPath: /mnt/secrets
readOnly: true
volumes:
- name: vault-secrets
secret:
secretName: keycloak-vault
9.5 File-based Vault (Development)
# Tạo vault files cho development
mkdir -p /opt/keycloak/vault/my-realm
# Mỗi file chứa một secret (filename = key)
echo -n "LdapS3cur3P@ss!" > /opt/keycloak/vault/my-realm/ldap_bind_credential
echo -n "SmtpP@ssw0rd!" > /opt/keycloak/vault/my-realm/smtp_password
# Cấu hình Keycloak
bin/kc.sh start-dev \
--vault=file \
--vault-dir=/opt/keycloak/vault
9.6 Rotating Credentials
# HashiCorp Vault: Rotate LDAP password
# 1. Update secret trong Vault
vault kv put secret/keycloak/my-realm \
ldap_bind_credential="NewLdapP@ss2026!"
# 2. Keycloak sẽ tự động lấy secret mới
# (Vault SPI cache TTL = 0 by default, mỗi lần cần sẽ fetch lại)
# Kubernetes Secrets: Update secret
kubectl create secret generic keycloak-vault \
--from-literal=my-realm_ldap_bind_credential="NewLdapP@ss2026!" \
--dry-run=client -o yaml | kubectl apply -f -
# Rolling restart Keycloak để pickup new secrets
kubectl rollout restart deployment/keycloak -n keycloak
10. Admin Console Access Restrictions
10.1 Dedicated Admin Realm
Use master realm only for admin, do not create user/client business:
Best Practice:
- Master realm: Chỉ chứa admin users
- Business realms: Chứa application users/clients
- Không cho phép self-registration trên master realm
- MFA bắt buộc cho admin accounts
10.2 IP Whitelist cho Admin Console
# Nginx: Restrict Admin Console access
location /admin/ {
# Chỉ cho phép IP nội bộ
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
proxy_pass http://keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Block admin REST API từ external
location /admin/realms/ {
allow 10.0.0.0/8;
deny all;
proxy_pass http://keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
10.3 Kubernetes Network Policy
# NetworkPolicy: Restrict Admin API access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-admin-restrict
namespace: keycloak
spec:
podSelector:
matchLabels:
app: keycloak
policyTypes:
- Ingress
ingress:
# Allow user-facing traffic from ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- port: 8080
# Allow admin traffic only from management namespace
- from:
- namespaceSelector:
matchLabels:
name: management
ports:
- port: 8080
11. Token Security
11.1 Short-lived Tokens
# Cấu hình token lifespans
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accessTokenLifespan": 300,
"accessTokenLifespanForImplicitFlow": 900,
"actionTokenGeneratedByUserLifespan": 300,
"actionTokenGeneratedByAdminLifespan": 43200
}'
# Per-client token lifespan override
curl -X PUT "http://localhost:8080/admin/realms/my-realm/clients/$CLIENT_UUID" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"access.token.lifespan": "180",
"client.session.idle.timeout": "600",
"client.session.max.lifespan": "3600"
}
}'
11.2 Token Introspection
# Introspect token (kiểm tra validity)
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token/introspect" \
-d "client_id=my-resource-server" \
-d "client_secret=$CLIENT_SECRET" \
-d "token=$ACCESS_TOKEN"
# Response
# {
# "active": true,
# "sub": "user-uuid",
# "aud": "my-app",
# "exp": 1710492645,
# "iat": 1710489045,
# "realm_access": { "roles": ["user"] },
# "scope": "openid profile email"
# }
11.3 Token Revocation
# Revoke refresh token
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/revoke" \
-d "client_id=my-app" \
-d "client_secret=$CLIENT_SECRET" \
-d "token=$REFRESH_TOKEN" \
-d "token_type_hint=refresh_token"
# Revoke access token
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/revoke" \
-d "client_id=my-app" \
-d "client_secret=$CLIENT_SECRET" \
-d "token=$ACCESS_TOKEN" \
-d "token_type_hint=access_token"
12. Production Deployment — Hardened
# docker-compose.production.yml - Hardened Keycloak
services:
keycloak:
image: quay.io/keycloak/keycloak:26.1
command:
- start
- --hostname=auth.mycompany.com
- --hostname-strict=true
- --proxy-headers=xforwarded
- --http-enabled=true
- --metrics-enabled=true
- --health-enabled=true
- --vault=file
- --vault-dir=/mnt/secrets
- --log=console
- --log-console-output=json
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD_FILE: /run/secrets/db_password
KC_CACHE: ispn
KC_CACHE_STACK: kubernetes
volumes:
- ./secrets:/mnt/secrets:ro
secrets:
- db_password
deploy:
replicas: 2
resources:
limits:
memory: 1G
cpus: '1.0'
reservations:
memory: 512M
cpus: '0.5'
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\n\r\n' >&3 && cat <&3 | grep -q '\"status\":\"UP\"'"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
secrets:
db_password:
file: ./secrets/db_password
13. Best Practices Summary
- HTTPS everywhere — Never deploy Keycloak production without TLS. Use HSTS headers.
- Secrets in Vault — Do not save plaintext credentials in the DB. Use HashiCorp Vault or Kubernetes Secrets.
- Strong password policies — Minimum 12 characters, use Argon2 or PBKDF2-SHA512 with high iterations.
- Session limits — Limit the number of concurrent sessions. Set appropriate idle/max timeouts.
- Admin access restricted — IP whitelist for Admin Console. MFA required for admin accounts.
- Short-lived tokens — Access token 5 minutes, refresh token rotation enabled.
- Brute-force protection — Enable and configure appropriately. Monitor login failure rates.
- Regular security audit — Review Keycloak configuration periodically. Update to the latest version.
- Separate admin realm — Use master realm for admin only. Do not mix admin and business users.
- Monitor and alert — Combine event logging (Lesson 18) with security monitoring for detection and response.