Chuyển đến nội dung chính

Lesson 19: Advanced Security and Vault Integration

Security hardening Keycloak, Content Security Policy headers, brute-force detection configuration, password policies (details for each policy), session management (Session Limits, Idle/Max timeout), CORS configuration, clickjacking protection, HTTPS/TLS best practices, certificate management, Vault integration (HashiCorp Vault, Kubernetes Secrets, file-based), rotating credentials, Admin Console access restrictions.

🔒 DevSecOps — Lesson 19 Lesson 19: Advanced Security and Vault Integration

Keycloak from Basic to Advanced

Part 5: Themes, Events, Security and Vault

xdev.asia

1. Security Hardening Checklist

Before putting Keycloak into production, the following hardening measures need to be fully implemented:

#ItemLevelStatus
1HTTPS/TLS enforcementCritical☐
2Change default Admin credentialsCritical☐
3Brute-force detectionHigh☐
4Password policiesHigh☐
5Session timeoutsHigh☐
6CSP headersHigh☐
7Clickjacking protectionHigh☐
8CORS configurationMedium☐
9Vault integration cho secretsHigh☐
10Admin Console access restrictionHigh☐
11Disable unused features/endpointsMedium☐
12Database encryption at restHigh☐
13Token security (short-lived)High☐
14Audit logging enabledHigh☐

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

HeaderRecommended valuePurpose
X-Frame-OptionsSAMEORIGINAnti-clickjacking
X-Content-Type-OptionsnosniffAnti-MIME type sniffing
X-XSS-Protection1; mode=blockEnable browser XSS filter
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce HTTPS (HSTS)
Referrer-Policyno-referrerDo not send referrer header
Content-Security-PolicyRestrict frame-src, object-srcAnti-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:

PolicyDescriptionSample Value
lengthMinimum length12
maxLengthMaxLength128
digitsMinimum number of digits1
lowerCaseMinimum number of lowercase characters1
upperCaseMinimum number of uppercase characters1
specialCharsMinimum number of special characters1
notUsernamePassword cannot be the same as username(no value)
notEmailPassword cannot match email(no value)
passwordHistoryDo not reuse the last N passwords5
hashAlgorithmPassword hash algorithmpbkdf2-sha512
hashIterationsNumber of hash iterations210000
forceExpiredPasswordChangeForce MK change after N days90
regulxExpressionRegex 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

AlgorithmIterations (recommended)Notes
pbkdf2-sha256600,000OWASP 2023 recommendation
pbkdf2-sha512210,000OWASP 2023 recommendation
argon2N/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:

SettingDescriptionRecommended
SSO Session IdleMaximum idle time for SSO session30 minutes
SSO Session MaxMaximum time for SSO session (regardless of activity)10 hours
SSO Session Idle Remember MeIdle timeout when "Remember Me" enabled7 days
SSO Session Max Remember MeMax lifetime when "Remember Me" enabled30 days
Client Session IdleIdle timeout for client-specific session15 minutes
Client Session MaxMax lifetime for client-specific session8 hours
Offline Session IdleIdle timeout for offline tokens30 days
Offline Session MaxMax limited lifetime for offline sessions60 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.