adorsys / keycloak-config-cli

Import YAML/JSON-formatted configuration files into Keycloak - Configuration as Code for Keycloak.
Apache License 2.0
714 stars 134 forks source link

Creation of realm with client_credentials authentication flow fails with 403 Forbidden #339

Closed chlumper closed 3 years ago

chlumper commented 3 years ago

Describe the bug Creation of a new realm fails with HTTP 403 Forbidden if client_credentials authentication flow with ClientId/ClientSecret is used (although the realm actually got created).

To Reproduce

Expected behavior The realm should be created without any errors and further configuration of the realm (e.g. creation of clients) should occur.

Possible issue? Could it be that the CLI needs to refresh the access-token after the creation of the realm? Since otherwise there are no claims for the new realm mynode-realm on the token. See comparison of a token manually issued before and after creation of the realm. This theory would fit if the RealmRepository.get() method triggers some requests. However I couldn't see any logs on the keycloak server regarding 403 forbidden.

Access token before creation of new realm [click to expand] ```JSON { "exp": 1614968632, "iat": 1614968572, "jti": "a5e103b1-82ec-4cad-93c9-c1de835e79ff", "iss": "http://localhost:8080/auth/realms/master", "aud": [ "security-admin-console", "master-realm", "account" ], "sub": "5255a1fd-0f3c-4b28-baf9-4bfc11f4d7fc", "typ": "Bearer", "azp": "my-cli", "acr": "1", "realm_access": { "roles": [ "create-realm", "offline_access", "admin", "uma_authorization" ] }, "resource_access": { "master-realm": { "roles": [ "view-identity-providers", "view-realm", "manage-identity-providers", "impersonation", "create-client", "manage-users", "query-realms", "view-authorization", "query-clients", "query-users", "manage-events", "manage-realm", "view-events", "view-users", "view-clients", "manage-authorization", "manage-clients", "query-groups" ] }, "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } }, "scope": "email profile", "clientHost": "192.168.80.1", "email_verified": false, "clientId": "my-cli", "preferred_username": "service-account-my-cli", "clientAddress": "192.168.80.1" } ```
Access token after creation of new realm [click to expand] ```JSON { "exp": 1614968495, "iat": 1614968435, "jti": "899b7864-12fb-4461-aa72-0e0383575260", "iss": "http://localhost:8080/auth/realms/master", "aud": [ "security-admin-console", "mynode-realm", "master-realm", "account" ], "sub": "593bf4b3-be03-42f0-9668-28f3da5d874a", "typ": "Bearer", "azp": "my-cli", "acr": "1", "realm_access": { "roles": [ "create-realm", "offline_access", "admin", "uma_authorization" ] }, "resource_access": { "mynode-realm": { "roles": [ "view-identity-providers", "view-realm", "manage-identity-providers", "impersonation", "create-client", "manage-users", "query-realms", "view-authorization", "query-clients", "query-users", "manage-events", "manage-realm", "view-events", "view-users", "view-clients", "manage-authorization", "manage-clients", "query-groups" ] }, "master-realm": { "roles": [ "view-realm", "view-identity-providers", "manage-identity-providers", "impersonation", "create-client", "manage-users", "query-realms", "view-authorization", "query-clients", "query-users", "manage-events", "manage-realm", "view-events", "view-users", "view-clients", "manage-authorization", "manage-clients", "query-groups" ] }, "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } }, "scope": "profile email", "clientHost": "192.168.48.1", "email_verified": false, "clientId": "my-cli", "preferred_username": "service-account-my-cli", "clientAddress": "192.168.48.1" } ```

Only workaround we know so far is to use a kind of "service"-user instead of the client (since everything works with user authentication).

Environment (please complete the following information):

jkroepke commented 3 years ago

I appreciate the detailed bug description. It helps a lot to reproduce the case and find the error quickly.

Could it be that the CLI needs to refresh the access-token after the creation of the realm?

I tried this and this resolves the error. Godlike hint.

jkroepke commented 3 years ago

