Docker Authentication with Keycloak

Docker Authentication Flow

Need to lock down your Docker registry?  Keycloak has you covered.

As of version 3.2.0, Keycloak has the ability to act as an “authorization service” for Docker authentication. This means that the Keycloak IDP server can perform identity validation and token issuance when a Docker registry requires authentication. Administrators may now leverage the same user base, audit controls, and configuration mechanisms in Keycloak to extend their SSO ecosystem past OpenID Connect and SAML to cover Docker registries. The chart below illustrates how this flow works:

Docker Authentication Flow

This article will walk through how to set up a local Keycloak IDP and Docker registry to demonstrate the Docker authentication capability. Note that the configuration used in this tutorial is for demonstration purposes only, and should never be run in a production environment. Also, be advised that Docker authentication remains a community-supported feature. It is not covered by a support subscription.

Configure the Keycloak Client and a Docker Registry

Begin by spinning up a Keycloak instance.  Note that the docker feature must be explicitly enabled:

docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=password jboss/keycloak -Dkeycloak.profile.feature.docker=enabled -b 0.0.0.0

Once the container boots up, open your web browser and head to the Keycloak admin console. For the sake of this demonstration, we’re going to use the master realm. However, in most real-world use cases, Docker registries will be configured against the primary realm or realms. Create a client for a Docker registry with the following steps. Don’t worry about having a registry container or server handy yet; we’ll get to that part in a bit.

  • Browse to the admin console at http://localhost:8080/auth/admin/master/console/and login with the credentials specified in the above command.
  • Choose Clients from the menu on the left side, and click the Create button.
  • Enter a meaningful name for ClientID, choose docker-v2 as the Client Protocol, and click Save.

Add Docker Client

A message will pop up indicating that the client was successfully created. At this point, the IDP is ready to interact with a Docker registry, so let’s spin one up. Thankfully, Docker Compose can automate the process of creating and configuring a Docker registry to interact with our IDP.

  • Navigate to the Installation tab
  • Choose Docker Compose YAML as the Format Option
  • Click Download.

Docker Compose Download

Save the .zip to the desired location, and then unzip the archive. After unzipping, the resulting directory should look like this:

keycloak-docker-compose-yaml
├── certs
│   ├── localhost.crt
│   ├── localhost.key
│   └── localhost_trust_chain.pem
├── data
├── docker-compose.yaml
└── README.md

From the keycloak-docker-compose-yaml directory, simply execute the following command to spin up a local registry:

docker-compose up

Test Authentication

Now that both the Keycloak IDP and the Docker registry have been configured and stood up locally, we can demonstrate authentication using the local Docker client. First, validate that the registry is protected by authentication:

# docker pull localhost:5000/busybox
Using default tag: latest
Error response from daemon: Get https://localhost:5000/v2/busybox/manifests/latest: unauthorized: Invalid username or password.

Note that the pull was unsuccessful because our client has not been authorized to access the registry. Now, log in with the same credentials previously used to gain access to the Keycloak console and observe a different message:

# docker login -u admin localhost:5000
Password: 
Login Succeeded

# docker pull localhost:5000/busybox
Using default tag: latest
Error response from daemon: manifest for localhost:5000/busybox:latest not found

Observe that a new error message is presented – namely that the manifest could not be found.  This is due to the container and data volume start with an empty registry. Simply tagging and uploading an image will resolve this error message:

# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
03b1be98f3f9: Pull complete 
Digest: sha256:3e8fa85ddfef1af9ca85a5cfb714148956984e02f00bec3f7f49d3925a91e0e7
Status: Downloaded newer image for busybox:latest 

# docker tag busybox:latest localhost:5000/busybox:latest

# docker push localhost:5000/busybox:latest
The push refers to a repository [localhost:5000/busybox]
6a749002dd6a: Pushed 
latest: digest: sha256:030fcb92e1487b18c974784dcc110a93147c9fc402188370fbfd17efabffc6af size: 527

# docker pull localhost:5000/busybox
Using default tag: latest
latest: Pulling from busybox
Digest: sha256:030fcb92e1487b18c974784dcc110a93147c9fc402188370fbfd17efabffc6af
Status: Image is up to date for localhost:5000/busybox:latest

The docker client can now perform operations against the registry, as it has authenticated against the Keycloak IDP Server.

For more information on how to use Keycloak with Docker, see the relevant sections in the server administration guide and the securing applications guide.


Whether you are new to Containers or have experience, downloading this cheat sheet can assist you when encountering tasks you haven’t done lately.

Share
  • Ralf Stubner

    Thank you for the useful article. Is it possible to restrict push- and pull-abilitiy to users with certain roles?

    • Josh Cain

      Hi Ralf, it sure is. So out-of-the-box, Keycloak is simply going to grant all requested scopes. However, if you look at the ‘Mappers’ tab for your docker registry client, you can further customize the response and do things like conditionally grant/deny certain scopes based on things like roles. At the moment, it would require you to extend the Authenticator SPI. Do you have a specific use case in mind? Would be happy to follow-up with an article and maybe try to get a general use case authenticator upstream.

      • Ralf Stubner

        Thanks Josh. A simple use case would be having two roles, say docker_push and docker_pull. Only users with role docker_push would be able to push images to the registry. Only users with role docker_pull would be able to pull images from this registry.