Securing WSO2 Services with Kerberos Token-based Security

WSO2 Micro Integrator provides 16 predefined, commonly-used security scenarios to secure web services and Kerberos Token-based security is one of them.

I encountered many issues when setting up this due to the lack of proper documentation and some configurations and locations have been changed in the newer products. So I wanted to write this post to show the steps in the latest versions of the products. Hope this would be helpful for anyone who needs to know how to apply Kerberos security in WSO2 Micro Integrator.

🔐Usually, Kerberos is used to authenticate Systems or users within a network. But when we need to access services outside network boundaries, we need to authenticate users with internal credentials. In such cases, we can use this scenario.

🔅Please note that this Security scenario is only applicable if you have a Key Distribution Center (KDC) and an Authentication Server in your environment. Ideally, you can find KDC and an Authentication Server in an LDAP Directory server.

We all know that WSO2 Identity Server(IS) comes with an LDAP Directory server. Hence it acts as KDC(key distribution center). So in this post, we will discuss how to set up and configure the Kerberos Token-based Security using Identity server and Micro Integrator products.

👉 To implement the use case we need to perform the following steps:

  1. Set up KDC server

We used WSO2 Identity server 5.9.0 and WSO2 Micro Integrator 7.1.0(1.2.0) in this blog and same setup tested with the WSO2 Enterprise Integrator 6.6.0 as well.

✔️ Set-up KDC server

Step 01: Download WSO2 Identity Server from here. Extract distribution to a preferred location and Offset the port.

Step 02:

  1. Open $IS_HOME/repository/conf/identity/embedded-ldap.xml

🔹Under <KDCServer> make the property “enable” = true as follows

<Property name="enabled">true</Property>

🔹Disable timestamp “pre-authentication” for this example.

<Property name="preAuthenticationTimeStampEnabled">false</Property>

➡️ So Final KDCServer configurations should be like this.

<KDCServer>
<Property name="name">defaultKDC</Property>
<Property name="enabled">true</Property>
<Property name="protocol">UDP</Property>
<Property name="host">localhost</Property>
<Property name="port">${Ports.EmbeddedLDAP.KDCServerPort}</Property>
<Property name="maximumTicketLifeTime">8640000</Property>
<Property name="maximumRenewableLifeTime">604800000</Property>
<Property name="preAuthenticationTimeStampEnabled">false</Property>
</KDCServer>

🙋 If you want to change the default realm of the KDC, change the “realm” property. By default it’s WSO2.ORG. We’ll keep it as it’s in this case for simplicity.

<Property name="realm">WSO2.ORG</Property>

Step 03: Next, open $IS_HOME/repository/conf/user-mgt.xml.

🔹Set following property to true, under “LDAPUserStoreManager” configuration,

<Property name=”kdcEnabled”>true</Property>.

Step 04: Go to $IS_HOME/bin and run “./wso2server.sh” to Start Identity Server

☑️ If Identity Server configured properly, you can see the below log in the console when it is starting.

INFO {org.apache.directory.server.kerberos.kdc.KdcServer} - Kerberos service started.

☑️ Once started the server, we have to create a Service Principal (SPN) and a client principal to use with Kerberos ticket-granting system (TGS)

SPN:- A service principal name (SPN) is a unique identifier of a service instance. SPNs are used by Kerberos authentication to associate a service instance with a service logon account.

💡 The KDC Server issues tickets to access a particular service. Thus, each type of service is associated with a particular service name. In the case of Kerberos, we call the service name “Service Principal Name” (SPN). Before we assign an SPN to service, we need to define the “Service Principal Name” in KDC.

In order to configure a service principal, you must first register a service provider for inbound authentication.

👉 You can follow up on the following Documentation to get further information regarding this …

Step 05:

🔹Sign in to the management console of the Identity Server.

🔹 Navigate to the Main menu to access the Identity menu. Click Add under Service Providers.

🔹 Fill in the Service Provider Name.

🔹Click Register to add the new service provider.

🔹Navigate to Kerberos KDC under Inbound Authentication Configuration.

🔹Add Service Principal Details.

Service Name : esb/localhost
Password : asara
Re-Enter Password : asara

Step 06: Create Client Principle that means “User” in the LDAP server.

🔹Navigate to the Main menu to access the Users and Roles menu.

🔹 Add User.

 Username  : asara
Password : asara
User Role : admin

That’s all with WSO2 IS KDC configuration😀. Let’s now configure the Local Machine for the Kerberos setup.

✔️ Configure Local Machine for Kerberos

Step 01: Create or Change the krb5.conf file which is in /etc folder with the below details.

