Secure Wso2 ESB REST APIs using Kerberos

When a consumer attempts to consume a REST API, if the API is secured using some authentication protocol (e.g Basic , Kerberos, NTLM) the consumer has to prove his authentication to the API by sending credentials or relevant token through authorization headers. By default ESB REST APIs does not include any security and from this blog post I will speak about how to make the APIs secured using kerberos.

I have used main 3 components ;  Active Directory as the KDC , ESB server as the resource server and a C#.net client to invoke the rest APIs in ESB server. The design of the implementation is as below.

The Design of the Kerberos Communication






1. Client tries to invoke the ESB proxy
2. The server responds with 401 unauthorized
3. Client requests a kerberos service ticket from active directory
4. If user is identified by the KDC (AD) it will send the SGT to the client
5. Client wraps the SPENEGO token in the request HTTP header and resend to ESB
6. ESB sees the HTTP header with the SPENEGO token. It extracts the kerberos ticket validates the ticket. After validation the server checks authorization.
7. If authorized, ESb serves the requested API otherwise it responds with an access denied.

The source code of the handler can be found from here in github. WSO2-REST-KerberosAuth-Handler-1.0-SNAPSHOT.jar can be found inside the target folder after building this project. We need to copy that .jar to ESB_HOME/repository/components/lib. Please note that this handler has been tested with ESB 4.9.0.


Configurations in ESB server

  •  In the ESB Management Console, go to Manage > Service Bus and click APIs.
  • Click Add Api and switch to source view
  • Add following sample API by copying the source
<api xmlns="http://ws.apache.org/ns/synapse" name="HealthCheckAPI" context="/HealthCheck">
   <resource methods="GET" url-mapping="/status" faultSequence="fault">
      <inSequence>

         <payloadFactory media-type="json">
            <format>{"Status":"OK"}</format>
            <args></args>
         </payloadFactory>
         <log>
            <property name="JSON-Payload" expression="json-eval($.)"></property>
         </log>

         <property name="NO_ENTITY_BODY" scope="axis2" action="remove"></property>
         <property name="messageType" value="application/json" scope="axis2" type="STRING"></property>
         <respond></respond>
      </inSequence>
   </resource>
</api>
  • Copy WSO2-REST-KerberosAuth-Handler-1.0-SNAPSHOT.jar to  ESB_HOME/repository/components/lib
  • Add following handler to the API. Once you define the handler inside the API it becomes a kerberos secured API.
       <handlers>      
       <handler class="org.wso2.handler.KerberosAuthHandler"/>
      </handlers>

  • Place server.properties and jaas.conf files to the ESB_HOME/repsitory/conf after changing the properties according toyour configurations. These files can be found from here.

Properties in jaas.conf file and server.properties file

Principal
We can define SPN for the service by using setspn command. Here in this my sample SPN is HTTP/SERVER.is.local@IS.LOCAL. here SERVER.is.local is the host name and IS.LOCAL is the realm. When you are configuring you should configure it accordingly.

Keytab
This is the path to my keytab file. Key tab file contains SPN and Password. So the server does not directly deal with the AD. Instead it takes credentials from keytab file. You have to generate a key tab file and provide the path here. I have used below command to generate the keytab file

ktpass -out c:\key1.keytab -princ HTTP/SERVER.is.local@IS.LOCAL
-mapuser hasanthi -mapOp set -pass WSO2@123
 
Common errors that can be raised when using this Sample
Defective token detected : This can be happen if it is generated a NTLM token instead of a kerberos token. Reasons for this are:
  •  If we use IP address instead of the host name when invoking the API 
  •  If the SPN is not correctly configured 
  • If the targeted spn is not declared in the  client side
If the error message indicates that it fails to read the encrypted keys (e.g. Cannot find key of appropriate type to decrypt AP REP - RC4 with HMAC)  we should define that encryption type in krb5.conf file

If you get some kind of "checksum" error this simply means that the SPN password you used when generating the keytab or (in the code) is wrong. This means that the encryption key used to encrypt the data in this message didn't match the encryption key used for decryption, and as a result the checksum comparison didn't work

Cannot find KDC for requested realm :Please make sure to specify the KDC in realm section of the krb5.conf file

 

Configure C#.Net Handler to Invoke APIs

The C#.net handler is a very simple console application and you can find the source code as below.
namespace KerberosRestAPIClient
{
    class KerberosClient
    {
        static void Main(string[] args)
        {
            try
            {
                Uri uri = new Uri("http://SERVER.is.local:8280/HealthCheck");
                string spn = "HTTP/SERVER.is.local@IS.LOCAL";
                AuthenticationManager.CustomTargetNameDictionary.Add(Properties.Client.

                Default.URL, spn);
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                req.AuthenticationLevel = System.Net.Security.AuthenticationLevel.

                MutualAuthRequired;
                req.Credentials = CredentialCache.DefaultCredentials;
                using (HttpWebResponse res = (HttpWebResponse)req.GetResponse())
                {
                    StreamReader sr = new StreamReader(res.GetResponseStream());
                    Console.WriteLine(sr.ReadToEnd());
                }
            }
            catch (WebException e)
            {
                Console.WriteLine(e.Message);
            }

        }
    }
}



Once you run the above c#.net client you can invoke the kerberos secured REST API.  By enabling wire logs in ESB the http request response flow can be observed.

HTTP Request/Response results obtained


  • As the above diagram explains when client tries to invoke the API without providing any credentials server returns 401 as below.
  • The client then generates a kerberos ticket and send it to the server as below. Below figure shows the generated kerberos ticket.

  • Then the server accepts the ticket and provide the client to the ability to consume the API

 

Comments

Popular posts from this blog

Applying CORS Filter to wso2 Identity Server

JWKS endpoint of wso2 IS