Work in progress

This site is in the process of being reviewed and updated.

Introduction

As an example of using Apache Directory's Kerberos provider, this lesson demonstrates Kerberos authentication to a Wicket webapp. This uses the "SPNEGO+GSS-API+Kerberos V5" scheme popularized by IE and now well-supported in Firefox. The "three-headed" Kerberos setup tested was (1) Firefox 3 and IE 7 (2) Wicket webapp (3) and Apache Directory.

Getting Started

The Java code follows the layout of the "signin" example webapps from Wicket Examples. This uses the jGSS code in JDK 1.6 to support Negotiate processing in Wicket. jGSS will check for system property "java.security.auth.login.config," where you configure it to use a Krb5LoginModule and where you tell it to use a Kerberos key from a keytab file (as opposed to username/password key derivation) and where you also tell it what principal it is operating as.

  1. Check out the example Kerberos + Wicket application.
    $ svn co https://svn.apache.org/repos/asf/directory/sandbox/erodriguez/kerberos-spnego kerberos-spnego
    $ cd kerberos-spnego
    
  2. (OPTIONAL) Import the project into an IDE, such as Eclipse.
    $ mvn eclipse:clean eclipse:eclipse
    
  3. Move the following two files from src/main/resources to anywhere on your filesystem.
    $ mv src/main/resources/http_krb5.config <destination>
    $ mv src/main/resources/http_krb5.keytab <destination>
    
  4. Edit class SignInSpnego and modify the following system property to point to the jGSS config file location from above.
    System.setProperty( "java.security.auth.login.config", "/path/to/http_krb5.config" );
    
  5. Modify the http_krb5.config file's "keyTab" property to point to the keytab file location from above.
    keyTab="/path/to/http_krb5.keytab"
    
    com.sun.security.jgss.accept {
      com.sun.security.auth.module.Krb5LoginModule
      required
      storeKey=true
      doNotPrompt=true
      useKeyTab=true
      realm="EXAMPLE.COM"
      principal="HTTP/www.example.com@EXAMPLE.COM"
      keyTab="/path/to/http_krb5.keytab"
      debug=false;
    };
    
  6. Verify name resolution. Underlying libraries used by the web browser will automatically determine your host name. For this reason the hostname you wish to use should be the top-most entry in your hosts file or matching A and PTR records in DNS. Overloading an IP address with multiple hostnames is common in developer workstations, so checking that the host you wish to use is first in the list to resolve will save you a lot of time later! You will likely find a sniffer (like WireShark) very handy for figuring out what hostnames are being assumed and whether DNS is working properly.
    $ more /etc/hosts
    # Do not remove the following line, or various programs
    # that require network functionality will fail.
    127.0.0.1       localhost.localdomain   localhost
    10.0.0.2        www.example.com
    10.0.0.2        www.apache.org
    
  7. (OPTIONAL) Configure Wireshark. You may also find it useful to have Wireshark (formerly Ethereal) on hand. You can filter on 'kerberos' to listen to traffic between the web browser and the KDC.
    $ yum install wireshark-gnome
    
  8. Configure your host so that it knows where to get Kerberos tickets. On linux this is configured in '/etc/krb5.conf'. The minimum config file must list the default Kerberos realm and the location of at least one key distribution center (KDC). With ApacheDS acting as a KDC and running on our localhost, we can use 'localhost' as the KDC.
    [libdefaults]
     default_realm = EXAMPLE.COM
    
    [realms]
     EXAMPLE.COM = {
      kdc = localhost
     }
    
    [domain_realm]
     .example.com = EXAMPLE.COM
     example.com = EXAMPLE.COM
    
  9. Make sure you are using ApacheDS 1.5.5
  10. Open the server.xml for editing.
    $ cd <trunk>/server-main
    $ vi server.xml
    
  11. Enable the Kerberos protocol provider. By default, the Kerberos protocol is not enabled. You may also change the Kerberos port so that Kerberos can bind if you're logged-in as a non-root user. If you change the default port of '88', you must change the KDC port in the krb5.conf, as well.
    <bean id="kdcConfiguration" class="org.apache.directory.server.kerberos.kdc.KdcConfiguration">
      <!-- Whether to enable the Kerberos protocol.                           -->
      <property name="enabled" value="true" />
      <!-- The port to run the Kerberos protocol on.                          -->
      <property name="ipPort" value="88" />
    </bean>
    
  12. Enable the KeyDerivationService. Kerberos authentication is based on symmetric keys. Since a user can't be expected to remember a symmetric key, there are "key derivation functions" that will produce symmetric key material based on the concatenation of the password, realm, and username. Any changes to the user's password must result in new keys being generated. Luckily, ApacheDS has the "KeyDerivationService" interceptor. This service will intercept any adds or modifications to the user's 'userPassword' attribute and generate keys. Service principals typically use random keys, so the interceptor will generate random keys when the special keyword 'randomKey' is used.
    <bean class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
      <property name="name" value="keyDerivationService" />
      <property name="interceptor">
        <bean class="org.apache.directory.server.core.kerberos.KeyDerivationService" />
      </property>
    </bean>
    
  13. Pre-load principals using an LDIF file. With the KeyDerivationService enabled, you should be able to use LDIFs or LDAP to configure principals on-the-fly. For this example, since the LDIF format is concise, we review some LDIF entries. You will find attached to this page an example LDIF. Download the LDIF and configure the 'ldifDirectory' in server.xml.
    <property name="ldifDirectory">
      <value>/path/to/kerberos-spnego-example.ldif</value>
    </property>
    
  14. Review the LDIF entries. The metaphor for Kerberos comes from the fact that it is "three-headed"; there is always a KDC principal, service principal, and user principal. All of these principals use the same objectClass'es. The attributes are the minimum to satisfy their respective schema, with the exception of the Kerberos schema. Because we are using the KeyDerivationService, we don't need to specify the Kerberos key, key types, or key version number (kvno); they are automatically added by the interceptor, which will also increment the kvno when the password changes. Looking at the LDIF file you'll see the ASL license, an organizational unit (ou) for our 'users' subcontext, and the following entries:

    Entry RDN

    Password

    Principal Name

    Description

    uid=hnelson

    userpassword: s3crEt

    krb5PrincipalName: hnelson@EXAMPLE.COM

    Our user principal. Note the user password.

    uid=krbtgt

    userpassword: randomKey

    krb5PrincipalName: krbtgt/EXAMPLE.COM@EXAMPLE.COM

    The KDC principal, with a random key.

    uid=hosthttp

    userpassword: s3crEt

    krb5PrincipalName: HTTP/www.example.com@EXAMPLE.COM

    The HTTP principal. Note the service password.

  15. (OPTIONAL) Increase logging in server-main/log4j.properties
    log4j.logger.org.apache.directory.kerberos=DEBUG
    
  16. You are now ready to start the server. Upon startup, the server will load the entries from the LDIF.
    $ cd <trunk>/server-main
    $ ./apacheds.sh
    
  17. (OPTIONAL) Make a keytab for the HTTP service principal. In order for a web server (HTTP service) to "run as" a given service principal, we most provision (export) a symmetric key for it to use. While a user principal typically uses a password, a service principal typically uses a random key. This random key is generated by the server and then exported for use by the service. This key is exported into a keytab file. Our HTTP service will look for this keytab file based on the "keyTab" property we set in the jGSS config file. Because key export for ApacheDS is currently (10-JUN-2007) under heavy development, we won't export keys for this example. Instead, we'll take advantage of the fact that key derivation algorithms are standardized and we'll create a keytab based on the HTTP service principal from the LDIF we imported. We stated, above, that service keys are typically random. For this example, we have set the HTTP service to use a password, "s3crEt." By fixing the password like this, we can use Kerberos tools to make a keytab. Alternatively, you may use the http_krb5.keytab we provided for you in src/main/resources. The provided keytab was created using the following commands. When prompted, the password for the service is "s3crEt" (without the quotes).
    $ ktutil
    ktutil:  addent -password -p HTTP/www.example.com@EXAMPLE.COM -k 1 -e des-cbc-md5
    Password for HTTP/www.example.com@EXAMPLE.COM:  s3crEt
    ktutil:  list
    slot KVNO Principal
    ---- ---- ---------------------------------------------------------------------
       1    1 HTTP/www.example.com@EXAMPLE.COM
    ktutil:  wkt /path/to/http_krb5.keytab
    ktutil:  quit
    
  18. (OPTIONAL) Sanity check: verify the presence of the server key in the keytab file. This will work whether or not you used the provided keytab or made your own.
    $ klist -5ke /path/to/http_krb5.keytab
    Keytab name: FILE:/path/to/http_krb5.keytab
    KVNO Principal
    ---- --------------------------------------------------------------------------
       1 HTTP/www.example.com@EXAMPLE.COM (DES cbc mode with RSA-MD5) 
    
  19. (OPTIONAL) Test the http_krb5.keytab. Instead of entering a password with 'kinit', you can also use a keytab file. The following code shows how to "log in" (acquire a TGT) using a keytab file.
    $ kinit -k -t /path/to/http_krb5.keytab HTTP/www.example.com@EXAMPLE.COM
    
    If 'kinit' returns with no message, it worked. Now, when you look in your ticket cache, you'll see default principal is set to the HTTP principal, who now has a TGT.
    $ klist -5fea
    Ticket cache: FILE:/tmp/krb5cc_0
    Default principal: HTTP/www.example.com@EXAMPLE.COM
    
    Valid starting     Expires            Service principal
    02/18/07 20:01:36  02/19/07 20:01:36  krbtgt/EXAMPLE.COM@EXAMPLE.COM
            Etype (skey, tkt): DES cbc mode with RSA-MD5, DES cbc mode with RSA-MD5 
            Addresses: (none)
    
  20. (OPTIONAL) Destroy the HTTP service's credentials. Your default principal is now set to the HTTP key you just tested, so you need to destroy it.
    $ kdestroy
    $ klist -5fea
    klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_0)
    
  21. Request a ticket-granting ticket (TGT) using 'kinit'. If you have not already "logged in," you must request a fresh TGT. Without a TGT your attempt to login to the example web application will silently fail. This is the behavior of the web browser, not ApacheDS. Also, if you don't specify the user principal, kinit will guess the principal name based on the logged-in user and the realm configured in the krb5.conf.
    $ kinit hnelson@EXAMPLE.COM
    Password for hnelson@EXAMPLE.COM:  s3crEt
    $ klist -5fea
    Ticket cache: FILE:/tmp/krb5cc_0
    Default principal: hnelson@EXAMPLE.COM
    
    Valid starting     Expires            Service principal
    02/18/07 20:02:23  02/19/07 20:02:22  krbtgt/EXAMPLE.COM@EXAMPLE.COM
            Etype (skey, tkt): DES cbc mode with RSA-MD5, DES cbc mode with RSA-MD5 
            Addresses: (none)
    
  22. Configure your browser. Both Firefox and IE require configuration of what hosts to try Negotiation with. In Firefox you configure this by typing about:config into the URL address bar. The URL you hit with the web browser must be the hostname of the web server in the about:config "trusted-uris" property.

    Preference Name

    Status

    Type

    Value

    network.negotiate-auth.trusted-uris

    user set

    string

    www.example.com

    In IE, you go to Tools | Internet Options | Security, select "Local intranet" | Sites | Advanced, and Add a website to the Intranet zone. Later, when you visit the website, you will see Intranet (vs. Internet) in the lower right of the browser window.
  23. (OPTIONAL) Enable logging for Firefox
    $ export NSPR_LOG_MODULES=negotiateauth:5
    $ firefox -console
    
  24. Start the web application from the command-line. The 'maven-jetty-plugin' is configured to allow easy launching of the web application.
    $ mvn jetty:run
    
  25. (OPTIONAL) Start the web application within an IDE. In the style of Wicket "Quick Start" applications, this example provides a Start class that you can run as a Java application. This Java application will start an embedded Jetty server and bind to port 8080.
  26. Try the web application. By pointing your browser to "http://www.example.com:8080/spnego/app" you should see a login form. You can either log in with a username/password (hnelson, s3crEt) or explicitly click the "Sign In with SPNEGO/Kerberos" link to attempt SPNEGO/Kerboros v5 login. When you login as "hnelson" you should see the greeting "Welcome, hnelson!" When you login using the Kerberos principal, you should see "Welcome, hnelson@EXAMPLE.COM!" Unfortunately, if anything is misconfigured, browser behavior is to not respond to the SPNEGO challenge. Therefore, the example web application doesn't receive a subsequent request and the login silently dies, in which case you'll see a blank white page.
  27. (OPTIONAL) Adapt this example to your real-world application. In a real-world web application you would probably want to attempt Kerberos authentication by default and not require the explicit link. Similarly, you would map the user's Kerberos principal name to the user's account and thus not differentiate between the two username forms like this.
  28. (OPTIONAL) List your Kerberos credentials. You'll see that in addition to a TGT, you also now have a service ticket for the HTTP server.
    $ klist -5fea
    Ticket cache: FILE:/tmp/krb5cc_0
    Default principal: hnelson@EXAMPLE.COM
    
    Valid starting     Expires            Service principal
    02/18/07 20:02:23  02/19/07 20:02:22  krbtgt/EXAMPLE.COM@EXAMPLE.COM
            Etype (skey, tkt): DES cbc mode with RSA-MD5, DES cbc mode with RSA-MD5 
            Addresses: (none)
    02/18/07 20:06:31  02/19/07 20:02:22  HTTP/www.example.com@EXAMPLE.COM
            Etype (skey, tkt): DES cbc mode with RSA-MD5, DES cbc mode with RSA-MD5 
            Addresses: (none)
    
  • No labels