Troubleshooting SSO using Spring Security and Kerberos

Problem:
I’m trying to get this to work. The article is quite old, but I managed to do all necessary changes.
My kerberos keytabs/tickets (for both sso and ldap) seem to work.

But when trying to log in to opennms with an AD user I get this warning in web.log:
`````2022-04-29 16:45:42,201 WARN [qtp176285712-712] o.o.w.s.s.HybridOpenNMSUserAuthenticationProvider: User not found: user.name```

Expected outcome:
At least for kerberos authentication to work

OpenNMS version:
29.0.9

Other relevant data:
In jetty-server.log I get this error stack thrown whenever I try to log in with an AD user:

2022-04-29 16:45:47,461 WARN  [qtp176285712-712] o.e.j.s.HttpChannel: /opennms/j_spring_security_check
org.springframework.ldap.AuthenticationException: Unable to obtain password from user
; nested exception is javax.naming.AuthenticationException: Unable to obtain password from user
 [Root exception is javax.security.auth.login.LoginException: Unable to obtain password from user
]
        at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:182) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:285) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        at org.springframework.ldap.core.support.AbstractContextSource.doGetContext(AbstractContextSource.java:119) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        at org.springframework.ldap.core.support.AbstractContextSource.getReadOnlyContext(AbstractContextSource.java:138) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        at org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:791) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:194) ~[spring-security-ldap-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:116) ~[spring-security-ldap-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.opennms.web.springframework.security.KerberosLdapAuthenticationProvider.authenticate(KerberosLdapAuthenticationProvider.java:75) ~[org.opennms.features.springframework-security-29.0.9.jar:?]
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156) ~[spring-security-core-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:92) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:107) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:107) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[org.apache.servicemix.bundles.spring-web-4.2.9.RELEASE_1.jar:?]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) ~[spring-security-web-3.2.7.RELEASE.jar:3.2.7.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[org.apache.servicemix.bundles.spring-web-4.2.9.RELEASE_1.jar:?]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[org.apache.servicemix.bundles.spring-web-4.2.9.RELEASE_1.jar:?]
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:231) ~[org.apache.servicemix.bundles.spring-orm-4.2.9.RELEASE_1.jar:?]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[org.apache.servicemix.bundles.spring-web-4.2.9.RELEASE_1.jar:?]
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.opennms.web.security.JSessionIdNoCacheFilter.doFilter(JSessionIdNoCacheFilter.java:68) ~[?:?]
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) ~[org.apache.servicemix.bundles.spring-web-4.2.9.RELEASE_1.jar:?]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[org.apache.servicemix.bundles.spring-web-4.2.9.RELEASE_1.jar:?]
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:201) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:548) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[jetty-security-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1434) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501) ~[jetty-servlet-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1349) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.opennms.netmgt.jetty.MDCHandler.handle(MDCHandler.java:46) ~[opennms-jetty-29.0.9.jar:?]
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:400) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:645) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:392) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) ~[jetty-server-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) ~[jetty-io-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) ~[jetty-io-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) ~[jetty-io-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) ~[jetty-util-9.4.44.v20210927.jar:9.4.44.v20210927]
        at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: javax.naming.AuthenticationException: Unable to obtain password from user

        at org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource.login(KerberosLdapContextSource.java:151) ~[spring-security-kerberos-client-1.0.1.RELEASE.jar:1.0.1.RELEASE]
        at org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource.getDirContextInstance(KerberosLdapContextSource.java:110) ~[spring-security-kerberos-client-1.0.1.RELEASE.jar:1.0.1.RELEASE]
        at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:273) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        ... 73 more
Caused by: javax.security.auth.login.LoginException: Unable to obtain password from user

        at com.sun.security.auth.module.Krb5LoginModule.promptForPass(Krb5LoginModule.java:877) ~[jdk.security.auth:?]
        at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:740) ~[jdk.security.auth:?]
        at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:592) ~[jdk.security.auth:?]
        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:747) ~[?:?]
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:672) ~[?:?]
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:670) ~[?:?]
        at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
        at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:670) ~[?:?]
        at javax.security.auth.login.LoginContext.login(LoginContext.java:581) ~[?:?]
        at org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource.login(KerberosLdapContextSource.java:147) ~[spring-security-kerberos-client-1.0.1.RELEASE.jar:1.0.1.RELEASE]
        at org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource.getDirContextInstance(KerberosLdapContextSource.java:110) ~[spring-security-kerberos-client-1.0.1.RELEASE.jar:1.0.1.RELEASE]
        at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:273) ~[spring-ldap-core-1.3.2.RELEASE.jar:1.3.2.RELEASE]
        ... 73 more

