Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

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

DZone's Guide to

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

Trying to set up a Single Sign-on Tomcat 7 server? Here's how to do so, complete with a look at what SPNEGO is, authentication vs. authorization, and Single Sign-on basics.

· Performance Zone
Free Resource

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 before going into the configuration details for setting up Single Sign-on on a Tomcat 7 server.

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 SSO

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.

SPNEGO Sequence Diagram

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.

Ticket Granting Ticket

Ticket Granting Ticket (TGT) is a small, encrypted identification file with a limited validity period. TGT exists so users don't have to enter their password every time they wish to connect to a kerberized service, or keep a copy of their password around. If the TGT is compromised, an attacker can only masquerade as a user until the ticket expires. When a user first authenticates to Kerberos, s/he talks to the Authentication Service on the KDC to get a TGT. After authentication, this file is granted to a user (e.g. Tomcat, end user) for data traffic protection by the key distribution center (KDC). The TGT file contains the session key, its expiration date, and the user's IP address, which protects the user from man-in-the-middle attacks. The TGT is used to obtain a service ticket from the Ticket Granting Service (TGS). The user is granted access to network services only after this service ticket is provided.

Ticket Granting Service

Ticket Granting Service (TGS) is a principal that can grant tickets to others. When the user wants to talk to a kerberized service, s/he uses the Ticket Granting Ticket to talk to the TGS (e.g. Tomcat to talk to LDAP). The TGS verifies the user's identity using the Ticket Granting Ticket, and issues a ticket for the desired service. In our case, Tomcat becomes a TGS, granting tickets between the Active Directory (AD) and end user, that is, Tomcat is able to piggyback requests of the end user to the AD.

Service Principal and User Principal

The User Principal is the fully qualified username of a user from a particular domain. The server principal is essentially the same thing but is for a computer instead of a user and includes the protocol for which it is valid.

The end user (User Principal or username e.g., user@MYDOMAIN.COM) and Tomcat (Service Principal or username e.g., HTTP/server.mydomain.com@MYDOMAIN.COM) are domain/realm users that have user tokens (TGT) in their local ticket cache. 

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

Configuration Steps

  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 PRINCIPALTECHNICAL_USER
  5. On the Domain Controller and on the Tomcat Server (if Windows), open the local security policy administration tool
    • Go to “> local policies > security options”
    • Look for “Network Security: Configure encryption types allowed for Kerberos”

    Image title

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

    Image title

    However, if some value is set, either retain or add

    • RC4_HMAC 

    • AES128 

    • AES256

    • Future Encryption Types

    Image title

    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:

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

    Image title

  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:

  7. 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

  8. 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.

    Image title

  9. 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:
  10. Trust this user for delegation to any service (Kerberos only).
    Image title

  11. Create the krb5.ini or krb5.conf file (depending on your platform) in the folder:
  12. CATALINA_BASE /conf

    Alternatively, you can point to another file containing this configuration by setting the property:

    -Djava.security.krb5.conf=PATH_TO_KRB_CONF

    The contents of the file should look like the following:

    [libdefaults]
    default_realm=REALM
    default_keytab_name=“CATALINA_BASE/conf/tomcat.keytab"
    default_txt_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 krb5.init, 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):

    [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
    }
  13. Add the following Java startup property to the environment variables. 
  14. -Djavax.security.auth.useSubjectCredsOnly=false
  15. 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: 

  16. -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.


    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;
    };
  17. Update the Java security libraries (Java Cryptography Extension (JCE) Unlimited Strength) to those for Strong Encryption. These libraries can be downloaded here:
  18. 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

  19. Set the registry settings on host:

  20. HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters

    Value Name: allowtgtsessionkey  

    Value Type: reg_sz or reg_dword  

    Value: 1

    Image title

  21. Configure environment variables JAVA_OPTS, CATALINA_HOME and CATALINA_OPTS. In Windows, this can be done here:Image title
  22. On Linux, this can be modified in the file:
    /etc/sysconfig/tomcat
  23. Enable Kerberos authentication in the browser.
  24. In Internet Explorer, you can find this setting in the Internet options menu:

    Image title

    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:

    Image title

    Image title

  25. Modify the login-conf section of your web.xml file to use the SPNEGO auth method.
  26. <login-config>
    <auth-method>SPNEGO</auth-method>
    </login-config>

    Add security roles for the roles you added to LDAP in step 2.

    <security-role>
      <description>Users</description>
      <role-name>WebAppUsers</role-name>
    </security-role>
    <security-role>
      <description>Admins</description>
      <role-name>WebAppAdmins</role-name>
    </security-role>
  27. 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. 

  28. <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>
  29. Modify the server.xml to use the SPNEGO valve and the JNDI Realm.
  30. 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 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.

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

kinit is for testing that krb5 config is correct.

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. she
  3. reg add “HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters” /v “MaxPacketSize” /t REG_DWORD /d 1
  4. Add the following to the krb5 config file in the [libdefaults] section so that TCP is used instead of UDP:  
  5. udp_preference_limit=1

Additional Debug Parameters

You can add the following Java property to increase the debug information related to Kerberos:

-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:

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

debug=true
moduleBanner=true


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:

klist purge

Clearing the ticket cache in Linux:

kdestroy

Resources

MIT Kerberos Documentation

Oracle About Kerberos Authentication

Tomcat Windows Authentication How-To

The Valve Component - SPNEGO Valve

Realm Configuration HOW-TO - JNDI Realm

The Realm Component - JNDI Directory Realm

Common Kerberos Error Messages (A-M)

Common Kerberos Error Messages (N-Z)

Troubleshooting Kerberos Errors

Troubleshooting Kerberos Delegation

Enabling Strict KDC Validation in Windows Kerberos

Next Week

Next week I will cover in depth accessing the LDAP server and 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.

Topics:
kerberos ,tomcat 7 ,security

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}