Saturday, July 30, 2016

Securing WSO2 ESB proxies with Kerberos - part 2 (Active directory (AD) as the KDC)

In a previous post, we looked at how to secure a WSO2 ESB proxy services using kerberos in which  Key Distribution Center (KDC) is the WSO2 identity server. In this post we'll look at securing WSO2 ESB proxy services when the KDC is the Active Directory (AD).

When the Key Distribution Center (KDC) is the Active Directory (AD), the SPN is usually attached with a service account. Contact your Windows Administrators to create an SPN and a service account and generate a key tab file against that. The main difference from WSO2 IS and AD based KDC is that, in AD the SPN doesn’t provide a text password, rather it’s in the keyTab file.

To create an SPN for a related service account, use the following command.
setspn -s HTTP/USESB.your.domain.com us\us-wso2esb
setspn -s HTTP/USESB us\us-wso2esb
Change the SPN and service account accordingly.

Ask the administrators to create the keytab using the following command.
ktpass /out wso2.keytab /princ HTTP/USESB@YOUR.DOMAIN.COM /mapuser us\us-wso2esb /pass password /crypto All /ptype KRB5_NT_PRINCIPAL

This will create a keyTab file named wso2.keytab

Get the keytab file and place it in a location accessible by the WSO2 ESB.

In our case the KDC hostname is us.your.domain.com. The ESB configuration is same as like the previous case. But make the following changes in the krb5.conf

[libdefaults]

        default_realm = US.YOUR.DOMAIN.COM
        default_tgs_enctypes = rc4-hmac des-cbc-crc des-cbc-md5 aes256-sha1 aes128-aha1
        default_tkt_enctypes = rc4-hmac des-cbc-crc des-cbc-md5 aes256-sha1 aes128-aha1
        permitted_enctypes = rc4-hmac des-cbc-crc des-cbc-md5 aes256-sha1 aes128-aha1
        allow_weak_crypto = true 

[realms]
                US.YOUR.DOMAIN.COM = {
                        kdc = us.your.domain.com
                        default_domain = US.YOUR.DOMAIN.COM
}

[domain_realm]
        .us.YOUR.DOMAIN.COM = US.YOUR.DOMAIN.COM
        us.YOUR.DOMAIN.COM = US.YOUR.DOMAIN.COM

[login]
        krb4_convert = true
        krb4_get_tickets = false
Have the jaas.conf contents be like the below.
Server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
useTicketCache=false
isInitiator=false
keyTab="/carbon/wso2/esb/keytab/wso2.keytab"
principal="HTTP/USESB@US.YOUR.DOMAIN.COM";
};
Note the useKeyTab and the keyTab property, which is different from the WSO2 IS as a KDC scenario. Provide the path of the keyTab file location to the keyTab parameter.

Next when you secure the proxy, you have to provide the SPN name as the AD configured SPN. In our case the SPN is HTTP/USESB@US.YOUR.DOMAIN.COM

Also note since you have no password for the SPN, you can provide the service account password which was created at the AD end. In fact that won’t be used in this case, since the password will get resolved from the keyTab file. Once the proxy is secured, we need to do one more change for Java 7. Go to the proxy dashboard and click Policies. In both, Binding echoSoap11Binding and Binding echoSoap12Binding policies add the following line below the service principal name.
<rampart:property name="kdc.ticket.decoder">org.wso2.carbon.krb.Java7.TicketDecoder</rampart:property>

This is required to be added due to a bug that was identified in the implementation and require a patch from WSO2. If you are a support customer of WSO2, you may open a support ticket to request the patch WSO2-CARBON-PATCH-4.2.0-1811.
If you are not a support customer, you can run the ESB using JRE 1.6.

That’s it in ESB end.

When you start the server, start the ESB with the following system property,

./wso2server.sh -Djava.security.krb5.conf= /carbon/wso2/esb/wso2esb-4.8.1/repository/resources/security/krb5.conf start

You may insert that property in the wso2server.sh file as well.

Now you can try invoking the echo proxy using soap ui and it should fail saying “security header does not found”. This is because the request didn’t contain any security information, such as the kerberos ticket etc. In fact it’s difficult to create a soap ui project to work with kerberos, as the WS-Security spec demands a format for the soap request, such as signing the message payload from kerberos ticket and so on.

We have written a java client to cater this. Clients need to incorporate same mechanism to call a kerberos secured proxy. Please find the Java client here.

Note: however the Java client should be running in a machine where it can connect to the AD based KDC.

Testing with a Java client

Change the krb5.conf of the java client as the following in the java client.
[libdefaults]
        default_realm = US.YOUR.DOMAIN.COM
        default_tgs_enctypes = rc4-hmac
        default_tkt_enctypes = rc4-hmac
        permitted_enctypes = rc4-hmac
        allow_weak_crypto = true

