Binding to AD LDAP with Kerberos credentials of the application server?

1,478 views
Skip to first unread message

Cat Mucius

unread,
Oct 19, 2014, 5:23:42 AM10/19/14
to ldap...@googlegroups.com
Hi,

Does ldaptive support this scenario?
  • Application server (say, Tomcat) authenticates clients by Kerberos. So it has a keytab file for a valid Active Directory account, used to decrypt and validate clients' tickets.
  • It means that the server cannot know passwords of its users.
  • Can I use the same keytab to make the server to bind (authenticate) to domain controller's LDAP and then send queries to it in order to discover users' group membership and such?

The JAAS page contains example of Kerberos authentication, but it's hard to understand, which scenario it describes exactly:
  1. When the server obtains clients' username & password by some means (let's say, login form) and then uses them to bind to LDAP by Kerberos?
  2. When client delegates its TGT to the server, so the server binds to LDAP on behalf of the client?
  3. Or when the server performs the bind on its own, as I described above?
Thanks! Any help will be appreciated.

dfisher

unread,
Oct 22, 2014, 3:13:16 PM10/22/14
to ldap...@googlegroups.com
On Sunday, October 19, 2014 5:23:42 AM UTC-4, Cat Mucius wrote:
Hi,

Does ldaptive support this scenario?
  • Application server (say, Tomcat) authenticates clients by Kerberos. So it has a keytab file for a valid Active Directory account, used to decrypt and validate clients' tickets.
  • It means that the server cannot know passwords of its users.
  • Can I use the same keytab to make the server to bind (authenticate) to domain controller's LDAP and then send queries to it in order to discover users' group membership and such?

That should work, but I'm not an Active Directory expert. I can't say whether that's good practice or not.
 
The JAAS page contains example of Kerberos authentication, but it's hard to understand, which scenario it describes exactly:
  1. When the server obtains clients' username & password by some means (let's say, login form) and then uses them to bind to LDAP by Kerberos?
  2. When client delegates its TGT to the server, so the server binds to LDAP on behalf of the client?
  3. Or when the server performs the bind on its own, as I described above?

That example performs Kerberos authentication, uses the username that successfully authenticated to resolve an LDAP DN, then uses the DN to resolve roles.
It is meant to demonstrate that if you're stacking JAAS modules, then it doesn't matter what system you use to perform the authentication, the principal can be used to retrieve data from an LDAP.

If you want to perform user authentication, then search for roles using a Kerberos principal, try the following jaas configuration:

ldaptive {

  org
.ldaptive.jaas.LdapLoginModule required
    storePass
="true"
    ldapUrl
="ldap://hostname:port"
    dnResolver
="org.ldaptive.auth.FormatDnResolver{{format=%s...@domain.com}}"
    useStartTLS
="true";

  org
.ldaptive.jaas.LdapRoleAuthorizationModule required
    useFirstPass
="true"
    ldapUrl
="ldap://hostname:port"
    bindSaslConfig
="{mechanism=GSSAPI}"
    baseDn
="ou=people,dc=org1,dc=org"
    roleFilter
="(sAMAccountName={user})"
    roleAttribute
="mail";
};

com
.sun.security.jgss.initiate {
  com
.sun.security.auth.module.Krb5LoginModule required
    doNotPrompt
="true"
    debug
="true"
    principal
="SERVICE-PRINCIPAL"
    useKeyTab
="true"
    keyTab
="/path/to/krb5.keytab";
};


You'll need to set the following system properties:
  -Djava.security.auth.login.config=/path/to/jaas.config
 
-Djava.security.krb5.realm=DOMAIN.COM
 
-Djava.security.krb5.kdc=HOSTNAME
 
-Djavax.security.auth.useSubjectCredsOnly=false

--Daniel Fisher

Cat Mucius

unread,
Oct 28, 2014, 10:14:46 AM10/28/14
to ldap...@googlegroups.com
Daniel, thanks a lot! It works exactly in the fashion that I was looking for!

