Non-Master Realm Authentication
Starting with Keycloak 26.4.0, access to the global /admin/serverinfo endpoint is restricted to users authenticated in the master realm. When authenticating against a non-master realm to manage its configuration, keycloak-config-cli needs special configuration to bypass server info fetching.
Related issues: #1343
The Problem
Users encounter authentication and server info access issues when:
- Authenticating with a user account in a non-master realm (e.g.,
my-realm) - Keycloak 26.4.0+ restricts
/admin/serverinfoto master realm users only - keycloak-config-cli attempts to fetch server version information and fails with permission errors
- The authenticated user does not have admin rights in the master realm
What Changed in Keycloak 26.4.0+
Before Keycloak 26.4.0
This worked fine:
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=my-realm \
--keycloak.user=realm-admin \
--keycloak.password=password \
--import.files.locations=path/to/config-file
Behavior:
- Any authenticated user could access
/admin/serverinfo - keycloak-config-cli fetched server version successfully
- Configuration import proceeded normally
After Keycloak 26.4.0
Same command fails:
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=my-realm \
--keycloak.user=realm-admin \
--keycloak.password=password \
--import.files.locations=path/to/config-file
Error:
Error fetching server info: 403 Forbidden
User authenticated in 'my-realm' does not have permission to access /admin/serverinfo
Why it fails:
/admin/serverinfois now restricted to master realm users only- Realm-specific admins no longer have access
- keycloak-config-cli cannot determine Keycloak version
- Import process halts
The Solution: Skip Server Info
Use the --keycloak.skip-server-info=true flag to bypass server info fetching.
Basic Configuration
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=my-realm \
--keycloak.user=realm-admin \
--keycloak.password=password \
--keycloak.skip-server-info=true \
--import.files.locations=path/to/config-file
Result:
- Server info fetching is skipped
- Authentication succeeds against
my-realm - Configuration import proceeds normally
- No master realm access required
With Environment Variables
export KEYCLOAK_URL=http://localhost:8080
export KEYCLOAK_LOGINREALM=my-realm
export KEYCLOAK_USER=realm-admin
export KEYCLOAK_PASSWORD=password
export KEYCLOAK_SKIPSERVERINFO=true
export IMPORT_FILES_LOCATIONS=path/to/config-file
java -jar keycloak-config-cli.jar
Docker Usage
docker run \
-e KEYCLOAK_URL=http://keycloak:8080 \
-e KEYCLOAK_LOGINREALM=my-realm \
-e KEYCLOAK_USER=realm-admin \
-e KEYCLOAK_PASSWORD=password \
-e KEYCLOAK_SKIPSERVERINFO=true \
-e IMPORT_FILES_LOCATIONS=path/to/config-file
-v $(pwd)/config:/config \
adorsys/keycloak-config-cli:latest
Kubernetes ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: keycloak-config-cli
data:
KEYCLOAK_URL: "http://keycloak:8080"
KEYCLOAK_LOGINREALM: "my-realm"
KEYCLOAK_USER: "realm-admin"
KEYCLOAK_SKIPSERVERINFO: "true"
IMPORT_FILES_LOCATIONS: "path/to/config-file"
When to Use Skip Server Info
Use Skip Server Info When:
-
Non-Master Realm Authentication
- Authenticating with users from realms other than
master - Using realm-specific admin accounts
- Managing individual realms independently
- Authenticating with users from realms other than
-
Keycloak 26.4.0+
- Running Keycloak version 26.4.0 or higher
- Encountering 403 Forbidden on
/admin/serverinfo
-
Security Policies
- Organization restricts master realm access
- Principle of least privilege requires realm-scoped access
- Service accounts limited to specific realms
Do Not Use Skip Server Info When:
-
Master Realm Authentication
- Already authenticating against
masterrealm - User has appropriate master realm permissions
- Already authenticating against
-
Version-Specific Features Needed
- Relying on automatic version detection for compatibility
- Using features that vary by Keycloak version
-
Keycloak < 26.4.0
- Server info is accessible to all authenticated users
- No restriction in place
Consequences of Skipping Server Info
What Happens
-
No Version Fetching
- keycloak-config-cli cannot automatically determine Keycloak version
- Version defaults to
unknown
-
Alternative Health Check
- Uses authentication-based health check instead
- Validates connectivity via login realm authentication
-
Compatibility Checks Limited
- Some version-specific compatibility checks may be skipped
Explicit Version Specification
If you know your Keycloak version and want version-specific behavior:
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=my-realm \
--keycloak.user=realm-admin \
--keycloak.password=password \
--keycloak.skip-server-info=true \
--keycloak.version=26.0.0 \
--import.files.locations=path/to/config-file
Benefits:
- Version-specific features enabled
- Better compatibility handling
- Clearer in logs what version is expected
Authentication Methods with Non-Master Realm
Method 1: Username/Password (Default)
--keycloak.login-realm=my-realm
--keycloak.user=realm-admin
--keycloak.password=password
--keycloak.skip-server-info=true
Use when:
- Interactive or development environments
- Credentials stored securely (secrets management)
- User has appropriate realm management permissions
Method 2: Client Credentials (Service Account)
--keycloak.login-realm=my-realm
--keycloak.client-id=config-cli-client
--keycloak.client-secret=ClientSecretHere
--keycloak.grant-type=client_credentials
--keycloak.skip-server-info=true
Setup:
- Create confidential client in target realm
- Enable "Service Accounts Enabled"
- Disable "Standard Flow" and "Direct Access Grants"
- Assign roles via "Service Account Roles" tab
Use when:
- Automated processes (CI/CD)
- No user credentials available
- Principle of least privilege (service account has only needed permissions)
Common Pitfalls
1. Forgetting skip-server-info Flag
Problem:
java -jar keycloak-config-cli.jar \
--keycloak.login-realm=my-realm \
--keycloak.user=admin \
--keycloak.password=password \
--import.files.locations=path/to/config-file
Error:
Solution: Add the flag:
2. Wrong Login Realm
Problem:
Error:
Solution: Use correct login realm:
3. Insufficient Permissions
Problem: User authenticated but cannot manage realm.
Error:
Cause: User lacks required realm management roles.
Solution: Assign appropriate roles:
- In Keycloak Admin Console
- Navigate to: Users → select user → Role Mappings
- Assign client roles from
realm-management:manage-realmmanage-clientsmanage-users- Or assign composite role:
realm-admin
Troubleshooting
Test Authentication
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-d "client_id=admin-cli" \
-d "username=realm-admin" \
-d "password=password" \
-d "grant_type=password"
Test Server Info Access
TOKEN=$(curl -s -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-d "client_id=admin-cli" -d "username=realm-admin" -d "password=password" \
-d "grant_type=password" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
curl -H "Authorization: Bearer $TOKEN" \
-w "\nHTTP Status: %{http_code}\n" \
http://localhost:8080/admin/serverinfo
Complete Configuration Examples
Example 1: Realm-Specific Admin
Scenario: Each realm has its own admin user, no master realm access.
# Realm-Specific Admin Configuration
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=corporate-realm \
--keycloak.user=corporate-admin \
--keycloak.password=SecurePassword123 \
--keycloak.skip-server-info=true \
--import.files.locations=corporate-realm-config.yaml \
--import.remote-state.enabled=true \
--import.validate=true
Test Command:
# Test with local config file
curl -o /tmp/corporate-realm-config.yaml https://raw.githubusercontent.com/adorsys/keycloak-config-cli/main/src/test/resources/import-files/realm-update/other-client.json 2>/dev/null || echo '{"realm": "test-realm", "enabled": true}' > /tmp/corporate-realm-config.yaml
Example 2: Multi-Realm Management
Scenario: Managing multiple realms with separate admin accounts.
Structure:
config/
├── realm-a/
│ ├── config.yaml
│ └── import.sh
├── realm-b/
│ ├── config.yaml
│ └── import.sh
└── realm-c/
├── config.yaml
└── import.sh
Import Script (realm-a/import.sh):
#!/bin/bash
set -e
# Test script for realm-a
java -jar keycloak-config-cli.jar \
--keycloak.url=${KEYCLOAK_URL:-http://localhost:8080} \
--keycloak.login-realm=realm-a \
--keycloak.user=${REALM_A_ADMIN_USER:-testuser} \
--keycloak.password=${REALM_A_ADMIN_PASSWORD:-testpass} \
--keycloak.skip-server-info=true \
--import.files.locations=/tmp/test-realm-config.yaml \
--import.remote-state.enabled=true
echo "✓ Realm-a configuration imported successfully"
Test Command:
# Make script executable and test
chmod +x /tmp/import.sh
# Note: Requires Keycloak running and realm-a configured
Example 3: CI/CD Pipeline
Scenario: Automated realm configuration in CI/CD using service accounts.
GitLab CI (.gitlab-ci.yml):
deploy-realm-config:
stage: deploy
image: adorsys/keycloak-config-cli:latest
script:
- |
java -jar /opt/keycloak-config-cli.jar \
--keycloak.url=${KEYCLOAK_URL} \
--keycloak.login-realm=${REALM_NAME:-test-realm} \
--keycloak.client-id=config-cli-client \
--keycloak.client-secret=${CLIENT_SECRET} \
--keycloak.grant-type=client_credentials \
--keycloak.skip-server-info=true \
--import.files.locations=/tmp/test-config.yaml \
--import.validate=true
only:
- main
Service Account Setup Commands:
# 1. Get master token
MASTER_TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
-d "client_id=admin-cli" -d "username=admin" -d "password=admin" \
-d "grant_type=password" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
# 2. Create client 'config-cli-client' in target realm
curl -s -X POST "http://localhost:8080/admin/realms/test-realm/clients" \
-H "Authorization: Bearer $MASTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"clientId":"config-cli-client","enabled":true,"serviceAccountsEnabled":true,"clientAuthenticatorType":"client-secret","secret":"test-secret-123"}'
# 3. Assign roles to service account
# Get client UUID and assign realm-management roles
Example 4: Kubernetes Deployment
Scenario: Deploy keycloak-config-cli as a Kubernetes Job.
apiVersion: batch/v1
kind: Job
metadata:
name: keycloak-realm-config
namespace: keycloak
spec:
template:
spec:
containers:
- name: config-cli
image: adorsys/keycloak-config-cli:latest
env:
- name: KEYCLOAK_URL
value: "http://keycloak:8080"
- name: KEYCLOAK_LOGINREALM
value: "production-realm"
- name: KEYCLOAK_USER
valueFrom:
secretKeyRef:
name: keycloak-admin
key: username
- name: KEYCLOAK_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-admin
key: password
- name: KEYCLOAK_SKIPSERVERINFO
value: "true"
- name: KEYCLOAK_AVAILABILITYCHECK_ENABLED
value: "true"
- name: KEYCLOAK_AVAILABILITYCHECK_TIMEOUT
value: "120s"
- name: IMPORT_FILES_LOCATIONS
value: "/config/realm-config.yaml"
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: realm-configuration
restartPolicy: OnFailure
Test Command (with kubectl):
# Apply the job (requires Kubernetes cluster)
# kubectl apply -f k8s-job.yaml
# kubectl logs job/keycloak-realm-config
Best Practices
- Use Service Accounts in Production
- More secure than user credentials
- Easier to rotate secrets
-
Better audit trail
-
Always Specify skip-server-info for Non-Master
- Make it explicit in scripts
-
Prevents unexpected failures after upgrades
-
Explicitly Set Version When Known
- Helps with compatibility checks
-
Makes behavior more predictable
-
Use Availability Checks
- Ensures Keycloak is ready
-
Prevents CI/CD failures
-
Enable Validation
-
Catches configuration errors early
-
Use Remote State
- Tracks managed resources
-
Safer for partial imports
-
Secure Credential Management
- Use secrets management (Vault, Kubernetes Secrets)
- Never commit credentials
- Rotate regularly
Migration Guide
From Master Realm to Non-Master Realm Authentication
Before (Keycloak < 26.4.0):
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.user=admin \
--keycloak.password=admin \
--import.files.locations=my-realm-config.yaml
After (Keycloak 26.4.0+):
Option 1: Continue using master realm (not recommended)
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=master \
--keycloak.user=admin \
--keycloak.password=admin \
--import.files.locations=my-realm-config.yaml
Option 2: Switch to realm-specific admin (recommended)
java -jar keycloak-config-cli.jar \
--keycloak.url=http://localhost:8080 \
--keycloak.login-realm=my-realm \
--keycloak.user=my-realm-admin \
--keycloak.password=SecurePassword \
--keycloak.skip-server-info=true \
--import.files.locations=my-realm-config.yaml
Benefits of Option 2: - Follows principle of least privilege - Each realm independently managed - Better security separation - Aligns with multi-tenant architectures
Quick Reference
| Option | CLI Flag | Environment Variable |
|---|---|---|
| Skip server info | --keycloak.skip-server-info=true |
KEYCLOAK_SKIPSERVERINFO=true |
| Keycloak URL | --keycloak.url |
KEYCLOAK_URL |
| Login realm | --keycloak.login-realm |
KEYCLOAK_LOGINREALM |
| Username | --keycloak.user |
KEYCLOAK_USER |
| Password | --keycloak.password |
KEYCLOAK_PASSWORD |
| Config file | --import.files.locations |
IMPORT_FILES_LOCATIONS |
Keycloak Version Compatibility
| Keycloak Version | ServerInfo Access | Skip Flag Required |
|---|---|---|
| < 26.4.0 | All authenticated users | No |
| 26.4.0 - 26.x.x | Master realm only | Yes (for non-master) |
| 27.0.0+ | Master realm only | Yes (for non-master) |
Recommendation: Always use --keycloak.skip-server-info=true when authenticating against non-master realms for forward compatibility.