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
- Run kinit (or use a valid keytab) before starting, and verify with klist
- Ensure the JAAS section has useKeyTab=true doNotPrompt=true with correct keyTab and principal, or useTicketCache=true
- Check KRB5CCNAME and /tmp/krb5cc_* availability for the process user
- 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
- Run kinit or configure useKeyTab=true with correct keyTab path and principal
- Verify tickets with klist before service start; watch KRB5CCNAME
- Use TGTRefreshThread/relogin scheduling for long-running services
- Validate JAAS modules actually acquire credentials in a startup smoke test
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Authentication use SASL/JAAS/GSSAPI but server not have Prin
- Unrecognized SASL GSSAPI Server Callback.
- Cannot create SASL client with empty JAAS subject principal
- SASL/JAAS error${e.getCause()}
- Invalid token string, missing attributes
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/609315f45559a39e.
Report an issue: GitHub.