> ## Content Index
> Fetch the complete content index at: https://www.ostering.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Configuring Tomcat Single Sign-on with SPNEGO (Kerberos & LDAP)

- URL: https://www.ostering.com/configuring-tomcat-single-sign-on-with-spnego-kerberos-and-ldap/
- Published: 2015-11-20T07:24:00.000Z
- Updated: 2026-07-20T08:44:59.341Z
- Description: Configuring Tomcat Single Sign-on with SPNEGO (Kerberos & LDAP), ostering.com, Brett Crawley
- Author: Brett Crawley
- Tags: Application Security, Auth, Java

So what is SPNEGO? 
SPNEGO stands for Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO). It is a mechanism by which an authenticating body negotiates with the authenticator what security protocol to use, for example Kerberos, NTLM, Digest or Basic 

---readmore---

Originally posted on [DZone][1] 

# PART 1 In The Same Forest

## Introduction 
I'd like to start by explaining some of the terms that will be used throughout this article. 

### So what is SPNEGO? 
SPNEGO stands for Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO). It is a mechanism by which an authenticating body negotiates with the authenticator what security protocol to use, for example Kerberos, NTLM, Digest or Basic 

### What is the difference between Authentication and Authorization? 
Authentication is the act of verifying a user is who they say they are. Whereas authorization is the act of verifying that the user has sufficient permissions to access the data they are requesting. The difference is quite important so I wanted to introduce it early on. 

### Tomcat 
Single Sign-on in Tomcat is handled as a two step process. First authentication is handled by a valve component. A valve component is an element in the request processing chain. And then the authorization is handled by the Realm. The Realm is essentially a user database that contains a collection of usernames, groups and their associated roles. 

### Single Sign-on 
When you logon to a computer you are authenticating with either that local machine or some central server. Under normal circumstances if you then try and connect to an internal web application, that requires you be a user with certain rights, you would be prompted to login. However in the case of single sign-on this should all be transparent. 

![Kerberos Single Sign-on][2] 

### Kerberos 
The user logs into Windows, they are authenticated with the Key Distribution Centre (KDC) in the case of Windows this would be the Primary Domain Controller. The OS of the client receives a TGT token for the user. When they try to connect to the Tomcat server the authentication mechanism is negotiated and their token is passed to Tomcat who then verifies it with the KDC. Once they have been authenticated Tomcat then retrieves their roles from the LDAP server in the case of Windows the Active Directory and decides if they have access to the resource they have requested on the server. The sequence diagram below shows the interactions that take place in more detail. 

![Kerberos sequence diagram][3] 

Both the user and the Tomcat server have their own TGT token (credential associated with their identity). In the sequence diagram you can see outlined the division of responsibility between the Tomcat valve and the Tomcat realm. The valve is taking care of the authentications and the realm is taking care of the authorizations. Some internal interactions have been excluded to reduce the complexity of the diagram in particular related to Pre-Authentication Data PADATA which is used as part of the key sharing mechanism. 

### LDAP 
Entries in LDAP are hierarchical and have each entry has a Distinguished Name (DN) that reflects that hierarchy. For example the DN for a user might be **CN=Brett Crawley, OU=Development, DC=mydomain, DC=com** where CN stands for common name, OU stands for organizational unit and DC stands for domain component. There will be one DC for each component of the domain. In many organizations Users are split up by organization unit within LDAP and in some LDAP repositories this happens at the root of the tree. This can cause problems when searching for users because it is necessary to state the entry point at which to start searching. This entry point can only be one and therefore if your LDAP repository is organized in this manner it is necessary to user what is known as the Global Catalogue. 

* * *

#### NOTE: This requires binding to be carried out at the root and it is necessary to use a different port to connect to the LDAP server

* * *

