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