How to use LdapExtLoginModule for role mapping only in JBoss EAP5.1?

There are some situations where you want to authenticate an user against a database or using X509 certificates and then assign roles according to the mapping in a different backend, for example a LDAP server.

JBoss has a long history of supporting the password-stacking=useFirstPass option in its login modules but can it be used with LdapExtLoginModule so it is used for role mapping only? In recent versions of the application server (such as EAP 5.1) the answer is yes, as we will see in the following example.


Description

This example will show how to authenticate to JBoss' jmx-console with a X509 certificate (using BaseCertLoginModule) and getting the user's roles assigned by a LDAP server (using LdapExtLoginModule).

Creating the client certificate

We use keytool for this:

 

keytool -genkeypair -alias client -keysize 1024 -validity 365 -keystore client.keystore -dname "cn=Marcus,dc=jboss,dc=org"

This will create a JKS keystore containing a X509 certificate valid for one year.

Verify the certificate using keytool also:

 

[mmoyses@mmoyses temp]$ keytool -list -v -keystore client.keystore 
Enter keystore password:  

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: client
Creation date: Nov 24, 2010
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Marcus, DC=jboss, DC=org
Issuer: CN=Marcus, DC=jboss, DC=org
Serial number: 4ced116a
Valid from: Wed Nov 24 11:21:46 BRST 2010 until: Thu Nov 24 11:21:46 BRST 2011
Certificate fingerprints:
         MD5:  B9:F2:25:97:2D:4D:4F:A9:1B:E9:A3:09:8D:A0:D4:C8
         SHA1: 56:31:FE:B6:4B:11:83:2C:D9:C8:15:09:B3:C3:6D:38:83:99:D5:E7
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

Now we need to export this certificate so it can be later added to the server's truststore.

Here is how:

 

[mmoyses@mmoyses temp]$ keytool -exportcert -alias client -keystore client.keystore -file client.certificate
Enter keystore password:  
Certificate stored in file <client.certificate>

This client.certificate can be verified to see if the certificate was correctly exported:

 

[mmoyses@mmoyses temp]$ keytool -printcert -v -file client.certificate 
Owner: CN=Marcus, DC=jboss, DC=org
Issuer: CN=Marcus, DC=jboss, DC=org
Serial number: 4ced116a
Valid from: Wed Nov 24 11:21:46 BRST 2010 until: Thu Nov 24 11:21:46 BRST 2011
Certificate fingerprints:
         MD5:  B9:F2:25:97:2D:4D:4F:A9:1B:E9:A3:09:8D:A0:D4:C8
         SHA1: 56:31:FE:B6:4B:11:83:2C:D9:C8:15:09:B3:C3:6D:38:83:99:D5:E7
         Signature algorithm name: SHA1withDSA
         Version: 3

We also need the client certificate in the PKCS12 format so it can imported into the browser. This is how to do it:

 

keytool -importkeystore -srckeystore client.keystore -destkeystore client.p12 -srcstoretype JKS -deststoretype PKCS12

This will create the file client.p12 which contains the same certificate but in the PKCS12 format. To verify it, execute:

 

[mmoyses@mmoyses temp]$ openssl pkcs12 -info -in client.p12
Enter Import Password:
MAC Iteration 1024
MAC verified OK
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 1024
Bag Attributes
    friendlyName: client
    localKeyID: 54 69 6D 65 20 31 32 39 30 36 30 35 35 31 39 38 37 36 
Key Attributes: <No Attributes>
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIBljBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIacoVCCQSp9sCAggA
MBQGCCqGSIb3DQMHBAjfDdmRO6O0fASCAVBosjD/P8OcLN3UtSGPXmK5FAuWnfeM
72IufOdNoUoL/krl6Rij7qet8f6QTedDPwF479S6Vt1jMwT779NrI+gCmljnyhWr
xlotRFgF/qxVVCzITAWVlaqmAp7SfqmRF3+pjCcGY4Ihoz/bTDYj1Rn43w09mjBa
Lv9eyGKcHcbN1HtiP6ivLE69QuQrUKTJXrJsxOuzWaKIzcfwkrPqPQfm5DUCDYE2
DaQ7TP71aYyGJPaE7SE6IY7wRrw+X7mhsrNRLyXaFXPKM1M+sbPux0PS7KhEZycU
s+KygcPAGwjD2JQSgCbwrJshL3VKMJduMJq0qfJ37bR99Akf7XcXfUjUzL/w8+AI
6UKxhHgUCfrX1UCZl941g8VJraYIoU5Dtd3rqTSvQarFnHQ9lgolbzQeXyYxsYHM
GbU//AMVttNOJlR9Hv4/xMHwYOjxnBsuWT4=
-----END ENCRYPTED PRIVATE KEY-----
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 1024
Certificate bag
Bag Attributes
    friendlyName: CN=Marcus,DC=jboss,DC=org
    localKeyID: 54 69 6D 65 20 31 32 39 30 36 30 35 35 31 39 38 37 36 
