Monday, October 10, 2011

Using Kerberos with Web Services - part I

This is the first of a two-part series on using Kerberos with Web Services, with Apache WSS4J and CXF. WSS4J 1.6.2 adds support for obtaining a Kerberos ticket from a KDC (Key Distribution Center) and converting it to a BinarySecurityToken to be inserted into the security header of a SOAP request. On the receiving side, support has been added to validate the received Kerberos ticket accordingly. CXF 2.4.2 extends the Kerberos functionality available in WSS4J 1.6.2 to add support for WS-SecurityPolicy. No support is available yet (as of CXF 2.4.2) to use a secret key to sign and encrypt message parts, this will be the subject of part II.

In this post we will talk about installing the MIT Kerberos distribution in Ubuntu, and creating the necessary credentials to run some tests. Then we will go into some system tests in CXF that show how a client can get a Kerberos AP-REQ ticket from a KDC and send it to a service provider, who then authenticates the ticket, all driven by some spring configuration and WS-SecurityPolicy.

1) Installing MIT Kerberos

1.1) Installing the product

In this section we cover how to install the MIT Kerberos distribution in Ubuntu. This is needed to run the CXF Kerberos system tests. See here for more information on using Kerberos on Ubuntu. Open a command prompt and type:
sudo apt-get install krb5-kdc krb5-admin-server
When the product is installing you'll be asked for the default Kerberos Version 5 realm. Enter:
WS.APACHE.ORG
You will then be prompted for the hostnames of Kerberos servers in the WS.APACHE.ORG Kerberos realm. As we are installing the KDC and running the tests on the same machine, we only need to enter "localhost". Similarly, enter "localhost" when prompted for the Administrative server for the Kerberos realm.

1.2) Modifying configuration

Once apt-get has finished, we need to modify the Kerberos configuration file ("sudo vi /etc/krb5.conf"):
  • Under the "[realms]" section, add a "default_domain" entry for ws.apache.org. The entire entry should look like:
    WS.APACHE.ORG = {
                 kdc = localhost
                 admin_server = localhost
                 default_domain = ws.apache.org
    }
  • Under the "[domain_realm]" section, add the following:     
ws.apache.org = WS.APACHE.ORG  
.ws.apache.org = WS.APACHE.ORG
  • Finally, add a logging section:
[logging]

        kdc = FILE:/var/log/krb5kdc.log
        admin_server = FILE:/var/log/kadmin.log
        default = FILE:/var/log/krb5lib.log
1.3) Create principals

The next step is to create some principals. Create a master key via:
sudo kdb5_util create -s
The next step is to start kadmin locally via:
sudo kadmin.local
If you run "listprincs" at the prompt you should see the ticket-granting-ticket principal "krbtgt/WS.APACHE.ORG@WS.APACHE.ORG". We will add a client principal and service principal:
addprinc alice
addprinc bob/service.ws.apache.org
"quit" the kadmin prompt, and start the KDC with "sudo krb5kdc". If you see no error messages then everything should be working correctly. To test this try to get a ticket for "alice" via "kinit alice", entering the password given when creating the "alice" principal.

1.4) Create keytabs

To avoid having to enter passwords when running the tests, we will create Keytabs. Start kadmin.local again ("sudo kadmin.local"), and enter:
ktadd -k /etc/alice.keytab alice
ktadd -k /etc/bob.keytab bob/service.ws.apache.org
To check that the keytabs were create correctly, you can inspect them with klist, e.g. "sudo klist -k /etc/alice.keytab". Finally make sure the keytabs are readable via "sudo chmod og+r /etc/*.keytab" - obviously this is not secure, but it is sufficient for this test application.

2) Running the Kerberos system tests in Apache CXF

Now that we have installed Kerberos and created the relevant principals, we can run the Kerberos system tests in Apache CXF. These tests are @Ignore'd by default. The KerberosTokenTest contains a number of different Kerberos tests. In this article we will just examine the tests that involve obtaining a Kerberos Token, and not any of the tests that involve using the secret key associated with a Kerberos Token to secure some part of the request.

Firstly, make sure that the JDK has unlimited security policies installed, and then checkout the CXF trunk via:
svn co https://svn.apache.org/repos/asf/cxf/trunk/
Go into the "trunk" directory, and compile and install CXF via "mvn -Pfastinstall" (this will avoid running tests). Finally go into the WS-Security system tests in "systests/ws-security". Open KerberosTokenTest.java and comment out the "@org.junit.Ignore" entries for the first four tests, "testKerberosOverTransport", "testKerberosOverSymmetric", "testKerberosOverSymmetricSupporting" and "testKerberosOverAsymmetric". Finally, run the tests via:

        mvn test -Dtest=KerberosTokenTest -Djava.security.auth.login.config=src/test/resources/kerberos.jaas

2.1) WS-SecurityPolicy configuration

