Security settings for Kerberos and SPNEGO

This guide will help you set up a basic Single sign-on authentication with Keberos and SPNEGO using Spring Security under Weave. In our tests, we have been using Windows Server 2016 as our Domain Controller and Weave Server and Windows 10 as our clients.


Prerequisite
  • Windows Server with domain controller and AD setup. Kerberos enabled.
  • Windows client joined to the Windows domain

Setting up Windows Domain controller

In this example we use the DNS domain example.org and Windows Domain EXAMPLE. We have also created a weave domain user which is used to perform the authentication from the Weave server to the Domain Controller.


Add a Service Principal Name(SPN) on the Windows Domain Controller. It need to be setup with HTTP and a server name where the Weave instance is run. This is used with the weave domain user and the generated keytab file is then used as the service credentials.

To add the SPN, open the command line tool and run the following command:

C:\> setspn -A HTTP/example.org weave


Export a keytab file for the weave user. This file will be used with Weave to authenticate against the Windows Domain Controller. Copy the exported keytab file (weave.keytab) to the platform/workspace directory in Weave. The keytab file will be referenced in the security setup for Weave.

C:\> ktpass /out c:\weave.keytab /mapuser weave@EXAMPLE /princ HTTP/example.org@EXAMPLE/pass Password# /ptype KRB5_NT_PRINCIPAL /crypto All

Targeting domain controller: EXAMPLE.example.org
Using legacy password setting method
Successfully mapped HTTP/example.org to weave.

Setting up security.xml in Weave

Plugins

There are two plugins related to Kerberos that need to be added to Weave in order for the following configuration to work. The plugins are attached to this page.


Example configuration using SPNEGO/Kerberos and LDAP as User Detail Service.

Example
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:sec="http://www.springframework.org/schema/security"
  xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

  <!-- Configuration uses SPNEGO by default -->
  <sec:http use-expressions="true" entry-point-ref="spnegoEntryPoint" >
    <sec:intercept-url pattern="/login*" access="permitAll" />
    <sec:intercept-url pattern="/login/**" access="permitAll" />
    <sec:intercept-url pattern="/security/**" access="permitAll" />
    <sec:intercept-url pattern="/**" access="authenticated" />
    <sec:custom-filter ref="spnegoAuthenticationProcessingFilter"
      position="BASIC_AUTH_FILTER" />
    <sec:form-login login-page="/login.html" default-target-url="/index.html"/>
  </sec:http>

  <!-- Spnego Kerberos configuration -->
  <bean id="spnegoEntryPoint" class="org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint" />

  <bean id="spnegoAuthenticationProcessingFilter" class="org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager" />
  </bean>

  <sec:authentication-manager alias="authenticationManager">
    <sec:authentication-provider ref="kerberosServiceAuthenticationProvider" />
  </sec:authentication-manager>
  
  <bean id="kerberosServiceAuthenticationProvider"
    class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider">
    <property name="ticketValidator">
      <bean
        class="org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator">
        <property name="servicePrincipal" value="HTTP/example.org@EXAMPLE" />
        <property name="keyTabLocation" value="weave.keytab" />
        <property name="debug" value="true" />
      </bean>
    </property>
    <property name="userDetailsService" ref="ldapUserDetailsService" />
  </bean>

  
<!-- LDAP -->
  <bean id="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
    <constructor-arg>
      <bean class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
        <constructor-arg index="0" value="dc=example,dc=org"/>
        <constructor-arg index="1" value="(| (userPrincipalName={0}) (sAMAccountName={0}))"/>
        <constructor-arg index="2" ref="ldapContextSource" />
      </bean>
    </constructor-arg>
    <constructor-arg>
      <bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
        <constructor-arg ref="ldapContextSource"/>
        <constructor-arg value="CN=Users,DC=example,DC=org"/>
        <property name="groupRoleAttribute" value="cn"/>
        <property name="searchSubtree" value="true" />
        <property name="groupSearchFilter" value="(member={0})" />
      </bean>
    </constructor-arg>
  </bean>

  <sec:ldap-server id="ldapContextSource" url="ldap://example.org:389/" manager-dn="CN=Administrator,CN=Users,DC=example,DC=org" manager-password="yourPassword" />

</beans>

Upon a user request, the spnegoEntryPoint will be first visited. The Entry point will check the authentication header from the request and validate it. If it is not valid, it will respond with a HTTP 401 and ask the requesting browser to send a negotiation header. On success validation the request will move on to the authenticationManager. In this case, we only have one authentication provider (Kerberos) and the request will be sent to the kerberosServiceAuthenticationProviderkerberosServiceAuthenticationProvider will authenticate the user with the Domain Controller and if successful it will fetch authorities/roles using the ldapUserDetailsService. The ldapUserDetailsService will connect to the Active Directory/LDAP server and fetch the roles for the current user. We provide the LDAP connection credentials with ldapContextSource

Confguration

kerberosServiceAuthenticationProvider

Two properties need to be configured in the kerberosServiceAuthenticationProvider bean. The first one is the servicePrinciple. This value should be the same as the one we used when creating the SPN and keytab file. The keyTabLocation property is the path to the keytab file created earlier. The path is relative to the /workspace directory, but could also be absolute.

ldapUserDetailsService and ldapContextSource

Replace credentials

Additional notes

  • Client and server must not be run on the same machine. The client will send NTML authentication to the server which will invalidate the request.
  • The server must be accessed using it Fully Qualified Name (FQN) in order for authentication to be applied. IP address can not be used.
  • For Single Sign-on to be enabled, Complete following steps to ensure that your Internet Explorer browser is enabled to perform Spnego authentication
    • Go to Tools > Intenet Options > Security tab
    • In Local intranet section make sure your server is listed as a trusted website by adding it into a list.