Hi
Struggling for days now regarding the setup of Kerberos in a Keycloak 24.x docker container.
What I’ve done so far :
- Creating a custom image installing
krb5-workstation
in my image - Mounting an existing (and working)
/etc/krb5.conf
to run container (docker swarm)
However when setting every Kerberos settings to my new Keycloak, it fails flawlessly with error :
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt true ticketCache is null isInitiator false KeyTab is /var/kerberos/keycloak/keycloak.keytab_AES refreshKrb5Config is false principal is HTTP/keycloak.contoso.net@CONTOSO.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
principal is HTTP/keycloak.contoso.net@CONTOSO.NET
Will use keytab
Commit Succeeded
2024-06-11 00:00:07,379 WARN [org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator] (executor-thread-1) SPNEGO login failed: java.security.PrivilegedActionException: GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:716)
[...]
at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
at java.security.jgss/sun.security.jgss.krb5.Krb5Context.acceptSecContext(Krb5Context.java:864)
[...]
at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
... 25 more
Caused by: KrbException: Checksum failed
at java.security.jgss/sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType.decrypt(Aes256CtsHmacSha1EType.java:102)
[...]
at java.security.jgss/sun.security.jgss.krb5.Krb5Context.acceptSecContext(Krb5Context.java:837)
... 35 more
Caused by: java.security.GeneralSecurityException: Checksum failed
[...]
at java.security.jgss/sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType.decrypt(Aes256CtsHmacSha1EType.java:100)
... 41 more
[Krb5LoginModule]: Entering logout
[Krb5LoginModule]: logged out Subject
2024-06-11 00:00:07,385 WARN [org.keycloak.authentication.authenticators.browser.SpnegoAuthenticator] (executor-thread-1) Received kerberos token, but there is no user storage provider that handles kerberos credentials.
I’ve tested my keytab using two methods :
1/ kinit
& klist
which seems to work
bash-5.1$ kinit -v -k -t /var/kerberos/keycloak/keycloak.keytab_AES HTTP/keycloak.contoso.net@CONTOSO.NET
bash-5.1$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: HTTP/keycloak.contoso.net@CONTOSO.NET
Valid starting Expires Service principal
06/11/2024 00:14:47 06/11/2024 10:14:47 krbtgt/CONTOSO.NET@CONTOSO.NET
renew until 06/18/2024 00:14:47
2/ Using a Java piece of code provided in another SO answer but adapted to take Keycloak parameters (to work in same condition):
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.security.auth.Subject;
import com.sun.security.auth.module.Krb5LoginModule;
/**
* This is simple Java program that tests ability to authenticate
* with Kerberos using the JDK implementation.
*
* The program uses no libraries but JDK itself.
*/
public class Krb {
private void loginImpl(final String propertiesFileName) throws Exception {
System.out.println("NB: system property to specify the krb5 config: [java.security.krb5.conf]");
//System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
System.out.println(System.getProperty("java.version"));
System.setProperty("sun.security.krb5.debug", "true");
final Subject subject = new Subject();
final Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
final Map<String,String> optionMap = new HashMap<String,String>();
if (propertiesFileName == null) {
//optionMap.put("ticketCache", null);
optionMap.put("keyTab", "/var/kerberos/keycloak/keycloak.keytab_AES");
optionMap.put("principal", "HTTP/keycloak.contoso.net@CONTOSO.NET"); // default realm
optionMap.put("doNotPrompt", "true");
optionMap.put("refreshKrb5Config", "false");
optionMap.put("useTicketCache", "false");
optionMap.put("renewTGT", "false");
optionMap.put("useKeyTab", "true");
optionMap.put("storeKey", "true");
optionMap.put("isInitiator", "false");
optionMap.put("tryFirstPass", "false");
optionMap.put("useFirstPass", "false");
optionMap.put("storePass", "false");
optionMap.put("clearPass", "false");
} else {
File f = new File(propertiesFileName);
System.out.println("======= loading property file ["+f.getAbsolutePath()+"]");
Properties p = new Properties();
InputStream is = new FileInputStream(f);
try {
p.load(is);
} finally {
is.close();
}
optionMap.putAll((Map)p);
}
optionMap.put("debug", "true"); // switch on debug of the Java implementation
krb5LoginModule.initialize(subject, null, new HashMap<String,String>(), optionMap);
boolean loginOk = krb5LoginModule.login();
System.out.println("======= login: " + loginOk);
boolean commitOk = krb5LoginModule.commit();
System.out.println("======= commit: " + commitOk);
System.out.println("======= Subject: " + subject);
}
public static void main(String[] args) throws Exception {
System.out.println("A property file with the login context can be specified as the 1st and the only paramater.");
final Krb krb = new Krb();
krb.loginImpl(args.length == 0 ? null : args[0]);
}
}
which results in success:
[...]
======= login: true
Commit Succeeded
======= commit: true
======= Subject: Subject:
Principal: HTTP/keycloak.contoso.net@CONTOSO.NET
Private Credential: /var/kerberos/keycloak/keycloak.keytab_AES for HTTP/keycloak.contoso.net@CONTOSO.NET
Any help would be much appreciated. Don’t know why it seems to work using 1/ & 2/ but not in keycloak (checksum failed).