1.  Create a technical user for Tomcat in LDAP.
2.  Create your user groups in LDAP.
3.  Ensure that forward and reverse DNS resolutions are correctly configured for all servers involved.
4.  Associate a Service Principal Name (SPN) on the domain controller with the technical user, that is, associate 4 principals: 
    *   HTTP/ **FQDN** @ **REALM**
    *   HTTP/ **FQDN**
    *   HTTP/ **HOSTNAME** @ **REALM**
    *   HTTP/ **HOSTNAME**
    *   setspn -a **PRINCIPAL** **TECHNICAL_USER** 

5.  On the Domain Controller and on the Tomcat Server (if Windows), open the local security policy administration tool 

    1.  Go to "> local policies > security options"
    2.  Look for “Network Security: Configure encryption types allowed for Kerberos”

![][4]

If the value is not defined, this means that all types are enabled, contrary to what is written. 

![][5] 

However, if some value is set, either retain or add 
	*   RC4_HMAC
	*   AES128
	*   AES256
	*   Future Encryption Types

![][6] 

If you inadvertently set this value, you can remove it by going into the registry and deleting the value in the following hive of the registry: 

```reg
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters
```

![][7]

6.  On the Domain Controller, create a keytab file with the following command. The path is where the **tomcat.keytab** is written, and is a temporary location because the keytab is copied to the host machines: 

```bash
ktpass /out PATH/tomcat.keytab 
/mapuser TECHNICAL_USER@REALM 
/princ HTTP/ FQDN@REALM /Pass PASSWORD 
/crypto AES256-SHA1 ptype KRB5_NT_PRINCIPAL
```

Now copy the keytab file to the Tomcat server(s) into the `/conf` folder</p>

7.  In the Active directory, open the technical user properties and go to the account tab. Flag the following values: 
    *   Password never expires = true
    *   User cannot change password = true
    *   This account supports Kerberos AES128
    *   This account supports Kerberos AES256
    *   Use Kerberos DES encryption types for this account = should preferably be false
    *   The other settings are at your discretion as the are not pertinent to this setup.

![][8]</li>

8.  Still in the technical user properties, go to the delegation tab (which appeared as a consequence of step 6) and set the following value to true: 
`Trust this user for delegation to any service (Kerberos only). `
	
![][9]</li>

9.  Create the **krb5.ini** or **krb5.conf** file (depending on your platform) in the folder: 

```bash
CATALINA_BASE /conf
```
 
Alternatively, you can point to another file containing this configuration by setting the property: 

```bash
-Djava.security.krb5.conf=PATH_TO_KRB_CONF
```

The contents of the file should look like the following: 

```properties
[libdefaults]
default_realm=REALM
default_keytab_name="CATALINA_BASE/conf/tomcat.keytab"
default_tkt_enctypes=aes256-cts-hmac-shal-96,aes128-cts-hmac-shal-96
default_tgs_enctypes=aes256-cts-hmac-shal-96,aes128-cts-hmac-shal-96
forwardable=true

[realms]
REALM={
	kdc=DOMAIN_CONTROLLER_FQDN:88
}

[domain_realm]
yourdomain.com=REALM
.yourdomain.com=REALM
```

> Note: Make sure there are no spaces between forwardable, equals and true. Redundancy: In the `krb5.ini`, add multiple KDCs and, in case of a load-balanced set-up, define the load balancer KDC as the master_kdc and the admin_server (defines the host, which is typically the master_kdc): 

```properties
[realms]
REALM={
	kdc = DOMAIN_CONTROLLER_FQDN_1:88
	kdc = DOMAIN_CONTROLLER_FQDN_2:88
	master_kdc = DOMAIN_CONTROLLER_FQDN_LoadBalancer:88
	admin_server = DOMAIN_CONTROLLER_FQDN_LoadBalancer
}
```

10. Add the following Java startup property to the environment variables. 

```bash
-Djavax.security.auth.useSubjectCredsOnly=false
```

Create/edit the `jaas.conf` in the Tomcat conf (`CATALINA_BASE/conf`) or you could set the following property and point to a different file such as `login.conf` as is often referred to in other Kerberos documentation: 