The wsdl that defines the service endpoints contains WS-SecurityPolicy expressions that define the security requirements of the endpoints. The following security policies are used for the four tests defined above:
  • testKerberosOverTransport: A (one-way) transport binding is defined, with a KerberosToken required as a SupportingToken. Essentially, this means that the communication is secured with TLS, and authentication is handled by a Kerberos token.
  • testKerberosOverSymmetric: A symmetric binding is used, where a KerberosToken is required as a SignedSupportingToken. 
  • testKerberosOverSymmetricSupporting: A symmetric binding is used, where a KerberosToken is required as a SupportingToken.
  • testKerberosOverAsymmetric: An asymmetric binding is used, where a Kerberos token is required as a SignedSupportingToken.
The WS-SecurityPolicy expression used for a KerberosToken is:
<sp:KerberosToken sp:IncludeToken=".../Once">
    <wsp:Policy>
        <sp:WssGssKerberosV5ApReqToken11/>
    </wsp:Policy>
</sp:KerberosToken>
This means that a GSS V5 AP-REQ Token is required "once", in other words the initial invocation between the client and service endpoint must contain a token of this type encoded as a BinarySecurityToken in the security header of the request.

2.2) Kerberos LoginModule configuration

Both the CXF client and service endpoint use JAAS to authenticate to the KDC. The JAAS file used as part of the system test is passed to the tests via the System property "java.security.auth.login.config". The client (alice) uses the following login module:
alice {
    com.sun.security.auth.module.Krb5LoginModule required
    refreshKrb5Config=true useKeyTab=true keyTab="/etc/alice.keytab" 
    principal="alice";
};
and the service endpoint (bob) uses:
bob {
    com.sun.security.auth.module.Krb5LoginModule required
    refreshKrb5Config=true useKeyTab=true storeKey=true
    keyTab="/etc/bob.keytab" principal="bob/service.ws.apache.org";
}; 
2.3) Service endpoint configuration

The service endpoints are spring-loaded. Each endpoint definition contains the JAX-WS property "ws-security.bst.validator" which is defined in SecurityConstants. WSS4J uses Validator implementations to perform validation on received security tokens. This particular property means that BinarySecurityTokens are to be validated by the given reference, e.g.:
<jaxws:endpoint ...>  
    <jaxws:properties>      
        <entry key="ws-security.bst.validator" value-ref="kerberosValidator"/>
    </jaxws:properties>
</jaxws:endpoint>
"kerberosValidator" is defined as:
<bean id="kerberosValidator"
    class="org.apache.ws.security.validate.KerberosTokenValidator">
    <property name="contextName" value="bob"/>
    <property name="serviceName" value="bob@service.ws.apache.org"/>
</bean>
The KerberosTokenValidator class ships with Apache WSS4J. It requires a "contextName" property, which corresponds to the JAAS context name, as well as an optional "serviceName" property. Combined with the JAAS properties file, this is all that is required for the service endpoint to validate a received Kerberos Token. 

2.4 Client configuration

Finally, the client must contact a KDC and obtain a Kerberos Token, once it sees that the service endpoint has a security policy that requires a KerberosToken. The client configuration is available here. A sample configuration for the Kerberos Test case is as follows:
<jaxws:client name="{...}DoubleItKerberosTransportPort"
       createdFromAPI="true">
       <jaxws:properties>
           <entry key="ws-security.kerberos.client">
               <bean class="org.apache.cxf.ws.security.kerberos.KerberosClient">
                   <constructor-arg ref="cxf"/>
                   <property name="contextName" value="alice"/>
                   <property name="serviceName" value="bob@service.ws.apache.org"/>
               </bean>           
           </entry>
       </jaxws:properties>
</jaxws:client>
The JAX-WS property "ws-security.kerberos.client" (again, defined in SecurityConstants) corresponds to a KerberosClient object. Similar to the KerberosTokenValidator on the receiving side, this is configured with a JAAS context Name and service Name.

4 comments:

  1. Hi Colm,

    if I check out only: https://svn.apache.org/repos/asf/cxf/trunk/systests/ws-security/
    I can not run the test, because maven does not find his parent pom.

    Non-resolvable parent POM: Could not find artifact org.apache.cxf:cxf-parent:pom:2.5.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 21, column 13

    Do I need to checkout the whole trunk, or is there a better solution?

    ReplyDelete
  2. Hi Jan,

    You're right. The best way is to checkout the trunk and do a "mvn -Pfastinstall" before running the system tests. I'll update the blog entry.

    Thanks,

    Colm.

    ReplyDelete
  3. Hello,

    thanks a lot for this tutorial. do you have an example project? I already have a running KDC, but my server and client does not work.

    thanks
    ludi

    ReplyDelete
  4. Hi Ludi,

    Yes, look at section "2" - "Running the Kerberos system tests in Apache CXF ". It goes through some Kerberos tests in Apache CXF, where a client contacts a KDC (set up in section "1") to get a ticket and send it to a service provider using WS-Security.

    Colm.

    ReplyDelete