dockerldapjbpmkie-serverelytron

Setting up LDAP with jBPM (Kie Server) 7.74.1.Final in Docker


I have setup jBPM by using the following docker image: jboss/jbpm-server-full:latest

Setting up LDAP

After following the steps mentioned in https://blog.kie.org/2021/02/migrating-jbpm-images-secured-by-ldap-to-elytron.html (incl. modifying the kie-server.war file with the two ldap user property files).

Situation

I finally got the authentication so far, that I replaced the old OTHER authentication mechanism by the new KieLdap and when I login with a correct LDAP username (ie. "ldapuser") as well as correct password, I am presented with a different error view as when I am login with an inexistent user or with an incorrect password:

Login failed: Not Authorized

Login as another User

This means for me: Login is theoretically working!

On the LDAP server (FreeIPA), I see the request by jBPM for BINDing to the user as well as getting all the groups which the user is a member of.

Performing the user auth:

[15/Jan/2024:10:41:01.226351348 +0100] conn=19193 op=0 BIND dn="uid=tech_jbpm,cn=users,cn=accounts,dc=example,dc=ch" method=128 version=3
[15/Jan/2024:10:41:01.229601791 +0100] conn=19193 op=0 RESULT err=0 tag=97 nentries=0 wtime=0.000385959 optime=0.003256677 etime=0.003636806 dn="uid=tech_jbpm,cn=users,cn=accounts,dc=example,dc=ch"
[15/Jan/2024:10:41:01.230835297 +0100] conn=19193 op=1 SRCH base="cn=users,cn=accounts,dc=example,dc=ch" scope=1 filter="(uid=ldapuser)" attrs="1.1"
[15/Jan/2024:10:41:01.231469269 +0100] conn=19193 op=1 RESULT err=0 tag=101 nentries=1 wtime=0.000181602 optime=0.000634202 etime=0.000810154
[15/Jan/2024:10:41:01.232423161 +0100] conn=19193 op=2 UNBIND

Getting the groups of the user:

[15/Jan/2024:10:41:01.235885094 +0100] conn=19194 op=0 BIND dn="uid=tech_jbpm,cn=users,cn=accounts,dc=example,dc=ch" method=128 version=3
[15/Jan/2024:10:41:01.239461648 +0100] conn=19194 op=0 RESULT err=0 tag=97 nentries=0 wtime=0.000513500 optime=0.003582121 etime=0.004090225 dn="uid=tech_jbpm,cn=users,cn=accounts,dc=example,dc=ch"
[15/Jan/2024:10:41:01.240776989 +0100] conn=19194 op=1 SRCH base="cn=users,cn=accounts,dc=example,dc=ch" scope=1 filter="(uid=ldapuser)" attrs="1.1"
[15/Jan/2024:10:41:01.242193465 +0100] conn=19194 op=1 RESULT err=0 tag=101 nentries=1 wtime=0.000520836 optime=0.001418289 etime=0.001932418
[15/Jan/2024:10:41:01.242759072 +0100] conn=19194 op=2 SRCH base="cn=groups,cn=accounts,dc=example,dc=ch" scope=2 filter="(member=uid=ldapuser,cn=users,cn=accounts,dc=example,dc=ch)" attrs=" cn"
[15/Jan/2024:10:41:01.253362800 +0100] conn=19194 op=2 RESULT err=0 tag=101 nentries=26 wtime=0.000225495 optime=0.010595889 etime=0.010815564
[15/Jan/2024:10:41:01.256216795 +0100] conn=19194 op=3 UNBIND

DEBUGGING

I tried to enable debugging of the Elytron Security module by adding the following logger to the standalone.xml file:

            <logger category="org.wildfly.security">
                <level name="DEBUG"/>
                <handlers>
                    <handler name="CONSOLE"/>
                </handlers>
            </logger>

But no output is generated on authenticating a user.

There are some errors, which were inexistent prior to the configuration of the LDAP:

jbpm  | 10:08:35,421 INFO  [org.kie.server.controller.websocket.client.WebSocketKieServerControllerImpl] (KieServer-ControllerConnect) Kie Server points to non Web Socket controller 'http://localhost:8080/business-central/rest/controller', using default REST mechanism
jbpm  | 10:08:35,666 WARN  [org.kie.server.services.impl.controller.DefaultRestControllerImpl] (KieServer-ControllerConnect) Exception encountered while syncing with controller at http://localhost:8080/business-central/rest/controller/server/sample-server error Error while sending PUT request to http://localhost:8080/business-central/rest/controller/server/sample-server response code 405