[realms]
      US.YOUR.DOMAIN.COM = {
         kdc = us.your.domain.com
}
Note the KDC ip address. Change accordingly. Also change the config.properties to match the windows server.
keyStorePath=C:\\Users\\smohamednazeer\\Desktop\\DevKeyTab\\wso2carbon.jks
keyStorePassword=wso2carbon
axis2ClientPath=C:\\Users\\smohamednazeer\\Documents\\codes\\kerberospocsolution\\kerberosJavaClient\\repo\\conf\\client.axis2.xml
policyFilePath=C:\\Users\\smohamednazeer\\Documents\\codes\\kerberospocsolution\\kerberosJavaClient\\repo\\conf\\policy.xml
serviceEndpoint=https://<ip-address or domain name>:9443/services/echo
If you are using a custom keystore, use that keystore path and password instead.

The rampart configuration of policy should be changed. Client principal name in this case would be the logged in user. In our case we have the following configurations.
<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">smohamednazeer@US.YOUR.DOMAIN.COM</rampart:property    <rampart:property name="client.principal.password">xxxxxxxxxxxx</rampart:property>
    <rampart:property name="service.principal.name">HTTP/USESB@US.YOUR.DOMAIN.COM</rampart:property>
    <rampart:property name="java.security.krb5.conf">C:\Users\smohamednazeer\Documents\codes\kerberospocsolution\kerberosJavaClient\repo\conf\krb5.conf</rampart:property>
    <rampart:property name="java.security.auth.login.config">C:\Users\smohamednazeer\Documents\codes\kerberospocsolution\kerberosJavaClient\repo\conf\jaas.conf</rampart:property>
    <rampart:property name="javax.security.auth.useSubjectCredsOnly">true</rampart:property>
    <rampart:property name="kdc.ticket.decoder">org.wso2.carbon.krb.Java7.TicketDecoder</rampart:property>
  </rampart:kerberosConfig>

</rampart:RampartConfig>
Enter the username you use to login to Windows machine as the client.principal.name, or you have to enter a username that exists in your AD. Enter the password that you use to login to the Windows machine for the client.principal.password, or enter the password of the respective AD user.

Note the Java7.TicketDecoder property, if you are running the ESB in Java 7 (Note: To use with Java 7, you'll need to have above mentioned patch applied in the ESB 4.8.1 and ESB 4.9.0) If you are running in Java 6, you may remove that line.

If your server is using a custom keystore (which is normally the case), following needs to be done.

1.  Use the same keystore, its alias and password of primary keystore for RegistryKeyStore. This is due to a known issue in the system (see [1]). This will get fixed in the future releases.

[1] https://wso2.org/jira/browse/CARBON-15786

2.  Change the config.property of the Java client to use the new keystore and the password

To build this project you have to point the project to the plugins directory of the WSO2 ESB 4.8.1. You may copy the plugins directory somewhere else and point the project to that and build. If you are running the server in Java 7, make sure the plugins directory also contain the patched jars of the above mentioned issue; wss4j, rampart and TicketDecoder.

Now if you run the client you should see the response of the echo service. You should see an output like the following in the console.
Calling service with parameter - Hello Shazni!!!!!!!

Request = <p:echoString xmlns:p="http://echo.services.core.carbon.wso2.org"><in>Hello Shazni!!!!!!!</in></p:echoString>

The response is : <ns:echoStringResponse xmlns:ns="http://echo.services.core.carbon.wso2.org"><return>Hello Shazni!!!!!!!</return></ns:echoStringResponse>

Testing with a Windows Communication Foundation (WCF) client

The WCF client used to test the echo proxy is located here.

The WCF client creates a form with a button, when clicked it calls the echo proxy. Open App.Config in the Visual Studio.
      <endpoint address="http://<ip address or domain name>:8280/services/echo" behaviorConfiguration=""
        binding="customBinding" bindingConfiguration="customTextMessageEncoding"
        contract="WSO2.Echo.echoPortType">
        <identity>
          <servicePrincipalName value="HTTP/USESB@US.YOUR.DOMAIN.COM" />
        </identity>
      </endpoint> 
Note the endpoint address and the servicePrincipalName.

The client executable is located in EchoWcfClientServiceVisualStudio\EchoClient\bin\Debug

If the executable is not found, you’ll have to build the project in Visual Studio.

Either run the executable or run the client within Visual Studio. This should bring up a form. Click the echo button. The response should be shown in the message box.

You might have noticed that we used the http address. If you want to use the https endpoint for SSL, you have to add the following element in the app.config.
<httpsTransport authenticationScheme="Anonymous" bypassProxyOnLocal="false"
           hostNameComparisonMode="StrongWildcard" proxyAuthenticationScheme="Anonymous"
           realm="" useDefaultWebProxy="true" />
instead of the following http binding,
<httpTransport authenticationScheme="Anonymous" bypassProxyOnLocal="false" 
hostNameComparisonMode="StrongWildcard" proxyAuthenticationScheme="Anonymous" 
realm="" useDefaultWebProxy="true" />
And when you run the WCF client in the Windows machine, you would get an SSL error if you haven’t imported the ESB servers' primary keystore’s certificate to the Windows trusted certificates. Assuming you have generated your keystores and exported the certificate out of it, follow the steps here to import the certificate file to the Windows server.

Once the certificates are imported, you should be able to call the proxy with the https endpoint.

No comments:

Post a Comment