```bash
-Djava.security.auth.login.conf=PATH_TO_LOGIN_CONF
```

   The content of the file (**jaas.conf**/**login.conf**) must include the accept and initiate sections as in the example below. 

```properties
com.sun.security.jgss.krb5.accept {
	com.sun.security.auth.module.Krb5LoginModule
	required
	doNotPrompt=true
	principal="HTTP/FQDN@REALM"
	keyTab="DATA_HOME/conf/tomcat.keytab"
	storeKey=true
	useKeyTab=true
	useTicketCache=true
	isInitiator=true
	refreshKrb5Config=true
	moduleBanner=true
	storePass=true;
};
com.sun.security.jgss.krb5.initiate {
	com.sun.security.auth.module.Krb5LoginModule
	required
	doNotPrompt=true
	principal="HTTP/FQDN@REALM"
	keyTab="DATA_HOME/conf/tomcat.keytab"
	storeKey=true
	useKeyTab=true
	useTicketCache=true
	isInitiator=true
	refreshKrb5Config=true
	moduleBanner=true
	storePass=true
	debug=true;
};
```

11. Update the Java security libraries (Java Cryptography Extension (JCE) Unlimited Strength) to those for Strong Encryption. These libraries can be downloaded here: 
	
	Java 6: <http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html> 
	
	Java 7: <http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html> 
	
	Java 8: <http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html> 
	
Put them in `jre/lib/security` and `jdk/jre/lib/security`

12. Set the registry settings on host: 

```reg
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters

Value Name: allowtgtsessionkey 
Value Type: reg_sz or reg_dword 
Value: 1
```

![][10]

13. Configure environment variables `JAVA_OPTS`, `CATALINA_HOME` and `CATALINA_OPTS`. In Windows, this can be done here: 
	
![][11] 
	
On Linux, this can be modified in the file: 
```bash
/etc/sysconfig/tomcat
```
14. Enable Kerberos authentication in the browser. In Internet Explorer, you can find this setting in the Internet options menu: 
	
![][12] 

In Firefox, you must type "`about:config`" as the address, and then click on the button that says “I'll be careful, I promise!”. Once you have access to the settings, type “neg” in the search field and edit the fields highlighted below: 
	
![][13] 
	
![][14]

15. Modify the login-conf section of your web.xml file to use the SPNEGO auth method. 
```xml
<login-config>
	<auth-method>SPNEGO</auth-method>
</login-config>
```
Add security roles for the roles you added to LDAP in step 2. 
```xml
<security-role>
	<description>Users</description>
			<role-name>WebAppUsers</role-name>
</security-role>
<security-role>
	<description>Admins</description>
	<role-name>WebAppAdmins</role-name>
</security-role>
```

16. Then modify your security constraint sections of the web.xml, setting the auth-constraint roles to match the ones you specified in step 2. as shown below. 
```xml
<security-constraint>
		<web-resource-collection>
				<web-resource-name>Common Area</web-resource-name>
				<url-pattern>/common/*</url-pattern>
				<http-method>GET</http-method>
				<http-method>POST</http-method>
				<http-method>PUT</http-method>
				<http-method>HEAD</http-method>
				<http-method>TRACE</http-method>
				<http-method>DELETE</http-method>
				<http-method>OPTIONS</http-method>
		</web-resource-collection>
		<auth-constraint>
				<role-name>WebAppUser</role-name>
				<role-name>WebAppAdmin</role-name>
		</auth-constraint>
		<user-data-constraint>
				<transport-guarantee>NONE</transport-guarantee>
		</user-data-constraint>
</security-constraint>

<security-constraint>
		<web-resource-collection>
				<web-resource-name>User Area</web-resource-name>
				<url-pattern>/user/*</url-pattern>
				<http-method>GET</http-method>
				<http-method>POST</http-method>
				<http-method>PUT</http-method>
				<http-method>HEAD</http-method>
				<http-method>TRACE</http-method>
				<http-method>DELETE</http-method>
				<http-method>OPTIONS</http-method>
		</web-resource-collection>
		<auth-constraint>
				<role-name>WebAppUser</role-name>
		</auth-constraint>
		<user-data-constraint>
				<transport-guarantee>NONE</transport-guarantee>
		</user-data-constraint>
</security-constraint>

<security-constraint>
		<web-resource-collection>
				<web-resource-name>Admin Area</web-resource-name>
				<url-pattern>/admin/*</url-pattern>
				<http-method>GET</http-method>
				<http-method>POST</http-method>
				<http-method>PUT</http-method>
				<http-method>HEAD</http-method>
				<http-method>TRACE</http-method>
				<http-method>DELETE</http-method>
				<http-method>OPTIONS</http-method>
		</web-resource-collection>
		<auth-constraint>
				<role-name>WebAppAdmin</role-name>
		</auth-constraint>
		<user-data-constraint>
				<transport-guarantee>NONE</transport-guarantee>
		</user-data-constraint>
</security-constraint>
```

17. Modify the server.xml to use the SPNEGO valve and the JNDI Realm. 
  * * *
>#### NOTE: In the JNDI realm you should not include either the username or password as they will be ignored when using SPNEGO as the implementation of the realm does not support this configuration. It is assumed you will be using the GSSAPI
* * *
>#### NOTE: Do not use the userPattern because this also is not supported by Tomcat when using SPNEGO

### Replace property values as required.
```xml
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
	<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off"/>
	<Listener className="org.apache.catalina.core.JasperListener"/>
	<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
	<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
   	<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
	<Service name="Catalina">
   		<Connector port="8080" maxSavePostSize="2097152" URIEncoding="UTF-8" maxHttpHeaderSize="65536"/>
		<Engine name="Catalina" defaultHost="localhost">
       		<Realm className="org.apache.catalina.realm.JNDIRealm" 
       			connectionURL="ldap://dc.mydomain.com:3268" userSubtree="true"
           		userBase="cn=Users,dc=mydomain,dc=com"
           		userSearch="(sAMAccountName={0})" userRoleName="memberOf"
           		roleBase="cn=Users,dc=mydomain,dc=com" roleName="cn"
           		roleSearch="(member={0})" roleSubtree="true" roleNested="true"/>
			<Host name="localhost" appBase="webapps">
				<Context docBase="ROOT.war" path="">
					<Valve className="org.apache.catalina.authenticator.SpnegoAuthenticator"
						storeDelegatedCredential="true"/>
				</Context>
			</Host>
		</Engine>
	</Service>
</Server>
```

## Testing your Kerberos Config

### kinit

On Windows, copy your `krb5.ini` to the Windows directory. On Linux, copy your `krb5.conf` file to the `/etc` folder. 

```bash
kinit -k -t PATH_TO_KEYTAB SERVICE_PRINCIPAL (HTTP/ FQDN @ REALM )
```

kinit is for testing that krb5 config is correct. 
```bash
kinit.exe in $JAVA_HOME/bin
```

## Troubleshooting

### BAD RESPONSE 

Issues related to Bad Response could be caused by packet length restrictions. This is related to the token and ticket lengths in the HTTP headers. There are three possible solutions to this: 

1.  Add the `maxHttpHeaderSize` attribute to the connector in the `server.xml` with the value of `65535` 
2.  Run the following command on the servers and the machines suffering this problem. 

```bash
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v "MaxPacketSize" /t REG_DWORD /d 1
```

3.  Add the following to the krb5 config file in the `[libdefaults]` section so that TCP is used instead of UDP: 

```properties
udp_preference_limit=1
```

## Additional Debug Parameters You can add the following Java property to increase the debug information related to Kerberos: 

```bash
-Dsun.security.krb5.debug=true
```

For additional debugging in Apache, you can add the following lines to the logging.properties in the conf directory of Tomcat: 
```java
org.apache.catalina.realm.level = ALL
org.apache.catalina.realm.useParentHandlers = true
org.apache.catalina.authenticator.level = ALL
org.apache.catalina.authenticator.useParentHandlers = true

org.apache.juli.logging.UserDataHelper.CONFIG = INFO_ALL
org.apache.coyote.http11.level = DEBUG
```

For additional logging from the Krb5LoginModule, you can add the following lines to each section of the login.conf 
```properties
debug=true
moduleBanner=true
```

In the server config, you can also add the following attribute to the realm and valve: 
```properties
debug="9"
```

## Cleanup 
In some cases, it may be necessary to empty the ticket cache because it contains tickets that are no longer valid (for example, if you have created a new keytab). Clearing the ticket cache in Windows: 

```batch
klist purge
```

Clearing the ticket cache in Linux: 
```bash
kdestroy
```

## Resources

[MIT Kerberos Documentation][15] 

[Oracle About Kerberos Authentication][16] 

[Tomcat Windows Authentication How-To][17] 

[The Valve Component - SPNEGO Valve][18] 

[Realm Configuration HOW-TO - JNDI Realm][19] 

[The Realm Component - JNDI Directory Realm][20] 

[Common Kerberos Error Messages (A-M)][21] 

[Common Kerberos Error Messages (N-Z)][22] 

[Troubleshooting Kerberos Errors][23] 

[Troubleshooting Kerberos Delegation][24] 

[Enabling Strict KDC Validation in Windows Kerberos][25]

>**Next week I will cover how to configure cross forest authentication and the algorithm necessary to implement cross forest authorization when the root domain components of two trusted domains are different e.g. you have mydomain.com and mydomain.info**

aliases:
    - /blog/2015/11/20/configuring-tomcat-single-sign-on-with-spnego-kerberos-ldap/

 [1]: https://dzone.com/articles/do-not-publish-configuring-tomcat-single-sign-on-w
 [2]: https://www.ostering.com/media/posts/6/kerberos1.png
 [3]: https://www.ostering.com/media/posts/6/kerberos2.png
 [4]: https://www.ostering.com/media/posts/6/local_policy1.png
 [5]: https://www.ostering.com/media/posts/6/local_policy2.png
 [6]: https://www.ostering.com/media/posts/6/local_policy3.png
 [7]: https://www.ostering.com/media/posts/6/registry.png
 [8]: https://www.ostering.com/media/posts/6/account01.png
 [9]: https://www.ostering.com/media/posts/6/delegation01.png
 [10]: https://www.ostering.com/media/posts/6/registry2.png
 [11]: https://www.ostering.com/media/posts/6/env_vars.png
 [12]: https://www.ostering.com/media/posts/6/Internet-Explorer.png
 [13]: https://www.ostering.com/media/posts/6/about_config_1.png
 [14]: https://www.ostering.com/media/posts/6/about_config_2.png
 [15]: http://web.mit.edu/kerberos/krb5-devel/doc/index.html
 [16]: http://docs.oracle.com/cd/E37670_01/E41138/html/ol_kerberosauth_sec.html
 [17]: http://tomcat.apache.org/tomcat-7.0-doc/windows-auth-howto.html
 [18]: http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html#SPNEGO_Valve
 [19]: http://tomcat.apache.org/tomcat-6.0-doc/realm-howto.html#JNDIRealm
 [20]: http://tomcat.apache.org/tomcat-6.0-doc/config/realm.html#JNDI_Directory_Realm_-_org.apache.catalina.realm.JNDIRealm
 [21]: http://docs.oracle.com/cd/E19253-01/816-4557/trouble-6/index.html
 [22]: http://docs.oracle.com/cd/E19253-01/816-4557/trouble-27/index.html
 [23]: http://www.microsoft.com/en-us/download/details.aspx?id=21820
 [24]: http://www.microsoft.com/en-us/download/details.aspx?id=4754
 [25]: http://www.microsoft.com/en-us/download/details.aspx?id=6382
