1. Security Hardening Checklist
Trước khi đưa Keycloak lên production, cần thực hiện đầy đủ các biện pháp hardening sau:
| # | Hạng mục | Mức độ | Trạng thái |
|---|---|---|---|
| 1 | HTTPS/TLS enforcement | Critical | ☐ |
| 2 | Đổi Admin credentials mặc định | 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 bảo vệ chống XSS attacks bằng cách kiểm soát resources nào được phép load trên page.
2.1 Cấu hình CSP trong Keycloak
Keycloak cấu hình CSP thông qua 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 Cấu hình qua 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 Các header bảo mật quan trọng
| Header | Giá trị khuyên dùng | Mục đích |
|---|---|---|
X-Frame-Options | SAMEORIGIN | Chống clickjacking |
X-Content-Type-Options | nosniff | Chống MIME type sniffing |
X-XSS-Protection | 1; mode=block | Bật XSS filter trình duyệt |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Force HTTPS (HSTS) |
Referrer-Policy | no-referrer | Không gửi referrer header |
Content-Security-Policy | Restrict frame-src, object-src | Chống XSS, data injection |
3. Brute-force Detection
Keycloak tích hợp sẵn cơ chế chống brute-force cho login attempts.
3.1 Cấu hình Brute-force
Vào 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 Cấu hình qua 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 Cách hoạt động Brute-force Detection
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 bị khóa
# 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 hỗ trợ nhiều password policies để đảm bảo mật khẩu mạnh.
4.1 Cấu hình Password Policies
Vào Authentication → Policies → Password Policy và thêm các policies:
| Policy | Mô tả | Giá trị mẫu |
|---|---|---|
length | Độ dài tối thiểu | 12 |
maxLength | Độ dài tối đa | 128 |
digits | Số ký tự số tối thiểu | 1 |
lowerCase | Số ký tự thường tối thiểu | 1 |
upperCase | Số ký tự hoa tối thiểu | 1 |
specialChars | Số ký tự đặc biệt tối thiểu | 1 |
notUsername | Mật khẩu không được trùng username | (no value) |
notEmail | Mật khẩu không được trùng email | (no value) |
passwordHistory | Không dùng lại N mật khẩu gần nhất | 5 |
hashAlgorithm | Thuật toán hash password | pbkdf2-sha512 |
hashIterations | Số vòng lặp hash | 210000 |
forceExpiredPasswordChange | Buộc đổi MK sau N ngày | 90 |
regulxExpression | Regex pattern tùy chỉnh | ^(?!.*(.)\1{2}).*$ |
4.2 Cấu hình qua 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 (khuyên dùng) | Ghi chú |
|---|---|---|
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
Quản lý session chặt chẽ là yếu tố quan trọng cho bảo mật.
5.1 Session Timeouts
Cấu hình tại Realm Settings → Sessions:
| Setting | Mô tả | Khuyên dùng |
|---|---|---|
| SSO Session Idle | Thời gian idle tối đa cho SSO session | 30 phút |
| SSO Session Max | Thời gian tối đa cho SSO session (bất kể activity) | 10 giờ |
| SSO Session Idle Remember Me | Idle timeout khi "Remember Me" enabled | 7 ngày |
| SSO Session Max Remember Me | Max lifetime khi "Remember Me" enabled | 30 ngày |
| Client Session Idle | Idle timeout cho client-specific session | 15 phút |
| Client Session Max | Max lifetime cho client-specific session | 8 giờ |
| Offline Session Idle | Idle timeout cho offline tokens | 30 ngày |
| Offline Session Max | Max limited lifetime cho offline sessions | 60 ngày |
5.2 Cấu hình Sessions chi tiết
# 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
Giới hạn số session đồng thời cho mỗi 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 Kiểm tra và 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) được cấu hình per client trong Keycloak.
6.1 Cấu hình CORS cho 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
- Luôn chỉ định origins cụ thể — Không dùng wildcard
* - Sử dụng
+— Tự động derive origins từ Valid Redirect URIs - Tách client — Mỗi frontend app nên có client riêng với CORS config riêng
- Test kỹ — Kiểm tra CORS response headers với browser DevTools
7. Clickjacking Protection
Clickjacking attack nhúng Keycloak login page vào iframe để đánh cắp credentials.
7.1 Cấu hình 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 (ưu tiên hơn)
# 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 Cấu hình Hostname và TLS
# 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 với Reverse Proxy (phổ biến)
# 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 hỗ trợ lưu trữ secrets (LDAP bind password, SMTP password, client secrets) trong external vault thay vì plaintext trong 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 Lưu Secrets trong 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 Sử dụng Vault Reference trong Keycloak
Trong Keycloak, sử dụng cú pháp ${vault.key} thay vì 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
Sử dụng master realm chỉ cho admin, không tạo 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 Tổng hợp
- HTTPS everywhere — Không bao giờ deploy Keycloak production không có TLS. Sử dụng HSTS headers.
- Secrets in Vault — Không lưu credentials plaintext trong DB. Sử dụng HashiCorp Vault hoặc Kubernetes Secrets.
- Strong password policies — Minimum 12 characters, sử dụng Argon2 hoặc PBKDF2-SHA512 với iterations cao.
- Session limits — Giới hạn số sessions đồng thời. Set idle/max timeouts phù hợp.
- Admin access restricted — IP whitelist cho Admin Console. MFA bắt buộc cho admin accounts.
- Short-lived tokens — Access token 5 phút, refresh token rotation enabled.
- Brute-force protection — Bật và cấu hình phù hợp. Monitor login failure rates.
- Regular security audit — Review Keycloak configuration định kỳ. Update lên phiên bản mới nhất.
- Separate admin realm — Sử dụng master realm chỉ cho admin. Không mix admin và business users.
- Monitor and alert — Kết hợp event logging (Bài 18) với security monitoring cho detection và response.