subject=/DC=org/DC=jboss/CN=Marcus
issuer=/DC=org/DC=jboss/CN=Marcus
-----BEGIN CERTIFICATE-----
MIICszCCAnGgAwIBAgIETO0RajALBgcqhkjOOAQDBQAwPTETMBEGCgmSJomT8ixk
ARkWA29yZzEVMBMGCgmSJomT8ixkARkWBWpib3NzMQ8wDQYDVQQDEwZNYXJjdXMw
HhcNMTAxMTI0MTMyMTQ2WhcNMTExMTI0MTMyMTQ2WjA9MRMwEQYKCZImiZPyLGQB
GRYDb3JnMRUwEwYKCZImiZPyLGQBGRYFamJvc3MxDzANBgNVBAMTBk1hcmN1czCC
AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgHaY1mg76jjXTkDD7kSPurgfaK6lJdzW
sUPDAsxAOgCUtfh/oZDnkKKqixRu5KI74Hx7gHRtFRSBzwOx+jWrJ5HG2ZVJR7Io
HXbRmEihLMoXIze5n9AzJCNJsfm1GWGsGxu/WiNdI5mJPCfECTPb53nk3JkxSBId
UXpuIOhQzkPfMAsGByqGSM44BAMFAAMvADAsAhQVNsju4jwV6PT/7Nf4c4USOWe5
SAIUSuFYIJEOduiagbA6KwG2L9PcZ8k=
-----END CERTIFICATE-----

Import this certificate into your browser. I'm not going into the details of that.


Configuring JBoss

I. Setting up a SSL connector

a) Creating the server certificate

Again we use keytool for this:

 

keytool -genkeypair -alias jboss -keysize 1024 -validity 365 -keystore jboss.keystore -dname "cn=JBoss Server,dc=jboss,dc=org"

This will create a JKS keystore containing a X509 certificate valid for one year.

Verifying the certificate:

 

[mmoyses@mmoyses temp]$ keytool -list -v -keystore jboss.keystore
Enter keystore password:  

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: jboss
Creation date: Nov 24, 2010
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=JBoss Server, DC=jboss, DC=org
Issuer: CN=JBoss Server, DC=jboss, DC=org
Serial number: 4ced102b
Valid from: Wed Nov 24 11:16:27 BRST 2010 until: Thu Nov 24 11:16:27 BRST 2011
Certificate fingerprints:
         MD5:  07:D5:49:FF:5C:B0:CB:BF:CD:CD:8C:50:98:CE:8F:74
         SHA1: 60:E2:32:8E:F0:7D:0F:F6:A0:E2:65:F9:C1:79:C6:C8:76:AA:1A:8C
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************


b) Creating the server truststore

For self signed certificates like the ones showed here we need to import each one into the server's truststore so they are accepted at the socket level. Here is how to import the certificate:

 

[mmoyses@mmoyses temp]$ keytool -importcert -alias "CN=Marcus, DC=jboss, DC=org" -file client.certificate -keystore clients.truststore
Enter keystore password:  
Re-enter new password: 
Owner: CN=Marcus, DC=jboss, DC=org
Issuer: CN=Marcus, DC=jboss, DC=org
Serial number: 4ced116a
Valid from: Wed Nov 24 11:21:46 BRST 2010 until: Thu Nov 24 11:21:46 BRST 2011
Certificate fingerprints:
         MD5:  B9:F2:25:97:2D:4D:4F:A9:1B:E9:A3:09:8D:A0:D4:C8
         SHA1: 56:31:FE:B6:4B:11:83:2C:D9:C8:15:09:B3:C3:6D:38:83:99:D5:E7
         Signature algorithm name: SHA1withDSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

Notice the certificate was imported using an alias equals to the DN of the certificate. This is required by BaseCertLoginModule.

You can verifiy the truststore in the same way:

 

[mmoyses@mmoyses temp]$ keytool -list -v -keystore clients.truststore 
Enter keystore password:  

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: cn=marcus, dc=jboss, dc=org
Creation date: Nov 24, 2010
Entry type: trustedCertEntry

Owner: CN=Marcus, DC=jboss, DC=org
Issuer: CN=Marcus, DC=jboss, DC=org
Serial number: 4ced116a
Valid from: Wed Nov 24 11:21:46 BRST 2010 until: Thu Nov 24 11:21:46 BRST 2011
Certificate fingerprints:
         MD5:  B9:F2:25:97:2D:4D:4F:A9:1B:E9:A3:09:8D:A0:D4:C8
         SHA1: 56:31:FE:B6:4B:11:83:2C:D9:C8:15:09:B3:C3:6D:38:83:99:D5:E7
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