[libdefaults]
default_realm = WSO2.ORG
default_tkt_enctypes = des-cbc-md5 des-cbc-crc des3-cbc-sha1
default_tgs_enctypes = des-cbc-md5 des-cbc-crc des3-cbc-sha1
permitted_enctypes = des-cbc-md5 des-cbc-crc des3-cbc-sha1
allow_weak_crypto = true

[realms]
WSO2.ORG = {
kdc = 127.0.0.1:8000
}

[domain_realm]
.wso2.org = WSO2.ORG
wso2.org = WSO2.ORG

Please note that the default realm of our KDC server is WSO2.ORG and krb5.conf file reads from the default /etc location

That’s all with configuring Local Machine 😀. Let’s now configure the Micro Integrator for the Kerberos setup.

✔️ Setup Micro Integrator for Kerberos

Step 01:

🔹Download WSO2 Micro Integrator from here. Extract distribution to a preferred location.

🔹Create ‘<MI_HOME>/repository/conf/identity’ folder.

🔹Create a file as jaas.conf file inside the identity folder and copy and paste the below content to it.

Client {    
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=false;
};

Step 02:

🔹Create a Carbon Application with the below content using the Integration studio.

  • Kerberos Token-based WS-Security policy file

You can find a sample Carbon Application from here.

Step 03:

🔹Deploy the Kerberos Token-based WS-Security policy file as a registry resource and define the below properties according to your Kerbores setup.

🔺 client.principal.name 🔺client.principal.password

🔺 service.principal.name

<rampart:kerberosConfig>       <rampart:property  name="client.principal.name">asara</rampart:property>
<rampart:property name="client.principal.password">asara</rampart:property>
<rampart:property
name="service.principal.name">esb/localhost@WSO2.ORG</rampart:property>
<rampart:property name="javax.security.auth.useSubjectCredsOnly">true</rampart:property>
</rampart:kerberosConfig>

⦿ The full policy file should be like this:

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="kerberossignandencrypt">
<wsp:ExactlyOne>
<wsp:All>
<sp:SymmetricBinding
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:ProtectionToken>
<wsp:Policy>
<sp:KerberosToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssKerberosV5ApReqToken11/>
</wsp:Policy>
</sp:KerberosToken>
</wsp:Policy>
</sp:ProtectionToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:SymmetricBinding>
<sp:SignedParts
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
</sp:SignedParts>
<sp:Wss11
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Policy>
<sp:MustSupportRefKeyIdentifier/>
<sp:MustSupportRefIssuerSerial/>
<sp:MustSupportRefThumbprint/>
<sp:RequireSignatureConfirmation/>
</sp:Policy>
</sp:Wss11>
<sp:Trust10
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
<sp:MustSupportIssuedTokens/>
</wsp:Policy>
</sp:Trust10>
<rampart:RampartConfig
xmlns:rampart="http://ws.apache.org/rampart/policy">
<rampart:timestampPrecisionInMilliseconds>true</rampart:timestampPrecisionInMilliseconds>
<rampart:timestampTTL>300</rampart:timestampTTL>
<rampart:timestampMaxSkew>300</rampart:timestampMaxSkew>
<rampart:timestampStrict>false</rampart:timestampStrict>
<rampart:nonceLifeTime>300</rampart:nonceLifeTime>
<rampart:kerberosConfig>
<rampart:property name="client.principal.name">asara</rampart:property>
<rampart:property name="client.principal.password">asara</rampart:property>
<rampart:property name="service.principal.name">esb/localhost@WSO2.ORG</rampart:property>
<rampart:property name="javax.security.auth.useSubjectCredsOnly">true</rampart:property>
</rampart:kerberosConfig>
</rampart:RampartConfig>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>

Step 04: Create a sample proxy service (kerberosSampleProxy) as below.

<?xml version="1.0" encoding="UTF-8"?>
<proxy name="kerberosSampleProxy" startOnLoad="true" transports="http https" xmlns="http://ws.apache.org/ns/synapse">
<target>
<inSequence>
<send>
<endpoint>
<address uri="http://asara:8290/services/backendProxy">
<enableSec outboundPolicy="gov:policies/KerberosPolicy.xml"/>
</address>
</endpoint>
</send>
</inSequence>
<outSequence>

<header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
name="wsse:Security"
action="remove"/>
<send/>
</outSequence>
<faultSequence/>
</target>
</proxy>