My guess this is LDAP related, but I have no idea how to debug/troubleshoot

Can you kinit -kt /path/to/your.keytab spName@REALM.COM successfully?

e.g.:

[root@opennms etc]# kinit -kt creds/opennms-ws.keytab opennms-ws@AD.EXAMPLE.COM
[root@opennms etc]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: opennms-ws@AD.EXAMPLE.COM

Valid starting       Expires              Service principal
04/29/2022 11:08:30  04/29/2022 21:08:30  krbtgt/AD.EXAMPLE.COM@AD.EXAMPLE.COM
        renew until 05/06/2022 11:08:30

That’s what I meant with “My kerberos keytabs/tickets (for both sso and ldap) seem to work”. But seems I had a permissions issue on the ldap keytab file to begin with. And opennms does not run as root anymore obviously…

After fixing some other things in sso_activeDirectory_kerb_ldap.xml I’m able to authenticate with an AD user. But now I’m getting an Access denied in opennms.
Do I still have to create a mirror user in opennms with the same username as the AD user?

Ok, to answer my own question: no mirror user needed. Seems to work now, including role assignment.

Just another question: Does OpenNMS have a real SSO endpoint? As in when I go there, my Windows AD user logs in automatically.

That is what should happen, if Krb/LDAP is configured correctly, and your browser is configured to attempt Kerberos auth by default.

Ok I’ll look into that, it’s a browser thing.

Another related question: how do I put such a user in an on-call role or notification path if the user doesn’t exist in opennms?

You’d have to create a local user in OpenNMS to do that, but the corresponding directory user would still be used for authentication.

1 Like

Good afternoon Dieter
I would like to know what you changed “fixing some other things in” in the files:
applicationContext-spring-security.xml and sso_activeDirectory_kerb_ldap.xml.
I’m using the 29.0.10 version. Grateful if you can help me.

It was more like fixing syntax of parameters.

e.g. (look at the arrows —>, don’t copy them) in sso_activeDirectory_kerb_ldap.xml

  <bean id="kerberosServiceAuthenticationProvider"
    class="org.opennms.web.springframework.security.KerberosServiceLdapAuthenticationProvider">
    <property name="ticketValidator">
      <bean
        class="org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator">
        <!-- Name of the Kerberos service principal designated for the OpenNMS webapp -->
        <property name="servicePrincipal" value="xxxxxx" />
 ---> syntax of that value is "HTTP/serviceprincipal.domain.local@DOMAIN.LOCAL"
        <!-- Location of the Kerberos keytab file. Must be of the form file:/path/to/keytab -->
        <property name="keyTabLocation" value="file:/etc/opennms/credentials/opennms.sso.keytab" />
        <property name="debug" value="true" />
      </bean>
    </property>
    <property name="ldapUserSearch" ref="kerberosLdapUserSearch"/>
    <property name="ldapAuthoritiesPopulator" ref="kerberosUserGroupLdapAuthoritiesPopulator"/>
  </bean>

and

  <bean id="kerberosLdapContextSource"
    class="org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource">
    <constructor-arg>
      <list>
        <!-- List one or more of your LDAP servers here -->
        <value>xxxxxx</value>
---> value is in format "ldap://ad01.domain.local:389/"
        <value>xxxxxx</value>
      </list>
    </constructor-arg>
    <!-- Base DN for LDAP searches. Every group below is relative to this. -->
    <constructor-arg value="xxxxxx" />
---> value "dc=domain,dc=local"
    <property name="loginConfig" ref="ldapLoginConfig" />
  </bean>

and

  <bean id="ldapLoginConfig"
    class="org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig">
    <!-- Name of the Kerberos service principal designated for the OpenNMS webapp -->
    <property name="servicePrincipal" value="xxxxxx" />
---> value "ldapserviceprincipal@DOMAIN.LOCAL"
    <!-- Location of the Kerberos keytab file. Must be of the form file:/path/to/keytab -->
    <property name="keyTabLocation" value="file:/etc/opennms/credentials/opennms.ldap.keytab" />
    <property name="debug" value="true" />
    <property name="isInitiator" value="true" />
  </bean>

Also make sure your keytabs work.

Hope it helps

Dear Dieter, thanked for responding. I checked the configuration files and they were configured correctly according to the examples you kindly sent me. Knowing that I looked for the fault elsewhere and found it. The file “applicationContext-spring-security.xml” was in a newer version (30.x.x) I am using version 29.0.10 of Opennms. This caused incompatibility leading to failure. Fault resolved and SSO working integrated with AD on an Ubuntu 20.04. Thanks.