Notice that the certificate is now a trustedCertEntry and not a privateKeyEntry as it used to be in the original keystore.


c) Setting up the connector

Modify deploy/jbossweb.sar/server.xml and add a SSL connector using the keystore and truststore we created in earlier steps:

 

<Connector protocol="HTTP/1.1" SSLEnabled="true" 
           port="8443" address="${jboss.bind.address}"
           scheme="https" secure="true" clientAuth="true" 
           keystoreFile="/home/mmoyses/certificate/temp/jboss.keystore"
           keystorePass="changeit" 
           truststoreFile="/home/mmoyses/certificate/temp/clients.truststore"
           trustStorePass="changeit"
           sslProtocol = "TLS" />

Notice the attribute clientAuth="true". This is required for CLIENT-CERT authentication.

II. Setting up the jmx-console web application to require client certificates

Just modify depoy/jmx-console.war/WEB-INF/web.xml and change:

 

<auth-method>BASIC</auth-method>

to

 

<auth-method>CLIENT-CERT</auth-method>

III. Setting up the security domain

a) Configuring the login modules

Modify conf/login-config.xml to authenticate using BaseCertLoginModule and LdapExtLoginModule:

 

  <application-policy name="jmx-console">
    <authentication>
      <login-module code="org.jboss.security.auth.spi.BaseCertLoginModule" flag="required">
        <module-option name="password-stacking">useFirstPass</module-option>
        <module-option name="securityDomain">java:/jaas/clients</module-option>
      </login-module>
      <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required">
        <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
        <module-option name="java.naming.provider.url">ldap://localhost/</module-option>
        <module-option name="java.naming.security.authentication">simple</module-option>
        <module-option name="bindDN">cn=Root,dc=jboss,dc=org</module-option>
        <module-option name="bindCredential">secret</module-option>
        <module-option name="baseCtxDN">dc=jboss,dc=org</module-option>
        <module-option name="rolesCtxDN">ou=Roles,dc=jboss,dc=org</module-option>
        <module-option name="baseFilter">(seeAlso={0})</module-option>
        <module-option name="roleFilter">(member={1})</module-option>
        <module-option name="roleAttributeID">cn</module-option>
        <module-option name="roleRecursion">0</module-option>
        <module-option name="password-stacking">useFirstPass</module-option>
      </login-module>
    </authentication>
  </application-policy>

b) Deploy the JaasSecurityDomain bean

BaseCertLoginModule requires a JaasSecurityDomain bean (the securityDomain option in the login module) to validate the certificates. We will use the same truststore created for the SSL connector for this. That's why we need to add the trusted certificates using the DN as the alias name of the entry.

To deploy the bean create a file named clients-jboss-beans.xml inside the deploy/ directory with the following contents:

 

<?xml version="1.0" encoding="UTF-8"?>

<!-- JaasSecurityDomain Microcontainer Beans -->
<deployment xmlns="urn:jboss:bean-deployer:2.0">

   <bean name="JaasSecurityDomain:clients" class="org.jboss.security.plugins.JaasSecurityDomain">
      <constructor>
         <parameter>clients</parameter>
      </constructor>
      <property name="keyStoreURL">/home/mmoyses/certificate/temp/clients.truststore</property>
      <property name="keyStorePass">changeit</property>
      <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.security:service=JaasSecurityDomain,domain=clients",exposedInterface=org.jboss.security.plugins.JaasSecurityDomainMBean.class)</annotation>
   </bean>

</deployment>

This will create the java:/jaas/clients security domain needed by BaseCertLoginModule.


Example LDAP server ldif

 

dn: dc=jboss,dc=org
objectclass: top
objectclass: dcObject
objectclass: organization
dc: jboss
o: example

dn: cn=Root,dc=jboss,dc=org
objectclass: organizationalRole
cn: Root

dn: ou=Roles,dc=jboss,dc=org
objectClass: top
objectClass: organizationalUnit
ou: Roles

dn: cn=JBossAdmin,ou=Roles,dc=jboss,dc=org
objectClass: top
objectClass: groupOfNames
cn: JBossAdmin
description: the JBossAdmin role
member: cn=Marcus,dc=jboss,dc=org

dn: cn=HttpInvoker,ou=Roles,dc=jboss,dc=org
objectClass: groupOfNames
objectClass: top
cn: HttpInvoker
description: the HttpInvoker role
member: cn=Marcus,dc=jboss,dc=org

dn: cn=Marcus,dc=jboss,dc=org
objectclass: person
cn: Marcus
sn: Moyses
userPassword: password
seeAlso: CN=Marcus, DC=jboss, DC=org