Step 05: Create another proxy (backendProxy) service that acts as a backend service.

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="backendProxy"
transports="http https"
startOnLoad="true">
<description/>
<target>
<inSequence>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://services.samples/xsd"
xmlns:ser="http://services.samples">
<soapenv:Header>
<ser:authenticationRequest>
<userName xmlns="">$1</userName>
<password xmlns="">$2</password>
</ser:authenticationRequest>
</soapenv:Header>
<soapenv:Body>
<ser:getQuote>
<ser:request>
<xsd:symbol>$3</xsd:symbol>
</ser:request>
</ser:getQuote>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args>
<arg value="asara"/>
<arg value="password123"/>
<arg value="helloWorld"/>
</args>
</payloadFactory>
<respond/>
</inSequence>
</target>
</proxy>

That’s all with configuring Micro Integrator😀. Let’s now configure the JAVA Client.

✔️ Set up the JAVA client to invoke service

🔁 The message flow is:-

Java Client(non-secure) —> EI(non-secure proxy) —> Backend(secured)

Now you can try invoking the kerberosSampleProxy using soap UI and it should fail saying “security header does not found”. This is because the request didn’t contain any security information.

According to the specification, the message payload should be signed from Kerberos ticket and having a timestamp, and etc. So to achieve that requirement we have to write a JAVA client program and Please find the attached sample java client org.wso2.identity.esb.kerberos.zip for invoking Kerberos secured backend.

You need to do some modifications before running the client and please follow the below steps.

Step 01:

🔹Download and extract the zip file.

🔹Navigate to the org.wso2.identity.esb.kerberos folder and open the build.properties file.

🔹You need to set MI_HOME as carbon.home in order to resolve the dependencies.

carbon.home=/home/asara/WSO2/setups/kerberos/wso2misetup/wso2mi-1.2.0/

Step 02:

🔹Navigate to the org.wso2.identity.esb.kerberos folder and open the build.xml file.

🔹provided the <MI_HOME>/wso2/components/plugin folder as the classpath as below.

<path id="classpath">
<fileset dir="${carbon.home}/wso2/components/plugins" includes="**/*.jar"/>
</path>

Step 03:

🔹Navigate to the org.wso2.identity.esb.kerberos/repo/conf folder and open the policy.xml.

🔹Change the below parameters according to your setup.

<rampart:kerberosConfig>                    <rampart:property name="client.principal.name">asara_carbon.super</rampart:property>
<rampart:property name="client.principal.password">asara</rampart:property>
<rampart:property name="service.principal.name">esb/localhost@WSO2.ORG</rampart:property>
<rampart:property name="java.security.auth.login.config">/home/asara/WSO2/setups/kerberos/03_12/client/org.wso2.identity.esb.kerberos/repo/conf/jaas.conf</rampart:property>
<rampart:property name="javax.security.auth.useSubjectCredsOnly">true</rampart:property>

</rampart:kerberosConfig>

Step 04:

🔹Navigate to the org.wso2.identity.esb.kerberos/src/org/wso2/identity/esb/kerberos folder and open the KerberosClient.java file.

🔹Change the ECHO_SERVICE_EPR according to your proxy services.

final static String ECHO_SERVICE_EPR = "http://asara:8290/services/kerberosSampleProxy";

Step 05: Install Apache Ant

Step 06: Go to the home directory of org.wso2.identity.esb.kerberos using a terminal and run the below command.

ant run

☑️ If the build successful it will return the below response.

Response  : <ser:getQuote xmlns:ser="http://services.samples"><ser:request><xsd:symbol xmlns:xsd="http://services.samples/xsd">Hello WORLD</xsd:symbol></ser:request></ser:getQuote>

This Java client contacts the KDC server to get a Kerberos ticket, build the request and send the request to the proxy.

At the MI server end, the rampart resolves the request and wss4j validates the ticket by decryption and validating the signature of the request payload and call the backend service if that validation passes.

Subsequently gets the response and sign it back with the Kerberos key and send the response back, once again in compliance with the WS-Security.

🙋 If you need to see the incoming messages to the MI server and outgoing messages from the MI server you can enable the wire logs as below. Please change the configs as below in the <MI_HOME>/conf/log4j2.properties file

# Following are to log HTTP headers and messageslogger.synapse-transport-http-headers.name=org.apache.synapse.transport.http.headers
logger.synapse-transport-http-headers.level=DEBUG
logger.synapse-transport-http-wire.name=org.apache.synapse.transport.http.wire
logger.synapse-transport-http-wire.level=DEBUG

That’s all the configurations you got to do👏🤓 and hope this article will be helpful to apply the Kerberos security policy in WSO2 MI with WSO2 IS.

Software Engineer @WSO2