How can I further debug this problem, without any working log output of the authentication?

Documentation

All performed LDAP setup steps are documented below:

# Blogs: https://blog.kie.org/2021/02/migrating-jbpm-images-secured-by-ldap-to-elytron.html, https://www.mastertheboss.com/jbossas/jboss-security/configuring-ldap-based-authentication-with-elytron-on-wildfly/

FULL MIGRATION
In the case of kie-server-showcase image, only the kie-server.war is present (no KieLoginModule dependencies) and therefore, it’s possible to make a full migration to Elytron.

Elytron is based on a security-domain concept,  in other words, on the representation of a security policy. It is backed by security-realm/s, and resources to make transformations (role-decoder, permission-mapper and others).

In this practical example, we are going to use Elytron LDAP Security Realm to access LDAP backend and verify credentials as well as obtain attributes associated with an identity.

More complex scenarios would allow having several security realms, and by means of a security-mapper, determine which attributes would be retrieved from each security realm.

0. Modify the kie-server.war file.
docker cp jbpm:/opt/jboss/wildfly/standalone/deployments/kie-server.war ./

Unpack the war on an ubuntu machine.
scp root@jbpm.example.ch:/opt/jbpm/kie-server.war ./

Then add these two files:

nano kie-server/WEB-INF/classes/jbpm.user.info.properties

ldap.user.ctx=cn\=users,cn\=accounts,dc\=example,dc\=ch
ldap.role.ctx=cn\=groups,cn\=accounts,dc\=example,dc\=ch
ldap.user.filter=(uid\={0})
ldap.role.filter=(cn\={0})

nano kie-server/WEB-INF/classes/jbpm.usergroup.callback.properties

ldap.user.ctx=cn\=users,cn\=accounts,dc\=example,dc\=ch
ldap.role.ctx=cn\=groups,cn\=accounts,dc\=example,dc\=ch
ldap.user.roles.ctx=cn\=groups,cn\=accounts,dc\=example,dc\=ch
ldap.user.filter=(uid\={0})
ldap.role.filter=(cn\={0})
ldap.user.roles.filter=(member\={0})
ldap.bind.user=uid\=tech_jbpm,cn\=users,cn\=accounts,dc\=example,dc\=ch
ldap.bind.pwd=9sdvYxGksGRX7stug7
java.naming.provider.url=ldap://10.1.20.10:389

Repack the kie-server.war and upload & mount it on the server:
jar -cvf kie-server.war *
scp ./kie-server.war  root@jbpm.example.ch:/opt/jbpm/kie-server.war

1.- First, let’s remove the security-domain called other at legacy security subsystem, as it will be no longer used:
[root@c01-lxc-jbpm-001 jbpm]# docker exec -it jbpm bash

[jboss@5a3ffa8318d2 bin]$ bash jboss-cli.sh
[standalone@localhost:9990 /] connect
[standalone@localhost:9990 /] /subsystem=undertow/application-security-domain=other:remove
[standalone@localhost:9990 /] /subsystem=ejb3/application-security-domain=other:remove

Then reload Kie Server:
[standalone@localhost:9990 /] reload

And connect again:
[standalone@localhost:9990 /] connect

3.- Define the directory context to connect with LDAP and the LDAP Realm into Elytron:
[standalone@localhost:9990 /] /subsystem=elytron/dir-context=ldap-connection:add(url=ldap://10.10.0.10:389, principal="uid=tech_jbpm,cn=users,cn=accounts,dc=example,dc=ch", credential-reference={clear-text="secret"})
[standalone@localhost:9990 /] /subsystem=elytron/ldap-realm="KieLdap":add(dir-context=ldap-connection, \
direct-verification=true, \
identity-mapping={search-base-dn="cn=users,cn=accounts,dc=example,dc=ch", \
rdn-identifier="uid", \
attribute-mapping=[{filter-base-dn="cn=groups,cn=accounts,dc=example,dc=ch",filter="(member=uid={0},cn=users,cn=accounts,dc=example,dc=ch)",from="cn",to="Roles"}]})

Notice that the LDAP connection needs the principal (bindDN) and its password as the used LDAP server doesn’t allow anonymous binding.
Retrieved roles are mapped from “cn” to “Roles”, where the RoleDecoder will take them.
This RoleDecoder component (as its name indicates) is in charge of decoding user’s roles.
Our simple-role-decoder (from-roles-attribute) is pretty straightforward: roles are obtained directly from the attribute “Roles”.
<simple-role-decoder name="from-roles-attribute" attribute="Roles"/>

4.- Create the security domain in Elytron, named KIEDomain, (any name is valid, as we will map it later to the one defined at application level) and add it the previous LDAP realm, and the default-permission-mapper:
[standalone@localhost:9990 /] /subsystem=elytron/security-domain=KIEDomain:add(realms=[{realm=KieLdap,role-decoder=from-roles-attribute}], default-realm="KieLdap", permission-mapper=default-permission-mapper)


5.- Next, we need to define the HTTP authentication factory: for kie-server, it’s needed to link the mechanisms for BASIC and FORM authentications:
[standalone@localhost:9990 /] /subsystem=elytron/http-authentication-factory=ldap-http-auth:add(http-server-mechanism-factory=global,security-domain=KIEDomain,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=KieLdap}]}, {mechanism-name=FORM}])

