Showing posts with label Identity Server. Show all posts
Showing posts with label Identity Server. Show all posts

Saturday, July 23, 2016

Securing WSO2 ESB proxies with Kerberos - part 1 (WSO2 Identity Server as a KDC)

WSO2 Enterprise Service Bus proxies can be secured in various security mechanisms. There are around 20 out of the box security schemes that we can use to secure proxies. Kerberos is one of them. In this post and next we go through the setup and configuration of the Kerberos with two options.
  • WSO2 Identity Server (IS) as Key Distribution Center (KDC)
  • Active directory (AD) as the KDC
In this post we shall look into the first option and see how we can invoke a secured proxy using a Java client. This post is a supplementary to some awesome posts that WSO2 folks have written in the past. Notable posts are the [1], [2] and [3]. I wanted to write this post to show the steps in the latest (or recent) versions of the products. Some configurations and locations have been changed and I encountered few issues setting this up. So I wanted to compile them up in one location. Hope this would be helpful for anyone wanting to know how to use Kerberos security with WSO2 Enterprise Service Bus (ESB) for proxies, hence for SOAP calls. In the next post I'll show you how to use a Java and a .Net [Windows Communication Foundation (WCF) ] client to invoke a secured proxy.

I would like to thank Prabath for permitting me to copy and modify some of the code he used to demonstrate in his posts.

WSO2 Identity Server (IS) as the Key Distribution Center (KDC)

In this procedure we need to have WSO2 IS and WSO2 ESB setup in two server machines. (You may try this with the same server with two different port offsets for WSO2 IS and WSO2 ESB). It’s assumed that you have configured the two servers beyond this point. Look at ESB and IS documentation for reference at [4] and [5]. WSO2 IS can be used in a standalone mode (without any external database setup) for you to work on this.

In WSO2 IS (here we used IS version 5.0.0), let’s setup the KDC.

First we have to enable the KDC. Open embedded-ldap.xml and enable KDC as below.
  <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>
We can also enable the KDC setting in the user-mgt.xml. Enable the following property in the UserStoreManager.
<Property name="kdcEnabled">true</Property>

Create a file named jaas.conf with following contents and place inside <IS_HOME>/repository/conf/security directory.

Server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=false
storeKey=true
useTicketCache=false
isInitiator=false;
};

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

Create a file name krb5.conf with following contents and place it in <IS_HOME>/repository/conf/security directory. This says your KDC locates in the current machine.

[libdefaults]
        default_realm = WSO2.ORG
        default_tkt_enctypes = rc4-hmac des-cbc-md5
        default_tgs_enctypes = rc4-hmac des-cbc-md5
        dns_lookup_kdc = true
        dns_lookup_realm = false

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

Now let’s start the WSO2 IS Server.

Once started the server, we have to create a Service Principal (SPN) and a client principal to use with kerberos ticket granting system (TGS). Once the server is started, navigate to Configure -> Kerberos KDC -> Service Principals and click “Add new Service Principal”. Provide a service principal name, description and a password. In this case we used the following service principal name.

SPN Name : esb/localhost@WSO2.ORG

Now create a new user by navigating to Configure -> Users and Roles -> Users -> Add User.

In our case I have added a user name “test” with a password. This will be the client principal.

That’s all with WSO2 IS KDC configuration.

Let’s now configure security in a sample proxy in WSO2 ESB (We used WSO2 ESB 4.8.1). In my case I’ve secured the default echo proxy. You may do the same for any other proxy.

Configuring ESB for kerberos.

Add the following into jass.conf file and place it in <ESB_HOME>/repository/conf/security directory. Note the principal name configuration

Server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=false
storeKey=true
useTicketCache=true
isInitiator=false
principal="esb/localhost@WSO2.ORG";
};
Add the following to the krb5.conf and place it in in <ESB_HOME>/repository/conf/security directory. Configure the ip address of the WSO2 IS in kdc section. If the WSO2 IS is running in the same server as WSO2 ESB, this is the loop back address 127.0.0.1, which is the case in my scenario. Remember to use all caps for the default_realm.

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

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

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