I had, though, to search for some additional options not appearing on the JAAS page, such as subtreeSearch. The critical bindSaslConfig parameter isn't documented there as well.
jaas.conf:
SpnegoAuthenticator
{
    com.sun.security.auth.module.Krb5LoginModule required
    doNotPrompt=true
    principal="HTTP/tomcat.myd...@MYDOMAIN.LOCAL"
    useKeyTab=true
    keyTab="../conf/tomcat-keytab"
    storeKey=true
    isInitiator=false;
};
com.sun.security.jgss.initiate
{
    com.sun.security.auth.module.Krb5LoginModule required
    doNotPrompt="true"
    principal="HTTP/tomcat.myd...@MYDOMAIN.LOCAL"
    useKeyTab="true"
    keyTab="../conf/tomcat-keytab";
};
ldaptive
{
    org.ldaptive.jaas.LdapDnAuthorizationModule required
    storePass="true"
    ldapUrl="ldap://dc1.mydomain.local:3268"
    bindSaslConfig="{mechanism=GSSAPI}"
    baseDn="DC=MyDomain,DC=local"
    subtreeSearch="true"
    userFilter="(&(userPrincipalName={user})(objectClass=person))";

    org.ldaptive.jaas.LdapRoleAuthorizationModule required
    
ldapUrl="ldap://dc1.mydomain.local:3268"
    bindSaslConfig="{mechanism=GSSAPI}"
    
baseDn="DC=MyDomain,DC=local"
    subtreeSearch="true"
    roleFilter="(&(member={dn})(objectClass=group))"
    roleAttribute="cn";
};

The SpnegoAuthenticator section is for the SpnegoAuthenticator Valve referenced from META-INF/context.xml:
​<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
      <Valve className="org.apache.catalina.authenticator.SpnegoAuthenticator" storeDelegatedCredential="false" alwaysUseSession="true" loginConfigName="SpnegoAuthenticator"​/>​
      <Realm className="org.apache.catalina.realm.JAASRealm" appName="ldaptive" userClassNames="org.ldaptive.jaas.LdapPrincipal" roleClassNames="org.ldaptive.jaas.LdapRole" stripRealmForGss="false"/>
</Context>

I've added the "javax.security.auth.useSubjectCredsOnly" system property - thanks for the tip! - but found that there's no need to add "java.security.krb5.realm" and "java.security.krb5.kdc": turns out that Krb5LoginModule knows to determine addresses of domain controllers automatically. It finds out the domain name - I guess, by checking station's hostname - and then uses DNS to resolve _kerberos._udp.mydomain.local SRV records, which are preset in Active Directory.


Can I ask you some more questions, if you don't mind?
  • Is there a way to make LdapRoleAuthorizationModule to discover nesting of AD groups? Currently it just finds out the groups the user belongs to directly.

  • The so-called "Primary Group"  (which is usually "Domain Users") is a special case: it doesn't list its members in "member" attribute, and they don't list it in their "memberof" attribute. Instead, user objects list its RID in their PrimaryGroupID attribute (513 for "Domain Users"). Can LdapRoleAuthorizationModule find out the Primary Group in some way?

  • When requesting Kerberos ticket to the LDAP service, the LdapDnAuthorizationModule builds SPN by prepending "ldap/" to hostname specified in the ldapUrl. So in my case it will be "ldap/dc1.mydomain.local". This actually forces me to specify specific DC in the ldapUrl, which, of course, is problematic - what happens if the DC isn't reachable for some reason? Can we do better than this? For example, when binding to LDAP with username & password, I can specify ldapUrl in "ldap://mydomain.local:389" form - the actual DC will be resolved by DNS in round-robin fashion. But with Kerberos such URL results in invalid SPN! Is there a way to make the login modules to resolve the _ldap._tcp.mydomain.local SRV records, like it's done with KDC, or something like this?

  • I've paid attention that during the Kerberos authentication to LDAP, the LDAP server doesn't return the AP-REP ticket - the cryptographic proof of its identity. I guest it doesn't because the client (Tomcat) doesn't require it to do so. Can we force Krb5LoginModule to request this ticket from LDAP and to check it?
Thanks in advance!
Cat Mucius.

Daniel Fisher

unread,
Oct 29, 2014, 11:20:58 AM10/29/14
to ldap...@googlegroups.com
On Tue, Oct 28, 2014 at 10:14 AM, Cat Mucius <muc...@wirade.ru> wrote:
Daniel, thanks a lot! It works exactly in the fashion that I was looking for!

I had, though, to search for some additional options not appearing on the JAAS page, such as subtreeSearch. The critical bindSaslConfig parameter isn't documented there as well.
jaas.conf:

There are quite a few options that aren't documented. You should be able to wire up any of the setters for the objects used by the modules, which means documenting everything is big task. I tried to get the most common usages, but I admit the documentation could use some tuning.

