This article describes how one can use Kerberos to authenticate to an LDAP service. It requires that one knows how kerberos works and some basic java programming. This app is not for web app kerberos authentication: if you need that please go to Baeldung
The demo app needs to be configured corrrectly in your environment in order to run correctly. During this guide I will make the following assumptions:
- Java 11 SDk installed on the development computer
- Windows environemnt for development in an ative directory domain (A.K.A. Enterprise environmnt)
In order to use a Kerberos Subject/User to authenticate an LDAP interaction, one needs
- first to login to the kerberos and get a tgt (ticket-grantig ticket), or reuse the cached one
- second execute a code fragment "as" the authenticated user.
As first thing you need to know your domain (An excerpt from this guide follows)
C:\Users\username>systeminfo
[...]
Domain: example.com
[...]
After that, you can query the needed details from the domain itself
C:\Users\username>nltest /dsgetdc:example.com
DC: \\kdc.example.com
Address: \\10.0.0.1
Dom Guid: 00000000-0000-0000-0000-000000000000
Dom Name: example.com
Forest Name: example.com
Dc Site Name: Default-First-Site-Name
Our Site Name: Default-First-Site-Name
Flags: PDC DS LDAP KDC TIMESERV WRITABLE DNS_DC DNS_DOMAIN DNS_FOREST CL
OSE_SITE FULL_SECRET WS
The command completed successfully
with this data, you can write the kerberos configuration file, which should look ike this
[logging]
Default = FILE:/var/log/krb5.log
[libdefaults]
ticket_lifetime = 24h
clock-skew = 300
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = false
forwardable = true
renew_lifetime = 7d
[realms]
EXAMPLE.COM = {
kdc = kdc.example.com:88
; often the admin server is on the same machine of the kdc
; in my case it was something like DCBLABLABLA.example.local
admin_server = ad.example.com:464
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
And yes, the uppercase is important!
Once you have the krb5.conf
you can start with the java implementation.
In order to login using the kerberos login, we can either have a security configuration like the following
SampleClient /* this is the name of the configuration*/ {
// Example that reuses the stored tgt token
com.sun.security.auth.module.Krb5LoginModule required useTicketCache=true;
};
or set the Configuration programmatically
private static Configuration kerberosTicketCacheConfiguration() {
return new Configuration() {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
System.out.println("Name: " + name);
HashMap<String, Object> options = new HashMap<>();
options.put("useTicketCache", "true");
//options.put("useKeyTab", "true");
//options.put("principal", "endeios@EXAMPLE.COM");
//options.put("storeKey", "true");
//options.put("doNotPrompt", "true");
System.out.println(options);
AppConfigurationEntry appConfigurationEntry = new AppConfigurationEntry(Krb5LoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
return new AppConfigurationEntry[] { appConfigurationEntry };
}
};
}
The meaning of the parameter is in the Krb5LoginModule documentation. In the example, the configuration is set to any name of configuration entry. Finally, you can login
LoginContext ctx = new LoginContext("SampleClient", new SampleCallbackHandler());
ctx.login();
Subject subject = ctx.getSubject();
System.out.println("Login OK");
Now that one has the Subject it is possible to proceed to the second step.
Java has a standard way to execute a fragment of code as a user: the Subject.doAs method. With this method, a PrivilegedAction is launched within a AccessControlContext in which the current subject is the desired one. At this point, the only needed thing is to create a subclass of PrivilegedAction in which one calls the LDAP server using as SECURITY_AUTHENTICATION the "GSSAPI" subsystem. In case Authenticatin is not enough, the suggestion is of course encryption with setting in the LDAP context "javax.security.sasl.qop" to "auth-conf,auth-int,auth"
In code, just call the ```Subject.doAs`` function with your subject
Attributes attributes = Subject.doAs(lc.getSubject(), new LDAPAttributesRetrieverAction(true));
whre the action is as specified
class LDAPAttributesRetrieverAction implements java.security.PrivilegedAction<Attributes> {
private final boolean secureMode;
public LDAPAttributesRetrieverAction(boolean secureMode) {
this.secureMode = secureMode;
}
public Attributes run() {
Attributes attributes = performJndiOperation();
return attributes;
}
private Attributes performJndiOperation() {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://MYDCSERVER.example.com:389/");
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
if (secureMode) {
env.put("javax.security.sasl.qop", "auth-conf,auth-int,auth");
}
try {
final DirContext ctx = new InitialDirContext(env);
Attributes attributes = ctx.getAttributes("");
// Close the context when we're done please!
ctx.close();
return attributes;
} catch (final NamingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
In this example, the return value is the attributes from your Directory. This concludes the basic walktrhough.
The Kerberos login context configuration deserves a bit of more dissertation, because it has the potential to help in many situations. It is of course all in documentation, but this is a reminder.
Want | Configuration |
---|---|
I want to use the currently stored and shared tgt token | useTicketCache = true |
I want to ask username and password | doNotPrompt = false |
I want to authenticate as service, with no user interaction (works also for users!) (see next session for how to keytab) | useKeyTab = true , principal = <principal name> |
- TGT : ticket grantig ticket : with this ticket you can ask for service ticket and you get it as the first part of the Kerberoos protocol
- Keytab : store for principal/password, where your app has the possibility to retrieve the shared secret to authenticate to the KDC. can be created with a tool.
Sythesis from here
Initilizes the cached tgt, which means it saved it in a "default" position. Do not worry about the position, usually other tools know where to find it. You can request tgts for many principals!
kinit duke@EXAMPLE.LOCAL
... (changeit)
kinit endeios@EXAMPLE.LOCAL
... (myzuperduperpassword)
Creates a keytab file in the "default" location for the givben principal. You can have mutiple.
ktab -a endeios@EXAMPLE.COM
you can have mutiple entries once you have many service running. -a
means add.
Displays the tgt you currently have cached in your machine
klist
Credentials cache C:\Users\duke\krb5cc_duke not found