apache/pulsar · critical · LoginException

Kerberos authentication without KerberosTicket provided!

Error message

Kerberos authentication without KerberosTicket provided!

What it means

After a successful JAAS login, JAASCredentialsContainer checks the subject's private credentials for a KerberosTicket. If none is present, Kerberos authentication cannot proceed (no TGT to use or renew), so it throws LoginException. Typically the login module ran without actually acquiring a TGT (e.g. useTicketCache with no cache and no keytab).

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/sasl/JAASCredentialsContainer.java:81

                + "Please check your java.security.login.auth.config (="
                + System.getProperty("java.security.login.auth.config")
                + ") for section header: " + this.loginContextName;
            log.error().attr("details", errorMessage).log("No JAAS Configuration section header found for Client");
            throw new LoginException(errorMessage);
        }
        LoginContext loginContext = new LoginContext(loginContextName, callbackHandler);
        loginContext.login();
        log.info("successfully logged in.");

        this.loginContext = loginContext;
        this.subject = loginContext.getSubject();
        this.isKrbTicket = !this.subject.getPrivateCredentials(KerberosTicket.class).isEmpty();
        if (isKrbTicket) {
            this.isUsingTicketCache = SaslConstants.isUsingTicketCache(loginContextName);
            this.principal = SaslConstants.getPrincipal(loginContextName);
            this.ticketRefreshThread = new TGTRefreshThread(this);
        } else {
            throw new LoginException("Kerberos authentication without KerberosTicket provided!");
        }

        ticketRefreshThread.start();
    }

    void setLoginContext(LoginContext login) {
        this.loginContext = login;
    }

    @Override
    public void close() throws IOException {
        if (ticketRefreshThread != null) {
            ticketRefreshThread.interrupt();
            try {
                ticketRefreshThread.join(10000);
            } catch (InterruptedException exit) {
                Thread.currentThread().interrupt();
                log.debug().exception(exit).log("interrupted while waiting for TGT refresh thread to stop");

View on GitHub (pinned to 820761864e)

Solutions

  1. Run kinit (or use a valid keytab) before starting, and verify with klist
  2. Ensure the JAAS section has useKeyTab=true doNotPrompt=true with correct keyTab and principal, or useTicketCache=true
  3. Check KRB5CCNAME and /tmp/krb5cc_* availability for the process user
  4. Validate the ticket is for the expected principal and not expired

Example fix

// before (jaas.conf)
PulsarClient { com.sun.security.auth.module.Krb5LoginModule required; }; // acquires nothing
// after
PulsarClient {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true keyTab="/etc/security/pulsar.keytab"
  principal="pulsar/host@EXAMPLE.COM" doNotPrompt=true;
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify a TGT exists before attempting Kerberos login
javax.security.auth.login.LoginContext lc = new javax.security.auth.login.LoginContext("PulsarClient", handler);
lc.login();
boolean hasTicket = !lc.getSubject().getPrivateCredentials(javax.security.auth.kerberos.KerberosTicket.class).isEmpty();
if (!hasTicket) throw new IllegalStateException("No KerberosTicket acquired — check kinit/keytab");

Type guard

static boolean hasKerberosTicket(Subject s) {
    return s != null && !s.getPrivateCredentials(KerberosTicket.class).isEmpty();
}

Try / catch

try {
    container = new JAASCredentialsContainer(subject, handler, "PulsarClient", serviceName);
} catch (LoginException e) {
    log.error("No KerberosTicket after login. Run kinit or fix keytab config: {}", e.getMessage());
    throw new AuthenticationException("Kerberos TGT missing", e);
}

Prevention

When it happens

Trigger: login succeeds but subject.getPrivateCredentials(KerberosTicket.class) is empty — JAAS section configured without useKeyTab/useTicketCache acquiring a TGT, an empty or expired kinit cache, or a misconfigured Krb5LoginModule that does not store the ticket.

Common situations: No kinit performed and ticket cache path wrong (KRB5CCNAME); keytab path/principal wrong yet module configured as 'sufficient'; running in a container without krb5 ticket or keytab; JDK differences in credential storage.

Understand the failure class

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/609315f45559a39e. Report an issue: GitHub.