Can I ask you some more questions, if you don't mind?
  • Is there a way to make LdapRoleAuthorizationModule to discover nesting of AD groups? Currently it just finds out the groups the user belongs to directly.
 
Try adding this:
    searchEntryHandlers="org.ldaptive.handler.RecursiveEntryHandler{{searchAttribute=member}{mergeAttributes=cn}}"

  • The so-called "Primary Group"  (which is usually "Domain Users") is a special case: it doesn't list its members in "member" attribute, and they don't list it in their "memberof" attribute. Instead, user objects list its RID in their PrimaryGroupID attribute (513 for "Domain Users"). Can LdapRoleAuthorizationModule find out the Primary Group in some way?


You could write an entry handler that searches for the group DN. (Similar to what the recursive entry handler does.) File a feature request for this if you want. Ldaptive already has some components for dealing with AD specific attributes.
 
  • When requesting Kerberos ticket to the LDAP service, the LdapDnAuthorizationModule builds SPN by prepending "ldap/" to hostname specified in the ldapUrl. So in my case it will be "ldap/dc1.mydomain.local". This actually forces me to specify specific DC in the ldapUrl, which, of course, is problematic - what happens if the DC isn't reachable for some reason? Can we do better than this? For example, when binding to LDAP with username & password, I can specify ldapUrl in "ldap://mydomain.local:389" form - the actual DC will be resolved by DNS in round-robin fashion. But with Kerberos such URL results in invalid SPN! Is there a way to make the login modules to resolve the _ldap._tcp.mydomain.local SRV records, like it's done with KDC, or something like this?


You can configure multiple URLs like so:
    ldapUrl="ldap://dc1.mydomain.local:3268 ldap://dc2.mydomain.local:3268 ..."
and also specify a connection strategy:
    connectionStrategy="ACTIVE_PASSIVE"
I know that not exactly what you want, so please file a feature request on the SRV functionality.

  • I've paid attention that during the Kerberos authentication to LDAP, the LDAP server doesn't return the AP-REP ticket - the cryptographic proof of its identity. I guest it doesn't because the client (Tomcat) doesn't require it to do so. Can we force Krb5LoginModule to request this ticket from LDAP and to check it?

Daniel Fisher

unread,
Nov 22, 2014, 2:34:39 PM11/22/14
to ldap...@googlegroups.com
Just closing the loop on this.

On Wed, Oct 29, 2014 at 11:20 AM, Daniel Fisher <dfi...@gmail.com> wrote:
On Tue, Oct 28, 2014 at 10:14 AM, Cat Mucius <muc...@wirade.ru> wrote:
  • The so-called "Primary Group"  (which is usually "Domain Users") is a special case: it doesn't list its members in "member" attribute, and they don't list it in their "memberof" attribute. Instead, user objects list its RID in their PrimaryGroupID attribute (513 for "Domain Users"). Can LdapRoleAuthorizationModule find out the Primary Group in some way?


You could write an entry handler that searches for the group DN. (Similar to what the recursive entry handler does.) File a feature request for this if you want. Ldaptive already has some components for dealing with AD specific attributes.
 

Ldaptive 1.1 includes a PrimaryGroupIdHandler to pull that group DN into an entry's memberOf attribute.

  • When requesting Kerberos ticket to the LDAP service, the LdapDnAuthorizationModule builds SPN by prepending "ldap/" to hostname specified in the ldapUrl. So in my case it will be "ldap/dc1.mydomain.local". This actually forces me to specify specific DC in the ldapUrl, which, of course, is problematic - what happens if the DC isn't reachable for some reason? Can we do better than this? For example, when binding to LDAP with username & password, I can specify ldapUrl in "ldap://mydomain.local:389" form - the actual DC will be resolved by DNS in round-robin fashion. But with Kerberos such URL results in invalid SPN! Is there a way to make the login modules to resolve the _ldap._tcp.mydomain.local SRV records, like it's done with KDC, or something like this?


You can configure multiple URLs like so:
    ldapUrl="ldap://dc1.mydomain.local:3268 ldap://dc2.mydomain.local:3268 ..."
and also specify a connection strategy:
    connectionStrategy="ACTIVE_PASSIVE"
I know that not exactly what you want, so please file a feature request on the SRV functionality.

Ldaptive 1.1 includes a DnsSrvConnectionStrategy. If you have some time to test this functionality I would appreciate it.

--Daniel Fisher

Reply all
Reply to author
Forward
0 new messages