Apache Tomcat SPNEGO authentication configuration

This is a step-by-step HOW-TO configure AD server and Apache Tomcat server to achieve NTLM single sign-on. We will try to use Tomcat built-in SPNEGO support without 3rd party configuration. Advantage is, that it works out of box. Disadvantage is, that there’s no fallback to BASIC authentication if client doesn’t support SPNEGO autentication.

Environmental parameters

  • domain: eu-central-1.compute.internal – the domain is derived from AWS internal DNS names, easier to resolve
  • realm: EU-CENTRAL-1.COMPUTE.INTERNAL – capital letters of the domain name

AD Server installation

follow this AD step by step tutorial

  1. Name the server (e.g. dc01)
  2. Add server Role – Advice Directory Services
  3. Promote server to Active Directory controller
  4. Add new AD Forest eu-central-1.compute.internal
  5. Create users:
    • tomcat – this user will be used to run the application server service
    • client – this will be used to test the service (or – we can use the administrator user to test)
  6. Add the client user (or administrator if you wish) to the Users group. We need at least one group which will be considered as user role to access the test application. the Users group is already there, so we will reuse it. Please not the users are not automatically in the group, they need to be added manually.
  7. Download and install JRE
  8. Download install Apache Tomcat server
  9. Open port 8080 on the firewall (we won’t use any HTTP proxy for this configuration)
  10. Run the tomcat service as the domain tomcat user. The user should be given permission Logon as Service.

Kerberos configuration

  • register SPN (Service Principal Name) – binding between service and account
    • setspn -A HTTP/dc01.eu-central-1.compute.internal tomcat
    • each service may be bound only to one account
  • create a keytab file
    • ktpass /out c:\tomcat.keytab /mapuser tomcat@eu-central-1.compute.internal /princ HTTP/dc01.eu-central-1.compute.internal@EU-CENTRAL-1.COMPUTE.INTERNAL /pass <tomcat_user_password> /kvno 0 /pType KRB5_NT_PRINCIPAL
    • move C:\tomcat.keytab C:\programs\Tomcat8\conf
  • initialize a Kerberos key (test keytab settings) by Kinit tool
    • java -D”sun.security.krb5.debug”=true  sun.security.krb5.internal.tools.Kinit -k -t C:\programs\Tomcat8\conf\tomcat.keytab HTTP/dc01.eu-central-1.compute.internal@EU-CENTRAL-1.COMPUTE.INTERNAL

Tomcat SPNEGO configuration

For simplification we will run the Apache Tomcat server on the AD domain controller (so we have only a single windows server instance running), however in production the Tomcat will run on different server (Windows or Linux) instance.

First – we will define the user realm, so authenticated users will be recognized and their roles loaded. Let’s define user realm in the conf/server.xml

<Realm
className="org.apache.catalina.realm.JNDIRealm"
connectionURL="ldap://dc01.eu-central-1.compute.internal:389"
authentication="simple"
referrals="follow"
connectionName="cn=tomcat,cn=Users,dc=eu-central-1,dc=compute,dc=internal"
connectionPassword="VerySecretPassword"
userSearch="(sAMAccountName={0})"
userBase="cn=Users,dc=eu-central-1,dc=compute,dc=internal"
userSubtree="true"
roleSearch="(member={0})"
roleName="cn"
roleSubtree="true"
roleBase="cn=Builtin,dc=eu-central-1,dc=compute,dc=internal" />

Validate the AD settings using Active Directory Service management console or simple JXplorer tool to see exact AD LDAP parameters. After this step you should be able to log in into the web applications using active directory username and password (using BASIC or FORM authentication so far)

Create ${CATALINA_HOME}/conf/krb5.ini file:

[libdefaults]
default_realm = EU-CENTRAL-1.COMPUTE.INTERNAL
default_keytab_name = FILE:c:\programs\Tomcat8\conf\tomcat.keytab
default_tkt_enctypes = rc4-hmac,aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96
default_tgs_enctypes = rc4-hmac,aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96
forwardable=true

[realms]
EU-CENTRAL-1.COMPUTE.INTERNAL = {
kdc = dc01.eu-central-1.compute.internal:88
}

