{"record":{"id":"609315f45559a39e","repo":"apache/pulsar","slug":"kerberos-authentication-without-kerberosticket-pro","errorCode":null,"errorMessage":"Kerberos authentication without KerberosTicket provided!","messagePattern":"Kerberos authentication without KerberosTicket provided!","errorType":"exception","errorClass":"LoginException","httpStatus":null,"severity":"critical","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/sasl/JAASCredentialsContainer.java","lineNumber":81,"sourceCode":"                + \"Please check your java.security.login.auth.config (=\"\n                + System.getProperty(\"java.security.login.auth.config\")\n                + \") for section header: \" + this.loginContextName;\n            log.error().attr(\"details\", errorMessage).log(\"No JAAS Configuration section header found for Client\");\n            throw new LoginException(errorMessage);\n        }\n        LoginContext loginContext = new LoginContext(loginContextName, callbackHandler);\n        loginContext.login();\n        log.info(\"successfully logged in.\");\n\n        this.loginContext = loginContext;\n        this.subject = loginContext.getSubject();\n        this.isKrbTicket = !this.subject.getPrivateCredentials(KerberosTicket.class).isEmpty();\n        if (isKrbTicket) {\n            this.isUsingTicketCache = SaslConstants.isUsingTicketCache(loginContextName);\n            this.principal = SaslConstants.getPrincipal(loginContextName);\n            this.ticketRefreshThread = new TGTRefreshThread(this);\n        } else {\n            throw new LoginException(\"Kerberos authentication without KerberosTicket provided!\");\n        }\n\n        ticketRefreshThread.start();\n    }\n\n    void setLoginContext(LoginContext login) {\n        this.loginContext = login;\n    }\n\n    @Override\n    public void close() throws IOException {\n        if (ticketRefreshThread != null) {\n            ticketRefreshThread.interrupt();\n            try {\n                ticketRefreshThread.join(10000);\n            } catch (InterruptedException exit) {\n                Thread.currentThread().interrupt();\n                log.debug().exception(exit).log(\"interrupted while waiting for TGT refresh thread to stop\");","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/sasl/JAASCredentialsContainer.java#L63-L99","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (jaas.conf)\nPulsarClient { com.sun.security.auth.module.Krb5LoginModule required; }; // acquires nothing\n// after\nPulsarClient {\n  com.sun.security.auth.module.Krb5LoginModule required\n  useKeyTab=true keyTab=\"/etc/security/pulsar.keytab\"\n  principal=\"pulsar/host@EXAMPLE.COM\" doNotPrompt=true;\n};","handlingStrategy":"try-catch","validationCode":"// Verify a TGT exists before attempting Kerberos login\njavax.security.auth.login.LoginContext lc = new javax.security.auth.login.LoginContext(\"PulsarClient\", handler);\nlc.login();\nboolean hasTicket = !lc.getSubject().getPrivateCredentials(javax.security.auth.kerberos.KerberosTicket.class).isEmpty();\nif (!hasTicket) throw new IllegalStateException(\"No KerberosTicket acquired — check kinit/keytab\");","typeGuard":"static boolean hasKerberosTicket(Subject s) {\n    return s != null && !s.getPrivateCredentials(KerberosTicket.class).isEmpty();\n}","tryCatchPattern":"try {\n    container = new JAASCredentialsContainer(subject, handler, \"PulsarClient\", serviceName);\n} catch (LoginException e) {\n    log.error(\"No KerberosTicket after login. Run kinit or fix keytab config: {}\", e.getMessage());\n    throw new AuthenticationException(\"Kerberos TGT missing\", e);\n}","preventionTips":["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"],"tags":["kerberos","sasl","tgt","authentication"],"backgroundTag":"missing-kerberos-ticket","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}