spring-projects/spring-security · warning
Your keytab is in the classpath. This file needs special…
Error message
Your keytab is in the classpath. This file needs special protection and shouldn't be in the classpath. JAAS may also not be able to load this file from classpath.
What it means
SunJaasKerberosTicketValidator.afterPropertiesSet logs this warning when keyTabLocation is a ClassPathResource. The keytab contains the service's long-term Kerberos keys; shipping it on the classpath is insecure (anyone with the artifact can extract it) and Krb5LoginModule may fail to load it from inside a JAR. Validation of service tickets then proceeds with the keytab path after stripping any file: prefix.
Solutions
- Place the keytab at a protected filesystem path and point keyTabLocation at a FileSystemResource or file:/etc/... URL.
- Set key-tab-location: file:/etc/security/app.keytab (absolute path) in application.yml instead of classpath:.
- Mount the keytab via a secret/configMap volume in containerized deployments and chmod 600 it.
- Rotate the key (kadmin: ktadd -k new.keytab) if it was ever packaged into an artifact or committed to git.
Example fix
// before
validator.setKeyTabLocation(new ClassPathResource("kerberos/app.keytab"));
// after
validator.setKeyTabLocation(new FileSystemResource("/etc/security/app.keytab"));
// or key-tab-location: file:/etc/security/app.keytab Defensive patterns
Strategy: validation
Validate before calling
Resource keytab = validator.getKeyTabLocation();
if (keytab instanceof ClassPathResource) {
throw new IllegalStateException("Kerberos keytab must not live on the classpath; use file:/etc/security/app.keytab");
} Type guard
boolean isExternalKeytab(Resource r) { return r != null && !(r instanceof ClassPathResource); } Try / catch
Not an exception — only LOG.warn during bean initialization; enforce the check in your own @PostConstruct/bean customization instead of relying on try-catch.
Prevention
- Configure key-tab-location as an absolute filesystem path (file:/etc/security/app.keytab).
- Deliver keytabs via deployment secrets/configMaps, never inside the JAR.
- Set restrictive permissions (600) and correct ownership for the app service user.
- Audit the repo/CI for .keytab files; rotate any that were ever packaged or committed.
When it happens
Trigger: Configuring the SunJaasKerberosTicketValidator bean (setKeyTabLocation(new ClassPathResource("...")) or spring.security.kerberos.key-tab-location=classpath:...) and letting Spring call afterPropertiesSet during context startup.
Common situations: application.yml with key-tab-location: classpath:app.keytab; keytab copied into src/main/resources; tutorials/docker images that bake the keytab into the application JAR; team members reusing a committed sample configuration.
Related errors
- Your keytab is in the classpath. This file needs special…
- Error running rest call
- GSSContext name of the context initiator is null
- Kerberos validation not successful
- Negotiate Header was invalid
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/896ed2fd04a3f922.
Report an issue: GitHub.
Appendix: source
Thrown at kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/SunJaasKerberosTicketValidator.java:109
JaasSubjectHolder subjectHolder = new JaasSubjectHolder(subjectCopy);
return Subject.doAs(subjectHolder.getJaasSubject(), new KerberosMultitierValidateAction(token));
}
catch (IllegalStateException | PrivilegedActionException ex) {
throw new BadCredentialsException("Kerberos validation not successful", ex);
}
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.servicePrincipal, "servicePrincipal must be specified");
Assert.notNull(this.keyTabLocation, "keyTab must be specified");
if (this.servicePrincipal == null || this.keyTabLocation == null) {
throw new IllegalStateException("servicePrincipal and keyTabLocation must be set");
}
if (this.keyTabLocation instanceof ClassPathResource) {
this.LOG.warn(
"Your keytab is in the classpath. This file needs special protection and shouldn't be in the classpath. JAAS may also not be able to load this file from classpath.");
}
String keyTabLocationAsString = this.keyTabLocation.getURL().toExternalForm();
// We need to remove the file prefix (if there is one), as it is not supported in
// Java 7 anymore.
// As Java 6 accepts it with and without the prefix, we don't need to check for
// Java 7
if (keyTabLocationAsString.startsWith("file:")) {
keyTabLocationAsString = keyTabLocationAsString.substring(5);
}
LoginConfig loginConfig = new LoginConfig(keyTabLocationAsString, this.servicePrincipal, this.realmName,
this.multiTier, this.debug, this.refreshKrb5Config);
Set<Principal> princ = new HashSet<Principal>(1);
princ.add(new KerberosPrincipal(this.servicePrincipal));
Subject sub = new Subject(false, princ, new HashSet<Object>(), new HashSet<Object>());
LoginContext lc = new LoginContext("", sub, null, loginConfig);
lc.login();
this.serviceSubject = lc.getSubject();View on GitHub (pinned to 96852e8860)