[domain_realm]
eu-central-1.compute.internal= EU-CENTRAL-1.COMPUTE.INTERNAL
.eu-central-1.compute.internal= EU-CENTRAL-1.COMPUTE.INTERNAL

Note:

  • on Linux JRE the default file name is krb5.conf
  • custom file path or name can be set in the java.security.krb5.conf system property
  • seems the capital letters in the REALM name are important

Security consideration:

In most of the examples and configuration samples we can find rc4-hmac support for default_tgs_enctypes and/or default_ktk_enctypes. Do not use it. RC4-HMAC is considered weak and is deprecated. However, in this case you may need to download “Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files“. Check that you are downloading the policy files for your current JRE version. Correctly – support for RC4-HMAC should be disabled on the AD server, but it is out of scope of this blog.

This encryption is used to encrypt the Negotiation HTTP headers, so I am ok to use it until proper HTTPS is used. Still – it is safer than fallback to the BASIC authentication and passing cleartext password (regardless encoded).

Create ${CATALINA_HOME}/conf/jaas.conf file:

com.sun.security.jgss.krb5.initiate {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="HTTP/dc01.eu-central-1.compute.internal@eu-central-1.compute.internal"
useKeyTab=true
keyTab="/c:/programs/Tomcat8/conf/tomcat.keytab"
storeKey=true;
};

com.sun.security.jgss.krb5.accept {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="HTTP/dc01.eu-central-1.compute.internal@eu-central-1.compute.internal"
useKeyTab=true
keyTab="/c:/programs/Tomcat8/conf/tomcat.keytab"
storeKey=true;
};

Note:

  • jaas.conf custom name or path can be defined in the  java.security.auth.login.config system property

Web application

As a test application we can create a simple WAR web application with following files:

/index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SPNEGO test</title>
</head>
<body>
<h1>Hello World!</h1>
<p>auth type: <%=request.getAuthType()%> </p>
<p>remote user: <%=request.getRemoteUser() %> </p>
<p>principal: <%=request.getUserPrincipal() %></p>
<p>name: <%= (request.getUserPrincipal()!=null)?request.getUserPrincipal().getName():"NO PRINCIPAL" %></p>
</body>
</html>

/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<session-config>
 <session-timeout>
  30
 </session-timeout>
</session-config>
<security-constraint>
  <display-name>all_auth</display-name>
  <web-resource-collection>
    <web-resource-name>all</web-resource-name>
    <description/>
    <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>Users</role-name>
  </auth-constraint>
  </security-constraint>

  <login-config>
    <auth-method>SPNEGO</auth-method>
    <realm-name>EU-CENTRAL-1.COMPUTE.INTERNAL</realm-name>
  </login-config>
  <security-role>
    <description>App user role</description>
    <role-name>Users</role-name>
  </security-role>
</web-app>

Note:

  • we assume the client user is in the Users group (which is assumed to be user’s role by the Realm definition)
  • we can test it first with the BASIC authentication method, so we see the Realm settings are correct

/META-INF/context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/spnego">
<!-- valve will be explicitly created when SPNEGO is used,
 this is to declare additional attributes -->
  <Valve className="org.apache.catalina.authenticator.SpnegoAuthenticator" 
  alwaysUseSession="true" cache="true"  />
</Context>

I believe the SpnegoAuthenticator valve is added to the context automatically when SPNEGO authentication method is used, in this definition we specified additional settings.

Application Client

By default (according our experiences) the NTLM is directly supported by Internet Explorer and Google Chrome. Mozilla Firefox needs additional configuration.

To achieve SSO we need following configuration:

  • Client workstation must be in AD and user must be logged in by its domain account
  • Client workstation must be other windows instance then server. So it won’t work when we run the application server locally. According to the Tomcat documentation in this case the unsupported NTLM protocol will be used.
  • The application domain URL must be the same as defined in the krb5 configuration file.

Security consideration

NTLM is basically challenge-response authentication with data sent over HTTP headers. As best practice we advice to keep the headers confidential using HTTPS protocol. That could mitigate potential pass-the-hash attack.

Resources

, ,

  1. Leave a comment

Leave a comment