Notice I used the seeAlso attribute to set the DN of the user (and also the DN of the user certificate) and thus mapped the baseFilter option of LdapExtLoginModule to use this attribute. AD users should definitely use the distinguishedName attribute in this place.

 

With everything set up correctly we can now login to jmx-console using our certificate.

This is an example log showing the successful authentication and role mapping:

 

2010-11-24 15:07:53,465 TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.jmx-console] (http-127.0.0.1-8443-1) Begin isValid, principal:CN=Marcus, DC=jboss, DC=org, cache info: null
2010-11-24 15:07:53,465 TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.jmx-console] (http-127.0.0.1-8443-1) defaultLogin, principal=CN=Marcus, DC=jboss, DC=org
2010-11-24 15:07:53,465 TRACE [org.jboss.security.auth.login.XMLLoginConfigImpl] (http-127.0.0.1-8443-1) Begin getAppConfigurationEntry(jmx-console), size=11
2010-11-24 15:07:53,465 TRACE [org.jboss.security.auth.login.XMLLoginConfigImpl] (http-127.0.0.1-8443-1) End getAppConfigurationEntry(jmx-console), authInfo=AppConfigurationEntry[]:
[0]
LoginModule Class: org.jboss.security.auth.spi.BaseCertLoginModule
ControlFlag: LoginModuleControlFlag: required
Options:
name=securityDomain, value=java:/jaas/clients
name=password-stacking, value=useFirstPass
[1]
LoginModule Class: org.jboss.security.auth.spi.LdapExtLoginModule
ControlFlag: LoginModuleControlFlag: required
Options:
name=baseFilter, value=(seeAlso={0})
name=java.naming.security.authentication, value=simple
name=java.naming.factory.initial, value=com.sun.jndi.ldap.LdapCtxFactory
name=roleFilter, value=(member={1})
name=bindCredential, value=****
name=bindDN, value=cn=Root,dc=jboss,dc=org
name=java.naming.provider.url, value=ldap://localhost/
name=rolesCtxDN, value=ou=Roles,dc=jboss,dc=org
name=roleRecursion, value=0
name=baseCtxDN, value=dc=jboss,dc=org
name=roleAttributeID, value=cn
name=password-stacking, value=useFirstPass

2010-11-24 15:07:53,469 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) initialize
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) Security domain: jmx-console
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) securityDomain=java:/jaas/clients
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) found domain: org.jboss.security.plugins.JaasSecurityDomain
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) exit: initialize(Subject, CallbackHandler, Map, Map)
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) enter: login()
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) login
2010-11-24 15:07:53,470 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) enter: getAliasAndCert()
2010-11-24 15:07:53,471 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) exit: getAliasAndCert()
2010-11-24 15:07:53,471 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) enter: validateCredentail(String, X509Certificate)
2010-11-24 15:07:53,471 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1)
        Supplied Credential: 4ced116a
                CN=Marcus, DC=jboss, DC=org

        Existing Credential: 4ced116a
                CN=Marcus, DC=jboss, DC=org

2010-11-24 15:07:53,471 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) The supplied certificate matched the certificate in the keystore.
2010-11-24 15:07:53,471 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) exit: validateCredentail(String, X509Certificate)
2010-11-24 15:07:53,471 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) User 'CN=Marcus, DC=jboss, DC=org' authenticated, loginOk=true
2010-11-24 15:07:53,471 DEBUG [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) exit: login()
2010-11-24 15:07:53,476 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) initialize
2010-11-24 15:07:53,476 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) Security domain: jmx-console
2010-11-24 15:07:53,476 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) login
2010-11-24 15:07:53,486 TRACE [org.jboss.security.auth.spi.BaseCertLoginModule] (http-127.0.0.1-8443-1) commit, loginOk=true
2010-11-24 15:07:53,486 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) commit, loginOk=true
2010-11-24 15:07:53,487 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) Logging into LDAP server, env={java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, java.naming.security.principal=cn=Root,dc=jboss,dc=org, roleRecursion=0, password-stacking=useFirstPass, baseCtxDN=dc=jboss,dc=org, roleAttributeID=cn, roleFilter=(member={1}), rolesCtxDN=ou=Roles,dc=jboss,dc=org, baseFilter=(seeAlso={0}), jboss.security.security_domain=jmx-console, java.naming.provider.url=ldap://localhost/, bindDN=cn=Root,dc=jboss,dc=org, java.naming.security.authentication=simple, bindCredential=secret, java.naming.security.credentials=***}
2010-11-24 15:07:53,518 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) Assign user to role JBossAdmin
2010-11-24 15:07:53,519 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (http-127.0.0.1-8443-1) Assign user to role HttpInvoker