[login]
        krb4_convert = true
        krb4_get_tickets = false



  • Start the ESB server and navigate to the proxy list.
  • Click the echo proxy to navigate into the dashboard
  • Click the security button
  • Select ‘yes’ in the security section
  • Select the kerberos security mechanism (Item number 16) and click next
  • Provide the SPN name you configured in the WSO2 IS together with its password and click Finish


  • 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 specification requires a format for the soap request, such as signing the message payload from kerberos ticket, having a time stamp and so on.

    We have written a java client to cater this (Thanks Prabath, once again for permitting me to use and modify your client). Clients need to incorporate same mechanism to call a kerberos secured proxy in the Java code. Please find the client here. We shall discuss about a .Net client in the next post.

    Open this project in IntelliJIDEA or your favorite IDE.

    Navigate to the config.properties and configure the keyStorePath, keyStorePassword, axis2ClientPath, policyFilePath and servicEndpoint according to your setup. We are using the default keystore in this setup and the configuration is like the following.

    # Kerberos configs
    keyStorePath=/home/shazni/ProjectFiles/kerberosJavaClient/repo/resources/wso2carbon.jks
    keyStorePassword=wso2carbon
    axis2ClientPath=/home/shazni/ProjectFiles/kerberosJavaClient/repo/conf/client.axis2.xml
    policyFilePath=/home/shazni/ProjectFiles/kerberosJavaClient/repo/conf/policy.xml
    serviceEndpoint=https://localhost:9448/services/KerberosTestReg
    
    
    Note my ESB run on port offset of 5 hence the port 9448.

    Next, open up the policy.xml file and make the rampart configuration changes for client.principal.name, client.principal.password (This should be the user you created in the WSO2 IS), service.principal.name, java.security.krb5.conf, java.security.auth.login.config and javax.security.auth.useSubjectCredsOnly. In my case it’s like the following.

    <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">test_carbon.super</rampart:property>
               <rampart:property name="client.principal.password">test123</rampart:property>
               <rampart:property name="service.principal.name">esb/localhost@WSO2.ORG</rampart:property>
               <rampart:property name="java.security.krb5.conf">repo/conf/krb5.conf</rampart:property>
               <rampart:property name="java.security.auth.login.config">repo/conf/jaas.conf</rampart:property>
               <rampart:property name="javax.security.auth.useSubjectCredsOnly">true</rampart:property>
          </rampart:kerberosConfig>
    
        </rampart:RampartConfig>
    

    Also open up the krb5.conf file located in the client code and configure your kdc ip address. In this case the ip address of the WSO2 IS server.

    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!!!!!
    

    This client contacts the WSO2 IS KDC to get a kerberos ticket and then uses the rampart library to build the request as compliant to the WS-Security specification and send the request to the proxy. At the ESB 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 those 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.

    That's all the configurations you got to do. This is a very simple example on how to use keberos security scheme in WSO2 ESB with WSO2 IS.

    In the next post I'll show how to configure the ESB with AD based KDC (specifically with a keyTab file, instead of using the SPN and it's password as used in this post)

    [1] http://wso2.com/library/articles/2012/07/kerberos-authentication-using-wso2-products/
    [2] http://blog.facilelogin.com/2010/12/kerberos-authentication-with-wso2-esb.html
    [3] https://malalanayake.wordpress.com/2012/12/13/how-to-invoke-the-echo-service-secured-by-kerberos-in-wso2-esb/
    [4] https://docs.wso2.com/display/ESB490/WSO2+Enterprise+Service+Bus+Documentation
    [5] https://docs.wso2.com/display/IS510/WSO2+Identity+Server+Documentation

    Tuesday, October 28, 2014

    Creating Roles and Assigning Permissions in WSO2 IS

    In this article I will try to explain the ways to create and assign roles in WSO2 Identity Server. I'll walk you through on creating and assigning roles in the Management console UI and then outline the ways to do the same using an API and using a Java client programmatically.

    Let's start the WSO2 Identity Server (IS) first. To download and run the WSO2 IS, look here.

    Once started login to management console. First let's look at how we can create a role named 'TestRole' and assign some permissions in the permission tree to it. This involves only a very few steps. Navigate to Configure -> Users and Roles -> Roles ->  Add New Role. Enter the role name as 'TestRole' and click next. The permission tree would be shown and you need to select the the relevant permission in the tree for this role. If you need to assign certain existing users to this role, click next and select those users. Otherwise click finish. That's it. You have successfully create a a new role named 'TestRole' and assigned permission for that. You can see the existing roles by navigating to Home -> Configure -> Users and Roles -> Roles. At this place you can view the permission, delete the role, assign user and also rename the role.

    Next we shall see how to create roles and assign any permission in the permission tree to those roles using a programmatic way, without using the UI. You can do that by calling an admin service as an API or using a Java client. I'll outline both the methods here.

    There is a web service API called RemoteUserStoreManagerService that can be used to manage users and roles. This is an Admin Service in the WSO2 carbon platform. Admin services in the WSO2 products are hidden by default. To see the wsdl of this web service you need to unhide the Admin service WSDLs. To do that, first, open up CARBON_HOME/repository/conf/carbon.xml and look for the following line.
    <HideAdminServiceWSDLs>true</HideAdminServiceWSDLs>
    Make it to 'false' and restart the server.

    After the server is successfully started, you can access the wsdl of the RemoteUserStoreManagerService by navigating to https://localhost:9443/services/RemoteUserStoreManagerService?wsdl (Replace 'localhost' part as applicable).

    Following are the two methods I mentioned

    1. You can create a SOAP UI project with this wsdl. You can use the addRole method to add the role. A sample SOAP call is given below to add a role named 'ValidRole' and assign permission under '/permission/admin/login/EmailLogin' where 'EmailLogin' being a new permission I created under '/permission/admin/login/'
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.ws.um.carbon.wso2.org" xmlns:xsd="http://dao.service.ws.um.carbon.wso2.org/xsd">
       <soapenv:Header/>
       <soapenv:Body>
          <ser:addRole>
             <ser:roleName>ValidRole</ser:roleName>
             <ser:permissions>
                <xsd:action>ui.execute</xsd:action>
                <xsd:resourceId>/permission/admin/login/EmailLogin</xsd:resourceId>
             </ser:permissions>
          </ser:addRole>
       </soapenv:Body>
    </soapenv:Envelope>
    
    2. You can write a Java client instead and invoke the methods in the RemoteUserStoreManagerService. A sample Java program is shown below to achieve this. The very self explanatory. Note: you need to add the plugins directory of a IS product to the classpath of the program to build and run this.
    import org.apache.axis2.client.Options;
    import org.apache.axis2.client.ServiceClient;
    import org.apache.axis2.context.ConfigurationContext;
    import org.apache.axis2.context.ConfigurationContextFactory;
    import org.apache.axis2.transport.http.HTTPConstants;
    import org.apache.axis2.transport.http.HttpTransportProperties;
    import org.wso2.carbon.CarbonConstants;
    import org.wso2.carbon.um.ws.api.stub.PermissionDTO;
    import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceStub;
    
    public class ISClient {
    
        private static final String CARBON_HOME = "/home/shazni/Downloads/WSO2/wso2is-5.0.0";
        private static final String SEVER_URL = "https://localhost:9443/services/";
        private static final String USER_NAME = "admin";
        private static final String PASSWORD = "admin";
        private static final String ROLE_NAME = "permissionRole";
    
        public static void main(String args[]){
    
            String trustStore = CARBON_HOME + "/repository/resources/security/wso2carbon.jks";
            System.setProperty("javax.net.ssl.trustStore",  trustStore );
            System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
    
            ConfigurationContext configContext;
    
            try {
                configContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem( null, null);
                String serviceEndPoint = SEVER_URL + "RemoteUserStoreManagerService";
    
                RemoteUserStoreManagerServiceStub adminStub = new RemoteUserStoreManagerServiceStub(configContext, serviceEndPoint);
                ServiceClient client = adminStub._getServiceClient();
                Options option = client.getOptions();
    
                option.setProperty(HTTPConstants.COOKIE_STRING, null);
    
                HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
                auth.setUsername(USER_NAME);
                auth.setPassword(PASSWORD);
                auth.setPreemptiveAuthentication(true);
                option.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);
                option.setManageSession(true);
    
                boolean  authenticate = false;
    
                try{
                    authenticate = adminStub.authenticate(USER_NAME, PASSWORD);
                } catch (Exception e){
                    e.printStackTrace();
                }
    
                if(authenticate){
                    System.out.println("User is authenticated successfully");
                } else {
                    System.err.println("User is authentication failed");
                }
    
                try{
                    PermissionDTO permissionDTO = new PermissionDTO();
                    permissionDTO.setAction(CarbonConstants.UI_PERMISSION_ACTION);
                    permissionDTO.setResourceId("/permission/admin/login/WebLogin");
    
                    PermissionDTO[] permissionDTOs = new PermissionDTO[1];
                    permissionDTOs[0] = permissionDTO ;
    
                    adminStub.addRole(ROLE_NAME, null, permissionDTOs);
                    System.out.println("Role is created successfully");
                } catch (Exception e){
                    System.err.println("Role creation is failed");
                    e.printStackTrace();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    Well that's it. Hope this article has been informative to you.

    Friday, October 17, 2014

    Adding new permissions to permission tree in WSO2 Identity Server programmatically

    Let's start the WSO2 Identity Server (IS) first. To download and run the WSO2 IS, look here.

    The permission tree in WSO2 identity Server is simply,  a few collections stored in the underlying registry. Therefore adding new permissions to the permission tree involves adding a meaningful collection underneath a needed permission (collection). All these resources are stored in the registry path; /_system/governance/permission/admin. You can view this permission tree by navigating to an existing roles permission. To do that, navigate to Configure -> Users and Roles -> Roles -> Internal/everyone (Permissions) in the WSO2 IS. In this article I'm gonna write a sample Registry Client to add collections under /_system/governance/permission/admin to add new permissions to the permission tree. There's a permission named /_system/governance/permission/admin/login. We are going to add a permission named WebLogin under this. What needs to happen is simply creating a collection under this collection and assign a property called 'name' with its value being the name that needs to get displayed in the permission tree. This can be easily done using the management console of the WSO2 IS. What if someone needs to do this programmatically? Read further down to see how to!!

    Adding a resource or collection under another collection in a product like WSO2 IS involves few works. We can obtain some registry based services from a service client named WSRegistryServiceClient. Retrieving this service and performing operations like get and put on top of Registry instance is the easiest way to manipulate the resources in the registry tree. But this service feature is not shipped with some of the WSO2 products including WSO2 IS by default. Therefore it's required to add this feature to WSO2 IS. Following URL explains how to add a feature to any WSO2 product.

    https://docs.wso2.com/display/Carbon420/Installing+Features+via+the+UI

    Look for WS API feature under the category of Governance Registry section. Once installed you can use the Registry API's to add collections under the above collection /_system/governance/permission/admin.

    Following sample client java program adds the said collection.
    import org.apache.axis2.context.ConfigurationContext;
    import org.apache.axis2.context.ConfigurationContextFactory;
    import org.wso2.carbon.registry.core.Collection;
    import org.wso2.carbon.registry.core.Registry;
    import org.wso2.carbon.registry.ws.client.registry.WSRegistryServiceClient;
    
    import java.io.File;
    
    public class RegistryClient {
        private static final String CARBON_HOME = "/home/shazni/Downloads/WSO2/wso2is-5.0.0";
        private static final String axis2Repo = CARBON_HOME + File.separator + "repository" + File.separator + "deployment" + File.separator + "client";
        private static final String axis2Conf = CARBON_HOME + "/repository/conf/axis2/axis2_client.xml";
        private static final String username = "admin";
        private static final String password = "admin";
        private static final String serverURL = "https://localhost:9443/services/";
    
        private static WSRegistryServiceClient initialize() throws Exception {
            System.setProperty("javax.net.ssl.trustStore", CARBON_HOME + File.separator + "repository" +
                    File.separator + "resources" + File.separator + "security" + File.separator +
                    "wso2carbon.jks");
            System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
            System.setProperty("javax.net.ssl.trustStoreType", "JKS");
            System.setProperty("carbon.repo.write.mode", "true");
            ConfigurationContext configContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem(axis2Repo, axis2Conf);
            return new WSRegistryServiceClient(serverURL, username, password, configContext);
        }
    
        public static void main(String[] args) throws Exception {
            Registry registry = initialize();
    
            Collection newCol = registry.newCollection();
            newCol.setProperty("name", "WebLogin");
    
            registry.put("/_system/governance/permission/admin/login/WebLogin/", newCol);
        }
    }
    
    Change the CARBON_HOME variable path to the IS_HOME of your server. Further, to build this you need to add the plugins directory of a Governance Registry product to the class path. Then if you run this code, you get a collection called WebLogin created under /_system/governance/permission/admin

    Hope this article was useful for any WSO2 product user.