6.- Map the application security domain (other, as it is the one specified at jboss-web.xml) to our Elytron security domain (KIEDomain) for the undertow and ejb3 subsystems:
[standalone@localhost:9990 /] /subsystem=undertow/application-security-domain=other:add(security-domain=KIEDomain)
[standalone@localhost:9990 /] /subsystem=ejb3/application-security-domain=other:add(security-domain=KIEDomain)

7.- Update the messaging-activemq (JMS) to point to our Elytron security domain (KIEDomain) and undefine (remove) the default security domain given by WildFly:
[standalone@localhost:9990 /] /subsystem=messaging-activemq/server=default:write-attribute(name=elytron-domain, value=KIEDomain)
[standalone@localhost:9990 /] /subsystem=messaging-activemq/server=default:undefine-attribute(name=security-domain)

8.- Disable JACC from legacy security subsystem and enable it at elytron by adding the default policy:
Does not work: [standalone@localhost:9990 /] /subsystem=security:write-attribute(name=initialize-jacc, value=false)


[standalone@localhost:9990 /] /subsystem=elytron/policy=jacc:add(jacc-policy={})

[standalone@localhost:9990 /] reload
[standalone@localhost:9990 /] connect

Update 17.01.2024

After enabling debugging by using the configuration from the first answer, I get the following log output in server.log. There are only authentication successes in the log, even though the login to the kie-server still does not work.

