Issues accessing keycloak from docker-compose

I am running the keycloak in docker compose

services:
  postgres:
    image: postgres:latest
    container_name: postgres_db
    volumes:
      - keyclock_postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password

  keycloak_web:
    image: keycloak/keycloak:latest
    container_name: keycloak_web
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password

      KC_HOSTNAME: localhost
      KC_HOSTNAME_STRICT: false
      KC_HOSTNAME_STRICT_HTTPS: false

      KC_LOG_LEVEL: info
      KC_METRICS_ENABLED: true
      KC_HEALTH_ENABLED: true
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
    command: start-dev
    depends_on:
      - postgres
    ports:
     - "7080:8080"
     - "3002:8443"

volumes:
  keyclock_postgres_data:

then in browser I can access the dashboard from link http://localhost:7080

The problem arises when I try to to use keycloak from within my dotnet application, which also is running in docker… The config is self-explanatory. You can ignore the code itself. Just take a look at the links.

builder.Services.AddAuthentication()
    .AddJwtBearer("KeycloakBearer", options =>
    {
        options.Authority = "http://host.docker.internal:7080/realms/master"; 
        options.Audience = "account";
        options.RequireHttpsMetadata = false;
        options.MetadataAddress = "http://host.docker.internal:7080/realms/master/.well-known/openid-configuration";

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidIssuers = new[]
            {
                "http://localhost:7080/realms/master",
                "http://host.docker.internal:7080/realms/master"
            }
        };
    });

see that I am using the host.docker.internal as a hostname. I can confirm that the page http://host.docker.internal:7080/realms/master/.well-known/openid-configuration is accessible by the web service in docker container… I cannot use the usual localhost/

The problem is that the app is not able to perform the signing key verification!.

If you are curious about the error details, it’s the:

Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.

Basically I think that it means that the service cannot properly access the public key when validating the JWT token.

After days of struggle I’ve found the culprit. It’s the

      KC_HOSTNAME: localhost

environment variable. If I change the value to

      KC_HOSTNAME: host.docker.internal

then the JWT validation in my service starts working just fine!

But then I have a problem that I can no longer access the keycloak dashboard using the localhost domain from host machine!

Could someone please clarify how to make both the dashboard accessible, as well as allowing the services running in docker to perform their JWT validations?

I was thinking of somehow allowing the keycloak to run on multiple hostnames at the same time but it seems like the environment variable is allowing a single value… Nor aliases are allowed…

Should I try configuring an nginx reverse proxy of some sort to make it all work?

Any help is appreciated.

Hey just wanted to paste an update on this issue…

it turned out that the issue resolves itself after removing the hostname (KC_HOSTNAME) environment variable!

I’ve never tried it that way.

Now both the host system is able to access the dashboard using the localhost AND also an app in docker is able to request the public key from host.docker.internal hostname…

if there is anybody here who can explain this behavior please do.

To me it looks like the keycloak is performing the request validation based on the hostname. And if the variable is not present then validation is omited…

Few comments:

  • Running docker and testing locally can be confusing af. Understanding why may be even worse.
  • It would have made my life easier if I could see how you set up the dotnet docker in your docker-compose. Since you didn’t, I can only make some guesses.
  • More context on your dotnet app would help helping you because I have no idea what auth flow are we looking at here.

Here’s what I believe has happened:
Your dotnet signature verification has failed because you lost the security context for the session between the dotnet app and keycloak. Reminder: security context can only exist if EITHER the protocol uses TLS (i.e. https) OR if communications is between localhost to localhost. Your use of host.docker.internal broke that.

The solution will involve getting acquainted with two new docker concepts: extra_hosts and host-gateway.

This may not solve your issue, but may give you a direction, because it allowed me to set up a similar testing environment like yours:

  1. Use hostname v2 instead of v1: KC_HOSTNAME: http://localhost:7080
  2. Add an entry to your etc/host config with a new hostname named hostgateway pointing to 127.0.0.1
  3. Add the following docker settings to both your keycloak docker and dotnet docker in your compose yaml:
  extra_hosts:
      - hostgateway:host-gateway
  1. use "http://hostgateway:7080...." instead of "http://host.docker.internal:7080..." in your dotnet application

And now, to help you fall sleep, here’s some context: using localhost inside a docker ends up only within that docker itself, while using localhost in the browser of the docker host, can reach all local dockers. That’s why the address localhost:7080 in your dotnet app won’t be answered because there’s nothing on this address. However, for the dotnet app, the address host.docker.internal:7080 goes outside the dotnet docker, to the docker host, and then back - but loses the security context. Your browser, on the other hand, could easily get to localhost:7080 because that will hit the docker host which will route it correctly to the keycloak docker.