javaspringspring-securitysingle-sign-onspring-security-kerberos

Kerberos: Negotiate Header was invalid (Cause GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos credentails))


Moin!

My attempts to authenticate a user via SSO with Spring Security 5 and Kerberos fail due to an exception from deep in the Kerberos code. I will first show the stack trace and the code causing it and then give additional information about my environment which might help to eliminate some possibilities.

Stack trace

WARN 3932 --- [apr-8080-exec-1] w.a.SpnegoAuthenticationProcessingFilter : Negotiate Header was invalid: Negotiate YIILSwYGKwYBBQUCoIILPzCCCzugMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCN[and so on]

org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful
at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:71) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:64) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.1.1.RELEASE.jar:5.1.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.1.1.RELEASE.jar:5.1.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:512) ~[spring-security-config-5.1.1.RELEASE.jar:5.1.1.RELEASE]
...
Caused by: java.security.PrivilegedActionException: null
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_162]
at javax.security.auth.Subject.doAs(Subject.java:422) ~[na:1.8.0_162]
at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:68) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
...
Caused by: org.ietf.jgss.GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos credentails)
at sun.security.jgss.krb5.Krb5AcceptCredential.getInstance(Krb5AcceptCredential.java:87) ~[na:1.8.0_162]
at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:127) ~[na:1.8.0_162]
at sun.security.jgss.krb5.Krb5MechFactory.getMechanismContext(Krb5MechFactory.java:198) ~[na:1.8.0_162]
  1. So there is a BadCredentialsException while my SunJaasKerberosTicketValidator is validating the SSO Ticket. This is just rethrowing a PrivilegedActionException coming from

    public KerberosTicketValidation validateTicket(byte[] token) {
    try {
        return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
    }
    catch (PrivilegedActionException e) {
        throw new BadCredentialsException("Kerberos validation not successful", e);
    }
    

    }

  2. The PrivilegedActionException is hard to track as it comes from a native method java.security.AccessController.doPrivileged. I don't know the implementation. What I find interesting is that the PrivilegedActionException prints out as

    Caused by: java.security.PrivilegedActionException: null
    

    The PrivilegedActionException.toString method is

    public String toString() {
        String s = getClass().getName();
        return (exception != null) ? (s + ": " + exception.toString()) : s;
    }
    

    So the exception (the cause exception) is not null but it prints out as null...

  3. However The stack trace tells us that the root of the problem is a GSSException coming from the class Krb5AcceptCredential.

    if (creds == null)
        throw new GSSException(GSSException.NO_CRED, -1,"Failed to find any Kerberos credentails");
    

    And creds == null is because Krb5Util.getServiceCreds (see implementation) returns null without causing an exception.

This is how far I got until now. Now some additional information.

Creating the Ticket Validator in my WebSecurityConfig

    SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator(); 
    ticketValidator.setServicePrincipal("HTTP/host@REALM");

    FileSystemResource fs = new FileSystemResource("PATH_TO_KEYTAB");
    ticketValidator.setKeyTabLocation(fs);
    LOGGER.info(fs.exists()); // prints 'true'

Creating the KerberosServiceAuthenticationProvider

This is the configuration of the object which will throw BadCredentialsException.

    KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
    provider.setTicketValidator(sunJaasKerberosTicketValidator());
    provider.setUserDetailsService(myUserDetailService);
    provider.supports(KerberosServiceRequestToken.class);

I know SSO works

I have the luxury of being able to proof that the SSO infrastructor of my company works. The same server is running another app (Spring Security 4 with Kerberos) where a user can be successfully authenticated via SSO. So there is most likely something wrong with my setup.

I use Chrome by the way, but I also tested it with IE.

If you need additional information from my WebSecurityConfig or something else I will provide it. May the force be with you :-)

Other questions

This is what I found so far but these examples are slightly different.


Solution

  • It's been 2 years since I had that problem, but I remember, that I solved it... and forgot to post the solution here (my bad). Since the question got some new attention I'll try my best to remember.

    I dug out the code and I think I rembered the problem. In the WebSecurityConfig there are some methods annotated as @Bean - and one was missing. I think is was the SunJaasKerberosTicketValidator. It is used in this class to configure the KerberosServiceAuthenticationProvider but Spring Security seems to use that Bean internally too - and fails if the bean is missing in the Spring Context.

    Here is a (shortened) version of my code from back then. Check all methods annotated with @Bean if you have them too.

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
            auth.authenticationProvider(kerberosServiceAuthenticationProvider());  
        }
    
        @Override
        public void configure(WebSecurity web) {
            // ...
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //...
        }
    
        @Bean
        public SpnegoEntryPoint spnegoEntryPoint() {
            //...
        }
    
        private KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
            KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
            provider.setTicketValidator(ticketValidator());
            provider.setUserDetailsService(myUserDetailsService);
            return provider;
        }
    
        @Bean
        public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter() {
            SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
            AuthenticationFailureHandler failureHandler =
                new SimpleUrlAuthenticationFailureHandler(AUTHENTIFICATION_FAILED_URL);
            filter.setFailureHandler(failureHandler);
            try {
                filter.setAuthenticationManager(authenticationManagerBean());
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
            return filter;
        }
    
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Bean
        public SunJaasKerberosTicketValidator ticketValidator() {
            SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
            ticketValidator.setDebug(true);
            ticketValidator.setServicePrincipal(kerberosConfigMgmt.securityKerberosServicePrincipal());
    
            FileSystemResource fs = new FileSystemResource(kerberosConfigMgmt.securityKerberosKeyTapFileAbsolutePath());
            ticketValidator.setKeyTabLocation(fs);
    
            return ticketValidator;
        }
    
        @Bean(name = "authenticationSuccessHandler")
        public AuthenticationSuccessHandler authenticationSuccessHandler() {
            return new SimpleUrlAuthenticationSuccessHandler(STARTSEITE_URL);
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
    }