Argh, i do a mistake, the quick fix is in main (https://github.com/adorsys/keycloak-config-cli/commit/e9f86fd79c9be3266312a41c7b2977d3ba1811dc) now, but I will open a PR to have a test for this case.

chlumper commented 3 years ago

Thank you very much for fixing the issue so quickly! Just tested it with the edge image and everything works as expected now :clap:.

Looking forward to the next release version...

jledoux-sonergia commented 2 years ago

Hello there !

I'm kind of facing the same issue while testing locally with docker and docker-compose on Ubuntu. Not sure if it's relevant to open a new issue, so I'm commenting here for now.

Keycloak: 15.1.1 Keycloak Config CLI: v4.5.0-15.1.1

Both containers are attached to an internal network and are able to reach each another.

I'm trying to inject my JSON realms configuration through keycloak-config-cli, on a fresh new instance of Keycloak with its default out-of-the-box configuration.

Keycloak properly initialize and create its internal database. But config CLI is not able to reach it for some reason:

INFO 1 --- [           main] d.a.k.config.KeycloakConfigApplication   : Starting KeycloakConfigApplication v4.5.0 using Java 17.0.1 on ce89b99b38f3 with PID 1 (/app/keycloak-config-cli.jar started by ? in /)
INFO 1 --- [           main] d.a.k.config.KeycloakConfigApplication   : No active profile set, falling back to default profiles: default
INFO 1 --- [           main] d.a.k.config.KeycloakConfigApplication   : Started KeycloakConfigApplication in 1.053 seconds (JVM running for 1.444)
INFO 1 --- [           main] d.a.k.config.KeycloakConfigRunner        : Importing file '/config/realms/00_master-dev-realm-export.json'
INFO 1 --- [           main] d.a.k.config.provider.KeycloakProvider   : Wait 120 seconds until http://keycloak.docker:8080/auth is available ...
ERROR 1 --- [           main] d.a.k.config.KeycloakConfigRunner        : Could not connect to keycloak in 120 seconds: HTTP 403 Forbidden
INFO 1 --- [           main] d.a.k.config.KeycloakConfigRunner        : keycloak-config-cli running in 02:00.150.

If I curl Keycloak instance from config CLI, it's reachable:

# curl -L -I http://keycloak.docker:8080/auth

HTTP/1.1 303 See Other
Connection: keep-alive
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Location: http://keycloak.docker:8080/auth/
Referrer-Policy: no-referrer
Content-Length: 0
Date: Wed, 22 Dec 2021 16:41:14 GMT

HTTP/1.1 200 OK
Cache-Control: no-cache, must-revalidate, no-transform, no-store
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Referrer-Policy: no-referrer
Content-Security-Policy: frame-src 'self'; frame-ancestors 'self'; object-src 'none';
Date: Wed, 22 Dec 2021 16:41:15 GMT
Connection: keep-alive
X-Robots-Tag: none
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Content-Type: text/html;charset=utf-8
Content-Length: 4076

The only way I found to make it work is to change Keycloak's master realm setting SSL_REQUIRED from "EXTERNAL" to "NONE", then configuration is properly applied by config CLI.

My docker-compose.yml file looks like this:

  keycloak-config-cli:
    container_name: keycloak-config-cli
    image: quay.io/adorsys/keycloak-config-cli:v4.5.0-15.1.1
    depends_on:
      keycloak:
        condition: service_healthy
    volumes:
      - ./.docker/keycloak-config-cli/config:/config
    networks:
      - external
      - internal
    env_file:
      - .env

And relevant env vars:

KEYCLOAK_URL=http://keycloak.${INTERNAL_DOMAIN}:8080/auth
KEYCLOAK_USER=admin
KEYCLOAK_PASSWORD=secret
KEYCLOAK_SSLVERIFY=false
jkroepke commented 2 years ago

The only way I found to make it work is to change Keycloak's master realm setting SSL_REQUIRED from "EXTERNAL" to "NONE", then configuration is properly applied by config CLI.

Thats the reason here. I can see a http:// URL is configured here. If you are trying to login through http://, then keycloak would deny this request.

INFO 1 --- [           main] d.a.k.config.provider.KeycloakProvider   : Wait 120 seconds until http://keycloak.docker:8080/auth is available ...
ERROR 1 --- [           main] d.a.k.config.KeycloakConfigRunner        : Could not connect to keycloak in 120 seconds: HTTP 403 Forbidden

The curl request in you example return 200 but keycloak-config-cli does not do a simple GET / request. It constantly tries to authenticate, which already result in a 403 forbidden error.

From my point of view. Your issue is a different one compared to the original one.

Instead using the internal URL, you may need to use the external URL of Keycloak.

jledoux-sonergia commented 2 years ago

Thank you for the very quick reply and for your explanation and advice.

Makes total sense, guess I was expecting Keycloak to know it's on an internal network, but from its side it's still an external request which has to be secured. It would certainly work if I could reach it from localhost.

jkroepke commented 2 years ago

Checkout compose network_mode

https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode

You can configure that the keycloak-config-cli container will join the keycloak network namespace. If both containers share the same namespace, then you can reach keycloak via localhost.

jledoux-sonergia commented 2 years ago

Oh nice! I'll look into it, I didn't know you could actually do that.

I found another way around this, that I'm sharing here as well.

According to Keycloak documentation:

external requests:: Users can interact with Keycloak without SSL so long as they stick to private IP addresses such as localhost, 127.0.0.1, 10.x.x.x, 192.168.x.x, and 172.16.x.x. If you try to access Keycloak without SSL from a non-private IP address, you will get an error.

So I just changed my docker internal network subnet and now Keycloak config CLI can reach it like it would if it was on a same network namespace:

networks:
  internal:
    driver: bridge
    internal: true
    ipam:
      config:
        - subnet: 172.16.0.0/16
          gateway: 172.16.0.1
jkroepke commented 2 years ago

I guess it's something like this

  keycloak-config-cli:
    container_name: keycloak-config-cli
    image: quay.io/adorsys/keycloak-config-cli:v4.5.0-15.1.1
...
    network_mode: "service:keycloak"

and remove the networks from that service completly.

Looks like you found already a alternative solution.

ic2hrmk commented 2 years ago

The original issue is still reproducible with almost the same setup as @chlumper mentioned.

Environment setup:

- Kubernetes
- Keycloak 15.0.2 & Postgres as a persistence (both running in namespace A)
- Keycloak Config CLI v4.5.0 (running in the namespace B)

Initially, the target realm was created by Keycloak Config CLI v4.3.0 and it (CLI tool) was restarted multiple times during the releases with no issues at all. The problem came up when the Keycloak & Postgres were restarted (data anyways was stored in the volume so no data loss was faced).

I tried the approach suggested by @jkroepke to turn off SSL validation via Web UI in the Realm settings (Realm Settings > Login > Require SSL - none), but it didn't make it work. I do believe that SSL has nothing to do with it since all IP addresses inside the Kubernetes are private (10...* mask)

I collected HTTP logs of the request that got denied (cleaned up from sensitive info):

2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> GET /auth/admin/realms/my-realm HTTP/1.1
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> Accept: application/json
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> Authorization: Bearer TOKEN
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> Cookie: KC_RESTART=
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> Host: keycloak.namespace-name.svc.cluster.local:8080
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> Connection: Keep-Alive
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.13 (Java/11.0.11)
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 >> "GET /auth/admin/realms/my-realm HTTP/1.1[\r][\n]"
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 >> "Accept: application/json[\r][\n]"
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 >> "Authorization: Bearer TOKEN[\r][\n]"
2022-01-12 16:32:09.804 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 >> "Cookie: KC_RESTART=[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "HTTP/1.1 403 Forbidden[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "X-XSS-Protection: 1; mode=block[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "X-Frame-Options: SAMEORIGIN[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "Referrer-Policy: no-referrer[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "Date: Wed, 12 Jan 2022 16:32:09 GMT[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "Connection: keep-alive[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "Strict-Transport-Security: max-age=31536000; includeSubDomains[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "X-Content-Type-Options: nosniff[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "Content-Type: application/json[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "Content-Length: 25[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "[\r][\n]"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.wire                     : http-outgoing-0 << "{"error":"unknown_error"}"
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << HTTP/1.1 403 Forbidden
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << X-XSS-Protection: 1; mode=block
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << X-Frame-Options: SAMEORIGIN
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << Referrer-Policy: no-referrer
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << Date: Wed, 12 Jan 2022 16:32:09 GMT
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << Connection: keep-alive
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << Strict-Transport-Security: max-age=31536000; includeSubDomains
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << X-Content-Type-Options: nosniff
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << Content-Type: application/json
2022-01-12 16:32:09.807 DEBUG 7 --- [           main] org.apache.http.headers                  : http-outgoing-0 << Content-Length: 25

The payload part of the token which was used by Keycloak Config CLI and got the 403 code.

{
  "exp": 1642005189,
  "iat": 1642005129,
  "jti": "104d8fb4-062f-435c-a53e-d352fb4a267e",
  "iss": "http://keycloak.namespace-name.svc.cluster.local:8080/auth/realms/master",
  "sub": "uuid",
  "typ": "Bearer",
  "azp": "keycloak-loader-client",
  "acr": "1",
  "realm_access": {
    "roles": [
      "create-realm"
    ]
  },
  "scope": "profile email",
  "clientId": "keycloak-loader-client",
  "email_verified": false,
  "clientHost": "10.193.22.137",
  "preferred_username": "service-account-keycloak-loader-client",
  "clientAddress": "10.193.22.137"
}
jkroepke commented 2 years ago

Hi,

by reading the logs here, the error is a different one.

The author issue is around authentication/login. But you login clearly works. SSL or Hostname restrictions are not effected here. You gain an error 403 by access the realm my-realm.

Can you verifiy, the CLI users has enough permissions? create-realm is able to create realms, but not able to read existing one.

  "realm_access": {
    "roles": [
      "create-realm"
    ]
  },

The problem came up when the Keycloak & Postgres were restarted

Feels like, a users is created, then the users permission maybe modified but old permissions remains in a cache. Since the cache of keycloak is not persistent, A restart of Keycloak clear the cache.

ic2hrmk commented 2 years ago

Thanks, @jkroepke!

Yes, adding the admin role to the service account used by Config CLI fixed the issue, thanks!

The other problem here is that setting an administrative role to the service account might be a security concern since there is a gap between just creating a realm and administrating everything from the master realm.

Do you have any recommendations on the role set for this case?

jkroepke commented 2 years ago

Do you have any recommendations on the role set for this case?

My Keycloak knowledge in this corner is very rare. Looks like Keycloak is missing a global level view (or auditor role).

As I know, after realm creation, there is a client in the master realm named <realm-name>-realm and you can consume the view-realm role here. but thats a 2 step import here, since this is only possible after the realm is created (and the cli client needs permissions inside the master realm)

image

Anyway the fine-grained authorization policies in Keycloak maybe fits better here, but I don't have any knowledge here.

ic2hrmk commented 2 years ago

I did a reset cache for all realms on the Keycloak manually via the Web UI and restarted Keycloak Config CLI without the admin role - it worked without the 403 error.

Later on, restarted Keycloak & Postgres pods and later the Config CLI tool as well - the issue was reproduced...

ic2hrmk commented 2 years ago

Btw, restarting Config CLI tool for the same pod (instance) works without any additional actions all the time.

One more, I played around with assigning roles to the master-realm including view-realm, query-realms, manage-realm, but it didn't help. The only thing that fixed the issue was to add the master realm admin role. Keycloak is so finicky sometimes, haha

About the two-steps import, interesting idea but seams to be very complicated. By default, I have a file of master realm which has only credentials for the Keycloak Congig CLI without any additional details on other realms. The idea here is that Config CLI can login into the master realm with pre-created realm and later create/edit any realm it wants to

jkroepke commented 2 years ago

I played around with assigning roles to the master-realm

But this affects only the master realm itself, for my-realm realm, there should be a my-realm-realm client.

jkroepke commented 2 years ago

https://www.keycloak.org/docs/latest/server_admin/#global-roles

Users with the create-realm role are allowed to create new realms. They will be granted full access to any new realm they create.

I guess this is, what you want and expect. Maybe keycloak we grant such permissions, but keycloak-config-cli will revoke them again. because the permissions are not defined in the json.

https://github.com/keycloak/keycloak/blob/8ea09d38168c22937363cf77a07f9de5dc7b48b0/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java#L150-L159

ic2hrmk commented 2 years ago

Users with the create-realm role are allowed to create new realms. They will be granted full access to any new realm they create.

That's a nice point!

I guess this is, what you want and expect. Maybe keycloak we grant such permissions, but keycloak-config-cli will revoke them again. because the permissions are not defined in the json.

I don't think that's the case. In my situation, Config CLI has nothing to do with the permissions as it persisted in the master realm, not the target one, so I do believe all the permissions/roles are to be saved in the master realm.

But I will check your assumption, I will try to change the properties to keep all the configurations:

import.managed.client-scope-mapping=no-delete
import.managed.role=no-delete
jkroepke commented 2 years ago

Otherwise, you have to use a user (grant_type=password) to archive this, because grant_type=client_credentials together with the realm role create-realm does not work.

In case, it works with a user instead a client, its one more ticket again.

ic2hrmk commented 2 years ago

So, I tried different combinations, all my results are in the table below.

To make it all clear, I set all the import.managed.<resource> to be 'no-delete' to make sure that Config CLI won't delete it.

The reason I was adding/removing token exchange is that I found a different topic on the Keycloak Discussions saying that if the Policy Enforcer is enabled (in my case it was for the token exchange policy) - the topic starter was getting 403: https://keycloak.discourse.group/t/keyclock-return-403-instead-of-401-for-unauthenticated-requests-when-enabling-policy-enforcer-config-when-removing-policy-enforcer-config-it-returns-401/11336 , so it was more just to make sure.

Credentials type used Token exchange enabled Got the 'forbidden' error?
client_credentials yes yes
client_credentials no yes
password yes yes
password no yes
jkroepke commented 2 years ago

No clue whats going on here. I'm not able to reproduce your error...

I tried to test this on my local machine, here is my docker run:

docker run -eKEYCLOAK_USER=admin -eKEYCLOAK_PASSWORD=admin123 -p8080:8080 --rm -v $PWD/data:/opt/jboss/keycloak/standalone/data/ -ti jboss/keycloak:16.1.0

Then I create a service account

service-account.json ```json { "realm": "master", "displayName": "master", "users": [ { "username": "service-account-config-cli-master", "enabled": true, "serviceAccountClientId": "config-cli-master", "realmRoles": [ "create-realm" ] } ], "clients": [ { "clientId": "config-cli-master", "enabled": true, "clientAuthenticatorType": "client-secret", "secret": "config-cli-master-secret", "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": false, "implicitFlowEnabled": false, "directAccessGrantsEnabled": false, "serviceAccountsEnabled": true, "publicClient": false, "frontchannelLogout": false } ] } ```

Then I create an additional realm.json

java -jar target/keycloak-config-cli.jar --spring.profiles.active=dev --keycloak.grant-type=client_credentials --keycloak.client-id=config-cli-master --keycloak.client-secret=config-cli-master-secret --import.path=realm.json --import.force=true
realm.json ```json { "enabled": true, "realm": "realmWithRoles", "roles": { "realm": [ { "name": "my_realm_role", "description": "My realm role", "composite": false, "clientRole": false, "attributes": { "my added attribute": [ "my added attribute value", "my added attribute second value" ] } } ], "client": { "moped-client": [ { "name": "my_client_role", "description": "My moped-client role", "composite": false, "clientRole": true, "attributes": { "my added client attribute": [ "my added client attribute value", "my added client attribute second value" ] } } ] } }, "clients": [ { "clientId": "moped-client", "name": "moped-client", "description": "Moped-Client", "enabled": true, "clientAuthenticatorType": "client-secret", "secret": "my-special-client-secret", "redirectUris": [ "*" ], "webOrigins": [ "*" ] } ] } ```

Logs. Between second and third run, I restart the Keycloak.

jkr@joe-nb keycloak-config-cli % java -jar target/keycloak-config-cli.jar --spring.profiles.active=dev --import.path=./src/test/resources/import-files/service-account/00_update_realm_create_service_account_in_master_realm.json
2022-01-13 22:19:08.038  INFO 84692 --- [           main] d.a.k.config.KeycloakConfigApplication   : Starting KeycloakConfigApplication v4.5.1-SNAPSHOT using Java 11.0.13 on joe-nb with PID 84692 (/Users/jkr/IdeaProjects/keycloak-config-cli/target/keycloak-config-cli.jar started by jkr in /Users/jkr/IdeaProjects/keycloak-config-cli)
2022-01-13 22:19:08.040  INFO 84692 --- [           main] d.a.k.config.KeycloakConfigApplication   : The following profiles are active: dev
2022-01-13 22:19:08.489  INFO 84692 --- [           main] d.a.k.config.KeycloakConfigApplication   : Started KeycloakConfigApplication in 0.961 seconds (JVM running for 1.345)
2022-01-13 22:19:09.442  INFO 84692 --- [           main] d.a.k.config.KeycloakConfigRunner        : Importing file '/Users/jkr/IdeaProjects/keycloak-config-cli/./src/test/resources/import-files/service-account/00_update_realm_create_service_account_in_master_realm.json'
2022-01-13 22:19:11.248 DEBUG 84692 --- [           main] d.a.k.config.service.RealmImportService  : Updating realm 'master'...
2022-01-13 22:19:11.688 DEBUG 84692 --- [           main] d.a.k.c.service.ClientImportService      : Create client 'config-cli-master' in realm 'master'
2022-01-13 22:19:11.889 DEBUG 84692 --- [           main] d.a.k.config.service.UserImportService   : Update user 'service-account-config-cli-master' in realm 'master'
2022-01-13 22:19:12.146 DEBUG 84692 --- [           main] d.a.k.config.service.UserImportService   : Add realm-level roles [uma_authorization, offline_access, create-realm] to user 'service-account-config-cli-master' in realm 'master'
2022-01-13 22:19:12.301 DEBUG 84692 --- [           main] d.a.k.config.service.UserImportService   : Add client-level roles [view-profile, manage-account] for client 'account' to user 'service-account-config-cli-master' in realm 'master'
2022-01-13 22:19:12.588 DEBUG 84692 --- [           main] d.a.k.config.service.state.StateService  : Updated states of realm 'master'
2022-01-13 22:19:12.708 DEBUG 84692 --- [           main] d.a.k.c.s.checksum.ChecksumService       : Updated import checksum of realm 'master' to '788a04976262da5ee127a22181311afc6786bfd57957f8957e7b13ae2fcd081a'
2022-01-13 22:19:12.709  INFO 84692 --- [           main] d.a.k.config.KeycloakConfigRunner        : keycloak-config-cli running in 00:03.428.
jkr@joe-nb keycloak-config-cli % java -jar target/keycloak-config-cli.jar --spring.profiles.active=dev --keycloak.grant-type=client_credentials --keycloak.client-id=config-cli-master --keycloak.client-secret=config-cli-master-secret --import.path=src/test/resources/import-files/roles/00_create_realm_with_roles.json --import.force=true
2022-01-13 22:21:33.303  INFO 84706 --- [           main] d.a.k.config.KeycloakConfigApplication   : Starting KeycloakConfigApplication v4.5.1-SNAPSHOT using Java 11.0.13 on joe-nb with PID 84706 (/Users/jkr/IdeaProjects/keycloak-config-cli/target/keycloak-config-cli.jar started by jkr in /Users/jkr/IdeaProjects/keycloak-config-cli)
2022-01-13 22:21:33.305  INFO 84706 --- [           main] d.a.k.config.KeycloakConfigApplication   : The following profiles are active: dev
2022-01-13 22:21:33.786  INFO 84706 --- [           main] d.a.k.config.KeycloakConfigApplication   : Started KeycloakConfigApplication in 1.037 seconds (JVM running for 1.436)
2022-01-13 22:21:34.575  INFO 84706 --- [           main] d.a.k.config.KeycloakConfigRunner        : Importing file '/Users/jkr/IdeaProjects/keycloak-config-cli/src/test/resources/import-files/roles/00_create_realm_with_roles.json'
2022-01-13 22:21:34.836 DEBUG 84706 --- [           main] d.a.k.config.service.RealmImportService  : Creating realm 'realmWithRoles' ...
2022-01-13 22:21:36.075 DEBUG 84706 --- [           main] d.a.k.c.service.ClientImportService      : Create client 'moped-client' in realm 'realmWithRoles'
2022-01-13 22:21:36.285 DEBUG 84706 --- [           main] d.a.k.config.service.RoleImportService   : Create realm-level role 'my_realm_role' in realm 'realmWithRoles'
2022-01-13 22:21:36.312 DEBUG 84706 --- [           main] d.a.k.config.service.RoleImportService   : Create client-level role 'my_client_role' for client 'moped-client' in realm 'realmWithRoles'
2022-01-13 22:21:36.528 DEBUG 84706 --- [           main] d.a.k.config.service.state.StateService  : Updated states of realm 'realmWithRoles'
2022-01-13 22:21:36.607 DEBUG 84706 --- [           main] d.a.k.c.s.checksum.ChecksumService       : Updated import checksum of realm 'realmWithRoles' to 'c51a94d6600b2f6737a474d6abb924564533092940b9b8eea310f712a6a66f5f'
2022-01-13 22:21:36.607  INFO 84706 --- [           main] d.a.k.config.KeycloakConfigRunner        : keycloak-config-cli running in 00:02.144.
jkr@joe-nb keycloak-config-cli % java -jar target/keycloak-config-cli.jar --spring.profiles.active=dev --keycloak.grant-type=client_credentials --keycloak.client-id=config-cli-master --keycloak.client-secret=config-cli-master-secret --import.path=src/test/resources/import-files/roles/00_create_realm_with_roles.json --import.force=true
2022-01-13 22:21:38.576  INFO 84715 --- [           main] d.a.k.config.KeycloakConfigApplication   : Starting KeycloakConfigApplication v4.5.1-SNAPSHOT using Java 11.0.13 on joe-nb with PID 84715 (/Users/jkr/IdeaProjects/keycloak-config-cli/target/keycloak-config-cli.jar started by jkr in /Users/jkr/IdeaProjects/keycloak-config-cli)
2022-01-13 22:21:38.578  INFO 84715 --- [           main] d.a.k.config.KeycloakConfigApplication   : The following profiles are active: dev
2022-01-13 22:21:39.033  INFO 84715 --- [           main] d.a.k.config.KeycloakConfigApplication   : Started KeycloakConfigApplication in 0.937 seconds (JVM running for 1.324)
2022-01-13 22:21:39.841  INFO 84715 --- [           main] d.a.k.config.KeycloakConfigRunner        : Importing file '/Users/jkr/IdeaProjects/keycloak-config-cli/src/test/resources/import-files/roles/00_create_realm_with_roles.json'
2022-01-13 22:21:40.180 DEBUG 84715 --- [           main] d.a.k.config.service.RealmImportService  : Updating realm 'realmWithRoles'...
2022-01-13 22:21:40.417 DEBUG 84715 --- [           main] d.a.k.c.service.ClientImportService      : No need to update client 'moped-client' in realm 'realmWithRoles'
2022-01-13 22:21:40.595 DEBUG 84715 --- [           main] d.a.k.config.service.RoleImportService   : Update realm-level role 'my_realm_role' in realm 'realmWithRoles'
2022-01-13 22:21:40.626 DEBUG 84715 --- [           main] d.a.k.config.service.RoleImportService   : Update client-level role 'my_client_role' for client 'moped-client' in realm 'realmWithRoles'
2022-01-13 22:21:40.813 DEBUG 84715 --- [           main] d.a.k.config.service.state.StateService  : Updated states of realm 'realmWithRoles'
2022-01-13 22:21:40.903 DEBUG 84715 --- [           main] d.a.k.c.s.checksum.ChecksumService       : Updated import checksum of realm 'realmWithRoles' to 'c51a94d6600b2f6737a474d6abb924564533092940b9b8eea310f712a6a66f5f'
2022-01-13 22:21:40.904  INFO 84715 --- [           main] d.a.k.config.KeycloakConfigRunner        : keycloak-config-cli running in 00:01.185.
jkr@joe-nb keycloak-config-cli % java -jar target/keycloak-config-cli.jar --spring.profiles.active=dev --keycloak.grant-type=client_credentials --keycloak.client-id=config-cli-master --keycloak.client-secret=config-cli-master-secret --import.path=src/test/resources/import-files/roles/00_create_realm_with_roles.json --import.force=true
2022-01-13 22:23:02.008  INFO 84747 --- [           main] d.a.k.config.KeycloakConfigApplication   : Starting KeycloakConfigApplication v4.5.1-SNAPSHOT using Java 11.0.13 on joe-nb with PID 84747 (/Users/jkr/IdeaProjects/keycloak-config-cli/target/keycloak-config-cli.jar started by jkr in /Users/jkr/IdeaProjects/keycloak-config-cli)
2022-01-13 22:23:02.010  INFO 84747 --- [           main] d.a.k.config.KeycloakConfigApplication   : The following profiles are active: dev
2022-01-13 22:23:02.450  INFO 84747 --- [           main] d.a.k.config.KeycloakConfigApplication   : Started KeycloakConfigApplication in 0.969 seconds (JVM running for 1.353)
2022-01-13 22:23:03.249  INFO 84747 --- [           main] d.a.k.config.KeycloakConfigRunner        : Importing file '/Users/jkr/IdeaProjects/keycloak-config-cli/src/test/resources/import-files/roles/00_create_realm_with_roles.json'
2022-01-13 22:23:04.564 DEBUG 84747 --- [           main] d.a.k.config.service.RealmImportService  : Updating realm 'realmWithRoles'...
2022-01-13 22:23:05.114 DEBUG 84747 --- [           main] d.a.k.c.service.ClientImportService      : No need to update client 'moped-client' in realm 'realmWithRoles'
2022-01-13 22:23:05.408 DEBUG 84747 --- [           main] d.a.k.config.service.RoleImportService   : Update realm-level role 'my_realm_role' in realm 'realmWithRoles'
2022-01-13 22:23:05.455 DEBUG 84747 --- [           main] d.a.k.config.service.RoleImportService   : Update client-level role 'my_client_role' for client 'moped-client' in realm 'realmWithRoles'
2022-01-13 22:23:05.731 DEBUG 84747 --- [           main] d.a.k.config.service.state.StateService  : Updated states of realm 'realmWithRoles'
2022-01-13 22:23:05.861 DEBUG 84747 --- [           main] d.a.k.c.s.checksum.ChecksumService       : Updated import checksum of realm 'realmWithRoles' to 'c51a94d6600b2f6737a474d6abb924564533092940b9b8eea310f712a6a66f5f'
2022-01-13 22:23:05.862  INFO 84747 --- [           main] d.a.k.config.KeycloakConfigRunner        : keycloak-config-cli running in 00:02.739

I can see, that the service account of the client has manage permissions granted to the created realm

image

ic2hrmk commented 2 years ago

Finally, I was able to find and reproduce the issue. I am sorry, I cannot share full source codes as I am not allowed to, but I'll add as many details as possible.

@jkroepke, my initial setup was a bit different from what you mentioned in the last comment. My docker image of the Keycloak contained exported realm saved in the master-realm.json. There is an exported master realm that would be imported each time the container is being created. Exported master realm already had credentials for the Keycloak Config CLI, so it was possible for Config CLI to authorize and create all the necessary resources. As per your comment:

Users with the create-realm role are allowed to create new realms. They will be granted full access to any new realm they create.

which added realm management opportunities to the Keycloak Config CLI to re-connect and update the created realm in the future.

The import of the master realm was done via Keycloak's standard functionality (https://www.keycloak.org/docs/latest/server_admin/#assembly-exporting-importing_server_administration_guide):

-Dkeycloak.migration.action=import
-Dkeycloak.migration.provider=singleFile 
-Dkeycloak.migration.file=master-realm.json

And this is the root cause of the issue! By default, the strategy was set to the OVERWRITE_EXISTING mode, and that leaded Keycloak to remove the management roles from the Keycloak Config CLI client which caused 403 error later!

Changing the -Dkeycloak.migration.strategy=IGNORE_EXISTING fixed the issue

Thank you so much for assisting with the debug session!