Kerberos - Checksum failed

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).