2024-01-17 18:51:38,571 DEBUG [org.wildfly.security] (default task-1) Context [javax.naming.ldap.InitialLdapContext@1576a899] was closed. Connection closed or just returned to the pool.
2024-01-17 18:51:38,571 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [ldapuser] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2024-01-17 18:51:38,573 TRACE [org.wildfly.security] (default task-1) Authorizing principal ldapuser.
2024-01-17 18:51:38,574 TRACE [org.wildfly.security] (default task-1) Authorizing against the following attributes: [Roles] => [ipausers, employees, teamleads, vpn, app_kimai_teamlead, app_grafana_editor, team_mgmt, app_swissd_user, team_office_lead, team_cybsec_lead, team_devops_lead, team_sysops_lead, app_cockpit_admin, ma_bills, ma_tech, role_cyb, role_hr, role_sys, role_soc, role_dev, role_mgt, role_sales, role_mktg, user, admin, kie-server]
2024-01-17 18:51:38,575 TRACE [org.wildfly.security] (default task-1) Authorizing against the following runtime attributes: [] => []
2024-01-17 18:51:38,575 TRACE [org.wildfly.security] (default task-1) Permission mapping: identity [ldapuser] with roles [] implies ("org.wildfly.security.auth.permission.LoginPermission" "") = true
2024-01-17 18:51:38,575 TRACE [org.wildfly.security] (default task-1) Authorization succeed
2024-01-17 18:51:38,576 TRACE [org.wildfly.security] (default task-1) Handling CachedIdentityAuthorizeCallback: principal = ldapuser  authorizedIdentity = SecurityIdentity{principal=ldapuser, securityDomain=org.wildfly.security.auth.server.SecurityDomain@e3b9f68, authorizationIdentity=EMPTY, realmInfo=RealmInfo{name='KieLdap', securityRealm=org.wildfly.security.auth.realm.ldap.LdapSecurityRealm@1fa010d8}, creationTime=2024-01-17T18:51:38.571413Z}
2024-01-17 18:51:38,577 DEBUG [org.wildfly.security.http.form] (default task-1) User [ldapuser] authenticated successfully
2024-01-17 18:51:38,577 TRACE [org.wildfly.security] (default task-1) Handling AuthenticationCompleteCallback: succeed
2024-01-17 18:51:38,577 TRACE [org.wildfly.security] (default task-1) Handling SecurityIdentityCallback: identity = SecurityIdentity{principal=ldapuser, securityDomain=org.wildfly.security.auth.server.SecurityDomain@e3b9f68, authorizationIdentity=EMPTY, realmInfo=RealmInfo{name='KieLdap', securityRealm=org.wildfly.security.auth.realm.ldap.LdapSecurityRealm@1fa010d8}, creationTime=2024-01-17T18:51:38.571413Z}
2024-01-17 18:51:38,577 TRACE [org.wildfly.security.http.form] (default task-1) User redirected to original path [http://jbpm.example.ch/business-central/kie-wb.jsp]
2024-01-17 18:51:38,578 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [ldapuser] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2024-01-17 18:51:38,638 TRACE [org.wildfly.security.http.servlet] (default task-1) Created ServletSecurityContextImpl enableJapi=true, integratedJaspi=true, applicationContext=default-host /business-central
2024-01-17 18:51:38,638 TRACE [org.wildfly.security.http.servlet] (default task-1) No AuthConfigProvider for layer=HttpServlet, appContext=default-host /business-central
2024-01-17 18:51:38,638 TRACE [org.wildfly.security.http.servlet] (default task-1) JASPIC Unavailable, using HTTP authentication.
2024-01-17 18:51:38,638 TRACE [org.wildfly.security] (default task-1) No CachedIdentity to restore.
2024-01-17 18:51:38,638 TRACE [org.wildfly.security] (default task-1) Created HttpServerAuthenticationMechanism [org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$1@51543146] for mechanism [BASIC]
2024-01-17 18:51:38,639 TRACE [org.wildfly.security] (default task-1) Created HttpServerAuthenticationMechanism [org.wildfly.security.auth.server.SecurityIdentityServerMechanismFactory$1@4e1a1cab] for mechanism [FORM]
2024-01-17 18:51:38,639 TRACE [org.wildfly.security] (default task-1) Handling AvailableRealmsCallback: realms = []
2024-01-17 18:51:38,639 TRACE [org.wildfly.security.http.form] (default task-1) Trying to re-authenticate session PUpj1tWHGVItrh2pGn--S_v4-Xted7GauMr2MQmX. Request URI: [http://jbpm.example.ch/business-central/kie-wb.jsp], Context path: [/business-central]
2024-01-17 18:51:38,639 TRACE [org.wildfly.security] (default task-1) Handling CachedIdentityAuthorizeCallback: principal = null  authorizedIdentity = SecurityIdentity{principal=ldapuser, securityDomain=org.wildfly.security.auth.server.SecurityDomain@e3b9f68, authorizationIdentity=EMPTY, realmInfo=RealmInfo{name='KieLdap', securityRealm=org.wildfly.security.auth.realm.ldap.LdapSecurityRealm@1fa010d8}, creationTime=2024-01-17T18:51:38.571413Z}
2024-01-17 18:51:38,639 TRACE [org.wildfly.security] (default task-1) Handling AuthenticationCompleteCallback: succeed
2024-01-17 18:51:38,639 TRACE [org.wildfly.security] (default task-1) Handling SecurityIdentityCallback: identity = SecurityIdentity{principal=ldapuser, securityDomain=org.wildfly.security.auth.server.SecurityDomain@e3b9f68, authorizationIdentity=EMPTY, realmInfo=RealmInfo{name='KieLdap', securityRealm=org.wildfly.security.auth.realm.ldap.LdapSecurityRealm@1fa010d8}, creationTime=2024-01-17T18:51:38.571413Z}
2024-01-17 18:51:38,640 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [ldapuser] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2024-01-17 18:51:38,640 TRACE [org.wildfly.security] (default task-1) Role mapping: principal [ldapuser] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
2024-01-17 18:51:38,640 TRACE [org.wildfly.security] (default task-1) Permission mapping: identity [ldapuser] with roles [] implies ("javax.security.jacc.WebResourcePermission" "/kie-wb.jsp" "GET") = false

Solution

  • I finally found the answer:

    I used the default:

    <simple-role-decoder name="from-roles-attribute" attribute="roles"/>
    

    But my commands to connect LDAP parsed the CN attribute of the LDAP groups into "Roles". Since the default role-decoder uses "roles" as an attribute and the label is case sensitive, it did not find anything to parse.

    Solution was to change the above line to:

    <simple-role-decoder name="from-roles-attribute" attribute="Roles"/>