A guide for setting up Sigstore with Keycloak as an identity provider as in Sigstore the Hard Way.
This guide provides the steps for configuring Keycloak as an identity provider in the context of a Sigstore deployment on GCP as in Sigstore The Hard Way. It is a replacement for the step 5 of the tutorial where Dex is normally deployed as the OAuth2 server used for Sigstore signing.
Keycloak is an open source Identity and Access Management solution that provides OpenID Connect capabilities. The following steps assume that steps 1-4 from the Sigstore The Hard Way tutorial were successfully completed.
As in the step 2 of Sigstore the Hard Way, provision an instance for the Keycloak server:
gcloud compute instances create sigstore-keycloak \
--async \
--boot-disk-size 200GB \
--image-family debian-10 \
--image-project debian-cloud \
--machine-type e2-small \
--private-network-ip 10.240.0.10 \
--scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
--subnet sigstore \
--tags sigstore-the-hard-way-proj,sigstore-keycloak
When the instance is created, associate a static (IPv4) IP address to the instance. Allocate the IP address:
gcloud compute addresses create ADDRESS_NAME \
--region=REGION
Associate the IP address with your Keycloak instance:
gcloud compute instances add-access-config VM_NAME \
--access-config-name="ACCESS_CONFIG_NAME" --address=IP_ADDRESS
If not done already, configure a keycloak
subdomain for your domain name (for example: keycloak.example.com
).
Follow the same steps as in the Domain Configuration section of STHW for the keycloak
subdomain.
In the Google Cloud console, ensure the subdomain resolves to the reserved static IP address configured by going to Network Services
> Cloud DNS
and selecting your zone.
Select the corresponding subdomain and verify that the IP address is correct.
To start deploying Keycloak, ssh into your instance:
gcloud compute ssh sigstore-keycloak
If not installed, get wget
and unzip
:
sudo apt-get update -y
sudo apt-get install wget unzip
Note: This guide shows how to deploy Keycloak as a standalone server, but it is also possible to start the server as a container.
Go to the Keycloak downloads page and select the download URL for the latest version. This tutorial uses the version 21.0.2 of Keycloak. Get the zip file on your instance:
wget https://github.com/keycloak/keycloak/releases/download/21.0.2/keycloak-21.0.2.zip
(Or URL with the desired version)
Decompress the file:
unzip keycloak-21.0.2.zip
Follow the same steps from STHW to configure TLS with Let's Encrypt for your domain, replacing the $DOMAIN
name with your keycloak
subdomain.
The HAproxy configuration is similar to the one described in the tutorial, except for the haproxy.cfg
file that must override request headers to work with Keycloak.
In place of the file from the tutorial, write the following configuration for haproxy.cfg
(replacing 10.240.0.12
with your instance internal IP address):
defaults
timeout connect 10s
timeout client 30s
timeout server 30s
log global
mode http
option httplog
maxconn 3000
log 127.0.0.1 local0
frontend haproxy
#public IP address
bind 10.240.0.12:80
bind 10.240.0.12:443 ssl crt /etc/ssl/private/keycloak.example.com.pem
# HTTPS redirect
redirect scheme https code 301 if !{ ssl_fc }
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt-backend if letsencrypt-acl
http-request redirect scheme https unless { ssl_fc }
default_backend sigstore_keycloak
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
backend sigstore_keycloak
http-request redirect scheme https unless { ssl_fc }
server sigstore_keycloak_internal 10.240.0.12:8080
backend letsencrypt-backend
server certbot_internal 127.0.0.1:9080
Be careful of writing the correct headers and of specifying the correct port number (8080) for the Keycloak server, where the HTTPS traffic will be redirected. The template file is also available in this repository.
As for the rest of the tutorial section, move the configuration file under /etc/haproxy/
and check the file syntax:
sudo /usr/sbin/haproxy -c -V -f /etc/haproxy/haproxy.cfg
Then enable and restart the haproxy
systemd service:
sudo systemctl enable haproxy.service
sudo systemctl restart haproxy.service
Verify that the service is running:
sudo systemctl status haproxy.service
Go to the keycloak-{version number}
directory extracted from step 3.1.
To intialize the admin user, set the KEYCLOAK_ADMIN
and KEYCLOAK_ADMIN_PASSWORD
environment variables.
Start the Keycloak server with the following command:
sudo bin/kc.sh start \
--https-certificate-file=/etc/letsencrypt/live/keycloak.example.com/cert.pem \
--https-certificate-key-file=/etc/letsencrypt/live/keycloak.example.com/privkey.pem \
--hostname-strict=false \
--proxy=edge \
--https-port=8080
For more information about configuring Keycloak behind a reverse proxy, refer to the documentation.
Within a few seconds, the Keycloak server should be running and listening on port 8080 (default port) for HTTPS requests:
2023-04-03 08:42:49,783 INFO [org.keycloak.quarkus.runtime.hostname.DefaultHostnameProvider] (main) Hostname settings: Base URL: <unset>, Hostname: <request>, Strict HTTPS: false, Path: <request>, Strict BackChannel: false, Admin URL: <unset>, Admin: <request>, Port: -1, Proxied: true
2023-04-03 08:42:54,952 WARN [io.quarkus.agroal.runtime.DataSources] (main) Datasource <default> enables XA but transaction recovery is not enabled. Please enable transaction recovery by setting quarkus.transaction-manager.enable-recovery=true, otherwise data may be lost if the application is terminated abruptly
2023-04-03 08:42:58,216 INFO [org.infinispan.SERVER] (keycloak-cache-init) ISPN005054: Native IOUring transport not available, using NIO instead: io.netty.incubator.channel.uring.IOUring
2023-04-03 08:42:58,608 WARN [org.infinispan.PERSISTENCE] (keycloak-cache-init) ISPN000554: jboss-marshalling is deprecated and planned for removal
2023-04-03 08:42:58,729 WARN [org.infinispan.CONFIG] (keycloak-cache-init) ISPN000569: Unable to persist Infinispan internal caches as no global state enabled
2023-04-03 08:42:58,773 INFO [org.infinispan.CONTAINER] (keycloak-cache-init) ISPN000556: Starting user marshaller 'org.infinispan.jboss.marshalling.core.JBossUserMarshaller'
2023-04-03 08:42:59,121 WARN [io.quarkus.vertx.http.runtime.VertxHttpRecorder] (main) The X-Forwarded-* and Forwarded headers will be considered when determining the proxy address. This configuration can cause a security issue as clients can forge requests and send a forwarded header that is not overwritten by the proxy. Please consider use one of these headers just to forward the proxy address in requests.
2023-04-03 08:42:59,766 INFO [org.keycloak.broker.provider.AbstractIdentityProviderMapper] (main) Registering class org.keycloak.broker.provider.mappersync.ConfigSyncEventListener
2023-04-03 08:43:00,062 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000088: Unable to use any JGroups configuration mechanisms provided in properties {}. Using default JGroups configuration!
2023-04-03 08:43:00,238 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000078: Starting JGroups channel `ISPN`
2023-04-03 08:43:00,256 INFO [org.jgroups.JChannel] (keycloak-cache-init) local_addr: 8fddd816-2172-40bb-9d45-76b6ba487add, name: sigstore-keycloak-12281
2023-04-03 08:43:00,262 WARN [org.jgroups.protocols.UDP] (keycloak-cache-init) JGRP000015: the send buffer of socket MulticastSocket was set to 1MB, but the OS only allocated 212.99KB
2023-04-03 08:43:00,263 WARN [org.jgroups.protocols.UDP] (keycloak-cache-init) JGRP000015: the receive buffer of socket MulticastSocket was set to 20MB, but the OS only allocated 212.99KB
2023-04-03 08:43:00,264 WARN [org.jgroups.protocols.UDP] (keycloak-cache-init) JGRP000015: the send buffer of socket MulticastSocket was set to 1MB, but the OS only allocated 212.99KB
2023-04-03 08:43:00,265 WARN [org.jgroups.protocols.UDP] (keycloak-cache-init) JGRP000015: the receive buffer of socket MulticastSocket was set to 25MB, but the OS only allocated 212.99KB
2023-04-03 08:43:00,277 INFO [org.jgroups.protocols.FD_SOCK2] (keycloak-cache-init) server listening on *.27882
2023-04-03 08:43:02,293 INFO [org.jgroups.protocols.pbcast.GMS] (keycloak-cache-init) sigstore-keycloak-12281: no members discovered after 2010 ms: creating cluster as coordinator
2023-04-03 08:43:02,353 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000094: Received new cluster view for channel ISPN: [sigstore-keycloak-12281|0] (1) [sigstore-keycloak-12281]
2023-04-03 08:43:02,365 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000079: Channel `ISPN` local address is `sigstore-keycloak-12281`, physical addresses are `[10.240.0.12:43418]`
2023-04-03 08:43:03,245 INFO [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: sigstore-keycloak-12281, Site name: null
2023-04-03 08:43:05,091 INFO [io.quarkus] (main) Keycloak 21.0.2 on JVM (powered by Quarkus 2.13.7.Final) started in 20.026s. Listening on: http://0.0.0.0:8080 and https://0.0.0.0:8080
2023-04-03 08:43:05,091 INFO [io.quarkus] (main) Profile prod activated.
2023-04-03 08:43:05,091 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, logging-gelf, micrometer, narayana-jta, reactive-routes, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, vertx]
Once the Keycloak server is started, go to your Keycloak domain on your local machine browser.
Connect as the admin user by going to the Administration Console
and entering the configured admin username and password.
Create the sigstore
realm by clicking on Create realm
in the top left menu, then go to the newly created realm.
In the Clients
section, select Create client
.
In General settings
, select OpenID Connect
for the client type, and sigstore
for the client ID.
In Capability config
, set Client authentication
to On
and check the Standard Flow
and Service Account Roles
boxes.
In Login settings
, set the Root URL
and Home URL
to the base domain URL (i.e. https://keycloak.example.com
), and Valid redirect URIs
to *
.
Select Save
.
Go to the created sigstore
client.
Add expected claims to the identity token
The steps below allow to set the claims expected by Sigstore in the OIDC tokens generated by Keycloak.
In the Client scopes
tab, go to sigstore-dedicated
and add email
and email_verified
by selecting Add mapper
> From predefined mapper
and checking the corresponding boxes.
Then select Add mapper
> By configuration
> Hardcoded claim
, and enter the following values:
Name
:audience
Token Claim Name
:aud
Claim value
:sigstore
Set Add to ID token
as On
.
Note: In the Credentials
section for the client, you will find the OIDC client secret you can pass to Sigstore to get an identity token. The client ID must always be sigstore
.
In the Users
tab, select Add user
.
Leave Required user actions
blank, add sigstore
as a username and enter the email you want to sign artifacts with. Set Email verified
to On
.
Then go to the Credentials
tab and configure a password for the sigstore
user.
Log out from the admin console.
The next step is to add your Keycloak server as a recognized identity provider to your Fulcio instance configuration. For the Fulcio server to start, it is necessary that all the identity providers specified in the configuration are up and running.
SSH into your Fulcio instance:
gcloud compute ssh sigstore-fulcio
Follow the steps from the section 6 of STHW to configure the Fulcio instance before the Fulcio Config part.
Insert the configuration for your Keycloak server in config.json
:
"https://keycloak.example.com/realms/sigstore": {
"IssuerURL": "https://keycloak.example.com/realms/sigstore",
"ClientID": "sigstore",
"Type": "email"
}
Note: It is important to have the exact same URL between the one in the config and the one that will be passed to Sigstore to sign artifacts. Trailling slashes should also match.
Make sure your Keycloak instance is reachable at the configured URL and start the Fulcio server as specified in the tutorial.
Your custom Sigstore instance should now be up and ready for use.
To verify if everything has been set up correctly, follow the cosign set up from STHW step 10 and run cosign sign
with options as follows:
COSIGN_EXPERIMENTAL=1 cosign sign --fulcio-url https://fulcio.example.com --oidc-issuer https://keycloak.example.com/realms/sigstore --rekor-url https://rekor.example.com <IMAGE>
Note: the COSIGN_EXPERIMENTAL=1
environment variable should not be necessary anymore from cosign v2.0.0.
A browser window should open to redirect you to the Keycloak login page.
Log in as the sigstore
user and enter the password configured at step 4.3. You should be redirected to the Sigstore authentication successful
window.
Verify the command output to see if the container image has been signed successfully.
To report any bug, issue or improvement proposal to this tutorial, please open an issue describing your request in the repository.
This tutorial is an extension to the Sigstore the Hard Way tutorial available at https://sthw.decodebytes.sh/.