Thursday, September 15. 2022
Using jakartaee 10 security OIDC with wildfly 27 preview


Today a very quick entry about the jakarta security OpenID Connect is going to be presented. The new version 3.0 of the jakarta security spec allows as to configure an OpenID Connect authentication mechanism. The idea of this entry is testing the mechanism using keycloak as the authentication server and wildfly 27 preview (current alpha5 version) as the jakartaee 10 compatible server. This repository developed by hantsy will be used as the test application, specifically the folder called security-oidc which is a sample OIDC application.
The application defines a ProtectedServlet.java which has the OpenIdAuthenticationMechanismDefinition defined in it.
@OpenIdAuthenticationMechanismDefinition(
providerURI = "${openIdConfig.issuerUri}",
clientId = "${openIdConfig.clientId}",
clientSecret = "${openIdConfig.clientSecret}",
redirectURI = "${baseURL}/callback"
)
The configuration is loaded from a CDI Named (openIdConfig) bean which is defined in the class Auth0OpenIdConfig.java. This class in turn loads the final data from a file openid.properties located in the classpath. The file should be configured to specify the OpenID Connect server information (which will be the local keycloak installation).
Once the demo app has been explained (thanks hantsy!) all the detailed steps to make it run in the commented setup are presented.
Install keycloak and start it in dev mode.
wget https://github.com/keycloak/keycloak/releases/download/19.0.2/keycloak-19.0.2.zip unzip keycloak-19.0.2.zip cd keycloak-19.0.2/bin/ ./kc.sh start-dev
Go to the console (http://localhost:8080) and create the initial admin user. Then login with that user to the console.
Create a client in keycloak called wildfly in the master realm. Go to the Clients menu option and click Create client. Fill first form in the following way.
Click next and continue with the second form.
Click Save. In the Settings tab add the valid redirect URIs for the client (the wildfly server will run in port 8081 to not collide with keycloak).
And take note of the client secret which is displayed in the Credentials tab (this information will be needed to configure the openid.properties file in the application). The sample app requires a user called user. So let's create it. In the Users menu click the Add User button and fill the information.
Time to install the wildfly server. As previously commented it is started with an offset of 1 to use port 8081 (and avoid collision with keycloak).
wget https://github.com/wildfly/wildfly/releases/download/27.0.0.Alpha5/wildfly-preview-27.0.0.Alpha5.zip unzip wildfly-preview-27.0.0.Alpha5.zip cd wildfly-preview-27.0.0.Alpha5/bin/ ./add-user.sh -u admin -p admin ./standalone.sh -Djboss.socket.binding.port-offset=1
In order to build the application clone the repository and go to the security-oidc project.
git clone https://github.com/hantsy/jakartaee10-sandbox.git cd jakartaee10-sandbox/security-oidc
At this point we need to configure the file src/main/resources/openid.properties with our keycloak information. Note the secret was taken from a previous step.
domain=localhost:8080/realms/master clientId=wildfly clientSecret=4R9Akb1bCYc7TkW9up0cYUgBB9h4u5Mv
And finally I did some little modifications to fully set my environment for the application. Check this diff.
git diff diff --git a/pom.xml b/pom.xml index 2c53f21..28940ac 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> - <maven.compiler.release>17</maven.compiler.release> + <maven.compiler.release>11</maven.compiler.release> <!-- Official Maven Plugins --> vmaven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version> diff --git a/security-oidc/src/main/java/com/example/Auth0OpenIdConfig.java b/security-oidc/src/main/java/com/example/Auth0OpenIdConfig.java index 3326125..2c1da58 100644 --- a/security-oidc/src/main/java/com/example/Auth0OpenIdConfig.java +++ b/security-oidc/src/main/java/com/example/Auth0OpenIdConfig.java @@ -28,7 +28,7 @@ public class Auth0OpenIdConfig { domain = properties.getProperty("domain"); clientId = properties.getProperty("clientId"); clientSecret = properties.getProperty("clientSecret"); - issuerUri = "https://" + this.domain + "/"; + issuerUri = "http://" + this.domain + "/"; LOGGER.log( Level.INFO, "domain: {0}, clientId: {1}, clientSecret:{2}, issuerUri: {3}", diff --git a/security-oidc/src/main/java/com/example/CallbackServlet.java b/security-oidc/src/main/java/com/example/CallbackServlet.java index c739173..57dd4a2 100644 --- a/security-oidc/src/main/java/com/example/CallbackServlet.java +++ b/security-oidc/src/main/java/com/example/CallbackServlet.java @@ -23,7 +23,7 @@ public class CallbackServlet extends HttpServlet { LOGGER.log(Level.FINEST, "Enter callback servlet"); // response.getWriter().println(context.getAccessToken()); String referer = (String) request.getSession().getAttribute("Referer"); - String redirectTo = referer != null ? referer : "/protected"; + String redirectTo = referer != null ? referer : "/security-oidc-examples/protected"; LOGGER.log(Level.FINEST, "In /callback, redirect to: {0}", redirectTo); response.sendRedirect(redirectTo);
Java 11 (which is my default java) is used instead of 17, the issuerUri is http and not https (because keycloak was started in dev mode to not complicate the demo with certificates) and finally the application will be deployed normally from the war, so the context will be /security-oidc-examples (not deployed as the ROOT app).
One tricky point is that by default the elytron subsystem enforces the logged user to be in the default other domain (by default application users are placed in the application-users.properties file in wildfly). The integrated-jaspi option was set to false to avoid that. Now the logged users via jakartaee security will not be mapped to local elytron users.
./jboss-cli.sh --connect --controller=remote+http://localhost:9991 /subsystem=undertow/application-security-domain=other:write-attribute(name=integrated-jaspi, value=false) reload
Everything is OK to compile the sample app and deploy it to the server.
cd jakartaee10-sandbox/security-oidc mvn clean package cd ${JBOSS_HOME}/bin ./jboss-cli.sh --connect --controller=remote+http://localhost:9991 deploy /path/to/jakartaee10-sandbox/security-oidc/target/security-oidc-examples.war
You can now go the protected servlet http://localhost:8081/security-oidc-examples/protected and check performing a login into the app. The browser will be redirected to the keycloak login screen and, after a successful login with user, it will be redirected back to the app. The token information is displayed by the protected page in this application. I was about to record a video but it seems that my fedora laptop is not in the mood. So no video this time.
Click Create. In the Credentials tab set a password using Set password.

Click Save and Set password.
That's all folks! Today's entry is a quick review of the steps needed to configure jakartaee 10 OIDC security with keycloak and wildfly 27 preview. All these specs are very new so there will be issues for sure (note wildfly is a preview in alpha state, beta will be reached soon) but seeing it working is nice.
Brest regards!
Saturday, July 23. 2022
Creating a custom credential store for wildfly


The first step is downloading, installing and starting the last wildfly server.
wget https://github.com/wildfly/wildfly/releases/download/26.1.1.Final/wildfly-26.1.1.Final.zip unzip wildfly-26.1.1.Final.zip cd wildfly-26.1.1.Final/bin ./add-user.sh -u admin -p admin ./standalone.sh
The credential stores are just provided by a standard JavaSE Provider. So in order to create a custom implementation two classes should be developed.
The one that extends the CredentialStoreSpi. This class is the real implementation of the store, with methods like store, retrieve or getAliases to manage the secret values. The standard implementations are placed in this folder and, for example, the MapCredentialStore is a simple Map implementation which is a useful starting point.
But the Provider is also needed to register the previous implementation. Inside elytron the provider class is WildFlyElytronCredentialStoreProvider. It adds the services for the standard stores with the correct types, algorithms and implementation classes.
For the entry a simple credential store that saves the secrets in a properties file is going to be developed. But to make it a bit more interesting the values in the file are going to be encrypted using a PBE algorithm. The PBE key will be derived from the password passed to the credential store. This point is a bit weak and maybe it was better to use a hardcoded key, but I think that this way the entry is more educational. As always, the implementation is just an example you can extend, improve and adapt to your needs. The full maven project can be downloaded from here and it is also in my personal github account.
For details in the implementation just check the store class but let's show the provider constructor.
public CustomCredentialStoreProvider() { super("CustomCredentialStoreProvider", "0.1", "Custom CredentialStore Provider"); putService(new Service(this, "CredentialStore", "CustomCredentialStoreProvider", "es.rickyepoderi.ccs.CustomCredentialStore", Collections.
emptyList(), Collections. emptyMap())); } The class registers the implementation putting the service with type CredentialStore (it should be this value), algorithm is CustomCredentialStoreProvider (this name will be used later to configure the store inside wildfly) and the implementation class is es.rickyepoderi.ccs.CustomCredentialStore. No aliases or attributes are defined.
The final part of the coding process is adding a META-INF/services/java.security.Provider file that will make the JDK automatically detect the provider (ServiceLoader technique).
cat src/main/resources/META-INF/services/java.security.Provider es.rickyepoderi.ccs.CustomCredentialStoreProvider
So we have our custom credential store implementation ready. Just compile it and create the jar.
cd custom-credential-store mvn clean package
The final file will be place in the target directory.
Now the interesting part, using CLI add the library as a module inside wildfly.
module add --name=es.rickyepoderi.ccs --resources=/path/to/custom-credential-store/target/custom-credential-store-0.0.1.jar --dependencies=org.wildfly.security.elytron
The provider should be loaded into elytron to make it available to the server (if you remember I did the same with the Bouncy Castle FIPS jars in a previous entry).
/subsystem=elytron/provider-loader=ccs:add(module=es.rickyepoderi.ccs)
As the provider was defined in the service file no more arguments are needed. The provider class will be located automatically.
The custom credential can be configured at this point.
/subsystem=elytron/credential-store=ccs:add(providers=ccs, credential-reference={clear-text=XXXXXX}, location=custom.properties, relative-to=jboss.server.config.dir, type=CustomCredentialStoreProvider, create=true)
The type should be CustomCredentialStoreProvider (the algorithm registered inside the provider). The store will use the provided password (please here it is provided in plain which is a nonsense, as I commented this is just an example) to create a PBE key and the values will be encrypted with it. The underlying file is specified using location and relative-to options and placed inside the configuration folder of the server. By default the implementation encrypts values using PBEWithHmacSHA512AndAES_256 algorithm but it can be changed using the implementation-properties option. In general that option can pass custom attributes to the implementation and the example manages several of them you can try and play with.
Finally a secret is created inside the store ready to be used.
/subsystem=elytron/credential-store=ccs:add-alias(alias=alias1, secret-value=sa)
And if you check the properties file the alias will be there with the value encrypted.
grep alias1 ../standalone/configuration/custom.properties alias1=oDaesxvoSWLkiwWo89sjzA\=\=\:MFkwOAYJKoZIhvcNAQUMMCsEFGTaNHH+6TJiT6PrGPC/tByEaGIWAgIQAAIBIDAMBggqhkiG9w0CCwUAMB0GCWCGSAFlAwQBKgQQDzqM6mftjvjqCg49J2td7Q\=\=
That secret will be used to connect to the default H2 datasource that is defined in the wildfly server (that is why the secret value was sa).
batch /subsystem=datasources/data-source=ExampleDS:undefine-attribute(name=password) /subsystem=datasources/data-source=ExampleDS:write-attribute(name=credential-reference, value={store=ccs, alias=alias1}) run-batch reload
The server should be reloaded OK and the datasource should be able to connect to the H2 database.
/subsystem=datasources/data-source=ExampleDS:test-connection-in-pool { "outcome" => "success", "result" => [true] }
It works successfully!
That is all. The entry is a simple example to develop your own credential store. This is usually not needed and standard stores provided by wildfly should be enough for you. But, if for whatever reason, there is a specific requirement that forces you to implement a custom credential store, here you have an example. Remember my implementation uses the password provided to the store to derive a PBE key and encrypt values with it (it was done to show how to recover that password but maybe using a hadcoded key was a better idea). It is just a proof of concept. The entry details the implementation and the configuration of the store inside the server. Links to the maven project were available before, please use it with care.
Best regards!
Saturday, December 11. 2021
Wildfly bootable jar inside the OpenShift sandbox

Today's entry is going to continue the bootable jar series that was started some time ago. Red Hat currently offers the Developer Sandbox for OpenShift as a preconfigured environment of its cloud product which you can use for training or learning purposes. The sandbox needs a free Red Hat login and also requests your phone number to avoid resources overexploiting. Throughout the entry the same application that was initially used for the bootable jar solution will be adapted for the sandbox or, more generically, for OpenShift. My starting point was this good article about the maven OpenShift plugin, it explains how to use maven to deploy a wildfly application to OpenShift. As usual the full process is going to be detailed step by step.
Preparation for the sandbox
Once the sandbox is ready, log into the OpenShift console. The oc command tool needs to be downloaded and added to the system path. Click the question mark icon next to your login name and then the option Command line tools.

Download the command for your architecture, in my case the linux x86_64 option. Uncompress the file and put the oc binary in the path.
tar xvf oc.tar
mv oc ~/bin/
oc help
Now, again inside the console, click the option Copy login command and in the new page click on the DevSandbox button and then the Display Token Link. An oc login command is displayed that can be used to start working via terminal. That is more my style and now the command tool can also be used by the maven plugin.

oc login --token=xxx --server=https://api.sandbox-m2.xxxx.p1.openshiftapps.com:6443
oc whoami
rickyepoderi
oc project
Using project "rickyepoderi-dev" on server "https://api.sandbox-m2.xxxx.p1.openshiftapps.com:6443".
Keycloak/RH-SSO installation
The sample application was a jax-rs web services endpoint that used the keycloak plugin for security and swagger to document the API. So a keycloak server is needed in the deployment. As the sandbox is already provisioned with the Red Hat products the Red Hat Single Sign-On (RH-SSO, productivized version of the upstream keycloak project) can be directly installed.
There is a template called sso74-ocp4-x509-https which performs re-encryption to the https port and therefore no special certificates are needed for the configuration.
oc get templates -n openshift -o name | grep sso74-ocp4-x509-https
template.template.openshift.io/sso74-ocp4-x509-https
The server can be installed just passing the realm name and the admin username and password.
oc new-app --template=sso74-ocp4-x509-https -p SSO_REALM="ocp" -p SSO_ADMIN_USERNAME="admin" -p SSO_ADMIN_PASSWORD="xxxxx"
Wait the app to start and ask for the route used for the application. You can access the keycloak console in that https URL.
oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
sso sso-rickyepoderi-dev.apps.sandbox-m2.xxxx.p1.openshiftapps.com sso <all> reencrypt None
Preparing keycloak
The application will need several elements inside keycloak.
Once you are logged in the keycloak console, select the ocp realm and go to the roles tab.
Click Add to create the Users role.
That role is required to call the hello application endpoint because an authorization filter was developed to check for it.
Now time to create the sample user. Go to the users tab.
Fill the data for your user, in my case user ricky is created.
Set a password.
And assign the Users role to the created user.
Finally go to the clients tab and create one for the application.
Just a public client is needed named bootable-jar-sample and with the correct URL in the sandbox (you can change it later if the URL is not correct).
Application changes
The application is already implemented, but it needs to be modified to be deployed in the cloud.
First the keycloak configuration files should be modified to point to the new realm and URL in the sandbox. If you remember there are two keycloak.json files (one for the JavaScript part and another one for the Java/REST application, I used that trick for the app before).
- src/main/webapp/WEB-INF/keycloak.json
- src/main/webapp/keycloak.json
In the index.html the keycloak adapter is updated to download the JS source file from the sandbox URL.
And finally the interesting part, the pom.xml. All the versions were updated to the last one available, among them wildfly to 25.0.1 and the keycloak plugin to 15.0.2. In order to deploy on OpenShift, adding the <cloud/> tag to the wildfly-jar-maven-plugin is a must. That tag prepares the deployment to be cloud aware (adds microprofile health layer for checks, configure KUBE_PING if using clustering, assigns the pod name to the jboss.node.name,...).
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-jar-maven-plugin</artifactId> <version>6.1.1.Final</version> <configuration> <feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#25.0.1.Final</location> </feature-pack> <feature-pack> <groupId>org.keycloak</groupId> <artifactId>keycloak-adapter-galleon-pack</artifactId> <version>15.0.2</version> </feature-pack> </feature-packs> <layers> <layer>base-server</layer> <layer>logging</layer> <layer>jaxrs</layer> <layer>keycloak-client-oidc</layer> </layers> <cloud/> <!-- Remember this!!! --> </configuration> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin>
Now the openshift-maven-plugin is needed to create the build image, all the required kubernetes resources and deploy everything to the sandbox. This plugin is the other important piece that needs to be added to your application. The following configuration will use a NodePort service, and the route is configured to default edge (OpenShift will expose https externally but the pods will internally use plain 8080 port) and Redirect (external http will be redirected to https).
<plugin> <groupId>org.eclipse.jkube</groupId> <artifactId>openshift-maven-plugin</artifactId> <version>1.5.1</version> <executions> <execution> <goals> <goal>resource</goal> <goal>build</goal> </goals> </execution> </executions> <configuration> <enricher> <config> <jkube-service> <type>NodePort</type> </jkube-service> <jkube-openshift-route> <generateRoute>true</generateRoute> <tlsInsecureEdgeTerminationPolicy>Redirect</tlsInsecureEdgeTerminationPolicy> <tlsTermination>edge</tlsTermination> </jkube-openshift-route> </config> </enricher> </configuration> </plugin>
The only missing part is what image we start with, and that information is added using a global configuration property.
<properties> <jkube.generator.from>registry.redhat.io/ubi8/openjdk-11:latest</jkube.generator.from> </properties>
In the end the real work is performed by this plugin. Once the bootable jar is ready the openshift-maven-plugin adds the bootable jar to a openjdk-11 base and creates the final image for the application. Besides it creates the service, the deployment-config and the route resources for kubernetes. All the elements that are needed to incorporate that image to the cloud.
At this point we are one maven command away of running the application inside the sandbox. Just execute the following.
mvn oc:deploy
Demo
It is just one command, but it does a lot of things: first it compiles the sources; bundles the app into a war; creates the bootable jar file; then the new OpenShift plugin constructs the image with the application and all the resources; finally everything is uploaded and deployed into the sandbox. The following video shows exactly that. OpenShift is initially only running the keycloak server inside it. The maven command is executed and all the steps are performed. After waiting the deploy to finish and the pod is fully started, the route is accessed. The browser is redirected to log in the keycloak realm. After that the hello endpoint is executed successfully.
Summary
Today's entry explains how to adapt the bootable jar idea and use it in an OpenShift environment. The two main points are adding the cloud tag to the wildfly-jar-maven-plugin (it prepares the bootable jar for OpenShift) and incorporating the openshift-maven-plugin (it performs all the interaction with the sandbox, creating the image and resources and deploying them). The first part is simple, the second one is not so easy. The OpenShift plugin has lots of options that can be tweaked. The post used the most common ones, the application is deployed and exposed using https in the new route. The complete maven project for the demo app can be downloaded from here. It can be used and/or extended to explore more complex scenarios.
Regards from the cloud!
Comments