Keycloak-x specific extension documentation?

I’m working on porting several extensions in a project to run in the new Keycloak.X Docker image. Is anyone aware of documentation specific to the differences in how to install extensions?

A couple of specific things I’m looking at:

  1. Deploying modules. I used to put modules in /opt/jboss/keycloak/modules/system/layers/keycloak/foo/bar/main/foobar.jar and put the module.xml in /opt/jboss/keycloak/modules/system/layers/keycloak/foo/bar/main/module.xml. Now that this is gone? Where can I put modules?

  2. Naming modules and expressing their dependencies. I used to build module.xml files that named the module and listed the dependencies. Where do I do that now?

  3. EARs and third-party JARs. I used to build EAR files for extensions that required third-party JARs, and put those JARs in the lib directory of the EAR. It appears that EAR packaging is no longer supported. What is the recommended way to deploy extensions that require other libraries?

Would appreciate it if anyone has any guidance or templates. Thank you!

1 Like

As Quarkus is no Java/Jakarta EE runtime, there is no EAR support, of course. Just deploy them as regular JARs. Stian also mentioned this here: Keycloak.X: General feedback · Discussion #8655 · keycloak/keycloak · GitHub

Most of the times I create fat jars (with maven shade plugin or similar) which contain all necessary dependencies for the provider.

I didn’t have any experience with something that is a “module” in Wildfly in Keycloak.X environments, yet. Have you tried to place your JARs into one of the Quarkus lib folders?

Thanks @dasniko. I will try the fat-jar route for packaging, and a few spots for module deployment.

The thing I don’t have the first idea of is how to name modules (in the jar manifest?) in a way that they will be reference-able by other extensions. I used to do it in the module.xml as <module name="org.foo.my-module"..., but I’m not sure how to get keycloak to recognize it as a module. Maybe the route is to just deploy it with each extension fat-jar.

I’ll try a few things and report back here.

In Quarkus there is no such thing like a „module“. It just has to be regular JAR which will be made available to the classpath. Once it is there, it should be available for a provider (and any other custom code).

I’ve got something working that I wanted to share here to see if anyone sees something wrong with it, or can benefit from how I did it.

Description of the “legacy” keycloak setup:

  1. I have several extensions, packaged as jars, with overlapping 3rd party library and module dependencies
  2. I package the extensions as an .ear file, which contains all the 3rd party library jars, and an application.xml describing contents
  3. I build a docker image that
    a. puts the modules in /opt/jboss/keycloak/modules/system/layers/keycloak/, each with a module.xml describing the name, contents and dependencies.
    b. puts the .ear file in /opt/jboss/keycloak/standalone/deployments/

How I solved it for keycloak-x:

  1. I still “build” an unpacked version of the .ear file from above.
  2. I build a docker image that
    a. puts the extension jars from the unpacked .ear in /opt/keycloak/providers/
    b. puts the 3rd party library jars from the unpacked .ear in /opt/keycloak/lib/lib/main/
    c. puts the module jars in /opt/keycloak/lib/lib/main/

Notes:

  • As @dasniko indicated, it’s not a container in the same way Wildfly was, so it basically just loads everything on the same classpath.
  • I avoid conflicts, and don’t require building multiple fat-jars, by placing common dependencies in the common lib dir /opt/keycloak/lib/lib/main/
  • Module dependencies that used to be placed in my extensions jar manifests as Dependencies seem to get ignored. As long as something is on the classpath, it seems to work.
  • I have only tested this from a cold start, in a docker image.
4 Likes

Great news! Happy that you found a way :slight_smile:

Be careful if you follow this documentation (docker optimization):

COPY --from=builder /opt/keycloak/lib/quarkus/ /opt/keycloak/lib/quarkus/

You must copy the provider libraries to the build image and the final image to work.

Thanks for the tip. Here’s the Dockerfile that got me across the finish line:

FROM quay.io/keycloak/keycloak:17.0.0 as builder

ENV KC_METRICS_ENABLED=true
ENV KC_FEATURES=preview,admin2
ENV KC_DB=postgres
ENV KC_HTTP_RELATIVE_PATH=/auth
ENV KC_CACHE_CONFIG_FILE=cache-ispn-jdbc-ping.xml

# jdbc_ping infinispan configuration
COPY ./conf/cache-ispn-jdbc-ping.xml /opt/keycloak/conf/cache-ispn-jdbc-ping.xml

# custom keycloak.conf
COPY ./conf/keycloak.conf /opt/keycloak/conf/keycloak.conf

# local extensions
COPY ./target/bundle-1.0.0/*.jar /opt/keycloak/providers/

# 3rd party themes and extensions
COPY ./libs/*.jar /opt/keycloak/providers/

# keycloak-admin-client module
RUN curl -o /opt/keycloak/providers/org.keycloak.keycloak-admin-client-17.0.0.jar \
  https://repo1.maven.org/maven2/org/keycloak/keycloak-admin-client/17.0.0/keycloak-admin-client-17.0.0.jar && \
  chmod 664 /opt/keycloak/providers/org.keycloak.keycloak-admin-client-17.0.0.jar

RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:17.0.0

COPY --from=builder /opt/keycloak/lib/quarkus/ /opt/keycloak/lib/quarkus/
COPY --from=builder /opt/keycloak/providers/ /opt/keycloak/providers/

WORKDIR /opt/keycloak
RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore

ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"]
2 Likes