Securing STS (Security Token Service) with Kerberos

  • By Amila Jayasekara
  • |
  • 7 Aug, 2012

Applicable Versions

  1. WSO2 Identity Server - 3.2.0
  2. WSO2 Identity Server - 3.2.2
  3. WSO2 Identity Server - 3.2.3

Pattern

Kerberos is often used to authenticate users within a network. But sometimes, organizations may need to access services outside network boundaries. They will need to authenticate users with internal credentials when accessing those services from outside the network without exposing their LDAP server and JDBC server ports to the outside world. In such cases we can use a pattern with a Security Token Service (STS) and Kerberos.

For this pattern to work, the external web service has to build up a trust relationship with the Secure Token Service. e.g. The STS certificate should be imported to an external web service.

Figure 1 : Pattern with Kerberos Security on STS

According to Figure 1, the STS service is secured with Kerberos authentication. First, the client authenticates to the STS using Kerberos. Once the client is authenticated using the Kerberos protocol against STS, he/she will get a security token. The token is then signed using the STS private key. Then, the client makes the request with the obtained token. Finally, the end service can verify the token since it is signed using the STS private key and the web service trusts the STS.

Introduction

WSO2 Identity Server comes with a Security Token Service. An STS is capable of issuing SAML tokens and secure conversation tokens. As STS is a simple web service, it can be secured using any security mechanism of our choice.

For this example we will use the ApacheDS based KDC server. We will not discuss details about setting up a Key Distribution Center (KDC) server as this is already discussed. In this example, we will secure a web service in an Enterprise Service Bus (ESB).

We can use STS token information to perform authorizations at end service level. The STS will issue a token with subject attributes (claims). The end service can authorize users based on the provided subject attributes. We can use eXtensible Access Control Markup Language (XACML) to implement authorization at the end service. 

To keep this article simple, we will not discuss details about XACML authorization. Instead, we will discuss how users can secure the STS service using Kerberos and how to request and retrieve claims from STS responses and present them to the end web service. We will also discuss a few troubleshooting tips at the end of this article.

Examples of STS Tokens 

AWS Security Token Service (AWS STS) as a web service enables users to request temporary security credentials with limited privileges for a user with an AWS account or for users federated into AWS. These include an access key pair (access key ID and secret access key) and a session token. Security access tokens are mainly used for identity federation among trusted third party identity providers and providing cross account access.

Use Case on Security Tokens

In the following sections we will discuss how a user can authenticate using Kerberos to STS and retrieve a token. Then we will see how a client program presents this token to the end web service for authentication.

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

  1. Setup the KDC server
  2. Configure Kerberos security on STS
  3. Secure the end web service using a trust policy
  4. Write a client program to invoke service endpoint

Setup the KDC Server

Please follow the following steps outlined below:

  1. Download WSO2 Identity Server and extract distribution to a preferred location.
  2. Configure the ApacheDS based KDC server
    a. Open $IS_HOME/repository/conf/embedded-ldap.xmlin your favorite editor – Using this configuration file you can control embedded LDAP server parameters.
  3. b. Locate the configuration section.

  4. Under this you will find the following configuration element:
    a. <Property name="enabled">false</Property>
    b. Set above property value to “true”.
  5. c. In addition, we will disable time stamp “pre-authentication” for this example. To do that, navigate to “true” element in the same configuration section and set above property's value to “false”.

    d. Next, open $IS_HOME/repository/conf/user-mgt.xml in an editor.

  6. Set the following property to true, under “ApacheDSUserStoreManager” configuration,
    <Property name="kdcEnabled">true</Property>.
  7. Start Identity Server – Go to $IS_HOME/bin and run “./wso2server.sh”.

2. Configure Kerberos Security on STS

As we seek to apply Kerberos security to the STS, first we need to place the Kerberos related configuration files in the $IS_HOME/repository/conf directory. The related configuration files are as follows:

krb5.conf - IS will find the KDC server, port, and realm information from this file. A sample configuration file is listed below.

[libdefaults] 
        default_realm = WSO2.ORG 
	default_tgs_enCtypes = des-cbc-md5 des-cbc-crc des3-cbc-sha1 
	default_tkt_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 

[login] 
        krb4_convert = true 
        krb4_get_tickets = false

Jaas.conf - In Java, Kerberos is implemented with the help of Generic Security Service Application Program Interface (GSSAPI). In turn, the GSSAPI is implemented as a Java Authentication and Authorization Service (JAAS) module. Therefore, to provide certain authorizations we need to place a jaas.conf file in the $ESB_HOME/repository/conf directory.

A sample jaas.conf is listed below.

Server { 
com.sun.security.auth.module.Krb5LoginModule required 
useKeyTab=false 
storeKey=true 
useTicketCache=false 
isInitiator=false 
principal="sts/[email protected]"; 
};

After this is done, please restart the server for the above configurations to be implemented.

We also need to associate the STS service with a “Service Principal”. To do this, go to the management console (https://localhost:9443/carbo) and login with default credentials (user - “admin”, password - “admin”). Navigate to “Configure” –> “Service Principals”. If done correctly, the following screen (Figure 2) will appear.

Figure 2 : Service Principals

From here, Go to “Add New Service Principal” and add the following parameters.

Service Name - “ sts/localhost”
Password - “qazqaz”

With the above step we have finished creating the service principal. The next action would be to secure the STS service using Kerberos. 

To do this go to Main → Security Token Service → Apply Security Policy, an illustration of the screen is given in Figure 3 (see below).

Figure 3 : STS Apply Security

Apply security scenario and click “Next”. In the subsequent screen (Figure 4) users can bind the STS service to the service principal created above. Add the “Service Principal Name” as “sts/localhost” and insert the password as “qazqaz”.

Figure 4 : Bind Service Principal

Now we have secured the STS with Kerberos security. This can be confirmed by accessing the STS service URL (https://localhost:9443/services/wso2carbon-sts?wsdl). View the wsdl and you should find “KerberosToken” used in the policy.

3. Secure the End Web Service Using a Trust Policy

Please follow the steps outlined below:

  1. Download and install WSO2 Enterprise Service Bus -

    Download WSO2 Enterprise Service Bus here and unzip distribution to your preferred location. Change the port offset if you have WSO2 Identity Server and WSO2 Enterprise Service Bus instances in the same machine. To change the port offset, open $ESB_HOME/repository/conf/carbon.xml in an editor and locate the parameter and change the value of “Offset” to a numeric value other than 0. (E.g : - 5 or 10). Start the WSO2 Enterprise Service Bus server with $ESB_HOME/bin/wso2server.sh.

  2. Creating and securing the proxy service -

    Log in to WSO2 Enterprise Service Bus management console using default credentials (user - “admin”, password - “admin”). For this example we will use the “Echo” service as our end web service. Create a pass through a proxy service for Echo and name it “EchoProxy”. We need to secure the EchoProxy using WS-Trust based security policies as we do not have a pre-defined security scenario for trust based policies. Thus, we need to come up with a WS-Trust based security policy and we need to upload it to the governance space of WSO2 Enterprise Service Bus registry. You can find a trust based security policy from here. In this, the policy server also requests for the following claims,

    1. First Name
    2. Email

    Finally, we need to set the correct issuer address in the policy. This information resides within the “SupportingToken” [as shown below] of a policy.

    		<sp:SupportingTokens 
    				xmlns:sp="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> 
    				<wsp:Policy> 
    					<sp:IssuedToken 
    						sp:IncludeToken="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"> 
    						<Issuer xmlns="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> 
    							<Address xmlns="https://www.w3.org/2005/08/addressing"> 
    								https://localhost:9443/services/wso2carbon-sts 
    							</Address> 
    						</Issuer> 
    						<sp:RequestSecurityTokenTemplate 
    							xmlns:t="https://schemas.xmlsoap.org/ws/2005/02/trust"> 
    							<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType> 
    							<t:KeyType>https://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey</t:KeyType> 
    							<t:KeySize>256</t:KeySize> 
    							<t:Claims Dialect="https://wso2.org/claims" 
    								xmlns:ic="https://schemas.xmlsoap.org/ws/2005/05/identity"> 
    								<ic:ClaimType Uri="First Name" /> 
    								<ic:ClaimType Uri="Email" /> 
    							</t:Claims> 
    						</sp:RequestSecurityTokenTemplate> 
    						<wsp:Policy> 
    							<sp:RequireInternalReference /> 
    						</wsp:Policy> 
    					</sp:IssuedToken> 
    				</wsp:Policy> 
    			</sp:SupportingTokens>
    

    We also need to upload the policy to the Governance Registry space. Here’s how.

    Log in to the WSO2 Enterprise Service Bus management console using default username and password. Go to “Registry” → “Browse” and expand the tree. Go to “/_system/governance” and add a new collection called “policies”. (See Figure 5).

    Figure 5 : Creating Policy collection

    Add a policy in the “Add Resource” entry, as shown in Figure 6.

    Figure 6 : Adding Policy

    Secure the “EchoProxy” service :

    To apply the uploaded security policy to the service, go to “Web Services” → List and select “EchoProxy”. In the service dashboard select “Security”. Enable security policy and select the policy from the registry. To select the policy path, select “Governance Registry”. Expand the “policies” collection and select the uploaded policy. This is shown in Figure 7.

    Figure 7 : Selecting Policy from Registry

    In the next screen (see Figure 8) select “wso2carbon.jks” under “Trusted Key Stores”. You can select any other custom trust store as well (if you have one defined).

    Figure 8 : Selecting Trust Store

    With the process mentioned above, we have completed setting up security to the “EchoProxy” service. So far we have configured the KDC server, configured STS, and now we have set up security to the “EchoProxy” service. 

    All that’s left is to write the client program which can talk to the service.

    Configuring “EchoProxy” as a trusted service

    1. We need to configure STS to issue tokens to “EchoProxy” service. For this, login to WSO2 Identity Server with default credentials. Go to “Manage” → “Security Token Service”. Add “EchoProxy” as a trusted service. For this you need to specify the service URL and a secret key alias that should be used to sign the assertion.

    2. For our example we will use the default wso2carbon alias. In addition, users need to export the public key certificate of the above key to WSO2 Enterprise Service Bus as a trusted certificate. Since we are using default wso2carbon we don't need to export and import certificates.

    3. All users added to WSO2 Identity Server will be treated as “Client Principals”. For this example, we will create a user called “amilaj” with the password “wsxwsx”. To add a user, login to WSO2 Identity Server and navigate to Configure → Users. This is shown in Figure 9.

    Figure 9 : Add Users

    4. Additionally, in our request we request for claims. Therefore, we need to set up claim values for users (“Client Principals”). To do this go to “Configure” → Users → Select the user (“amilaj” in this case) → Select “User Profile” → Select “default” profile and set appropriate values. This is shown in Figure 10.

    Figure 10: Change Profile Data

4. Write a Client Program to Invoke Service

The client program first needs to talk to the STS to obtain a token. Only then will the client be able to communicate with the ESB “EchoProxy”. To obtain a STS token, the client’s program first needs to talk to Kerberos KDC server and needs to fulfill Kerberos communication. Therefore, when talking to STS, clients should use a Kerberos token policy. Furthermore, when communicating with “EchoProxy” clients need to communicate based on a trust policy.

The program flow can be mainly divided into two sections::

  1. Client obtaining a token from STS
  2. Client presenting a token to the ESB “EchoProxy” and making the actual request.

In the client program we will be using two policies. These are, a Kerberos policy to communicate with STS and a service policy to communicate with “EchoProxy” service.

In the client program we will be using two policies. These are, a Kerberos policy to communicate with STS and a service policy to communicate with “EchoProxy” service.

A sample client program is attached here. After changing the STS and Proxy service URLs appropriately users can run the service using the “ant” command. Run “ant run” to invoke the service.

After retrieving the token from STS we can print it. While running the program you would see the following string in the console.

<Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" AssertionID="_f245074e432aa5b0776051efe75d7071" IssueInstant="2012-06-27T17:05:22.625Z" Issuer="localhost" MajorVersion="1" MinorVersion="1"><Conditions NotBefore="2012-06-27T17:05:22.615Z" NotOnOrAfter="2012-06-27T17:10:22.615Z" /><AttributeStatement><Subject><NameIdentifier Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">amilaj</NameIdentifier><SubjectConfirmation><ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:holder-of-key.......</ds:KeyInfo></ds:Signature></Assertion>

Troubleshooting Tips

  1. In older versions of WSO2 Identity Server (3.2.0, 3.2.2, 3.2.3) a string could be seen similar to following:

    [2011-12-15 10:42:02,171]  WARN {org.apache.axis2.addressing.AddressingFaultsHelper} -  triggerActionNotSupportedFault: messageContext: [MessageContext: logID=172769bb006e0db76be2fc5210ae610cfba312bb9498931f] problemAction: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT 
    [2011-12-15 10:42:02,183] ERROR {org.apache.axis2.engine.AxisEngine} -  The [action] cannot be processed at the receiver. 
    org.apache.axis2.AxisFault: The [action] cannot be processed at the receiver. 
    	at org.apache.axis2.addressing.AddressingFaultsHelper.triggerAddressingFault(AddressingFaultsHelper.java:373) 
    	at org.apache.axis2.addressing.AddressingFaultsHelper.triggerActionNotSupportedFault(AddressingFaultsHelper.java:336) 
    	…
    

    To fix this issue, remove the rampart from the product's registry. The pathway is as follows; 

    Main → Registry → Browse. Go to the following registry path :/_system/config/repository/axis2/modules. From there, remove the “rahas” module and restart WSO2 Identity Server.

  2. log4j:WARN No appenders could be found for logger (org.apache.axiom.om.util.StAXUtils). 
    log4j:WARN Please initialize the log4j system properly. 
    Exception in thread "main" org.apache.rahas.TrustException: Error in obtaining token from : "https://localhost:9443/services/wso2carbon-sts" 
    	at org.apache.rahas.client.STSClient.requestSecurityToken(STSClient.java:159) 
    	…..
    Caused by: org.apache.axis2.AxisFault: Error in building kereberos token 
    	at org.apache.rampart.handler.RampartSender.invoke(RampartSender.java:76) 
    	... org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:229) 
    	
    	... 6 more 
    Caused by: org.apache.rampart.RampartException: Error in building kereberos token 
    	at org.apache.rampart.builder.BindingBuilder.getKerberosTokenBuilder(BindingBuilder.java:912) 
    	...
    	... 15 more 
    Caused by: org.apache.ws.security.WSSecurityException: The security token could not be authenticated or authorized (Kerberos login failed) 
    	at org.apache.ws.security.message.WSSecKerberosToken.prepare(WSSecKerberosToken.java:200) 
    	at org.apache.ws.security.message.WSSecKerberosToken.build(WSSecKerberosToken.java:91) 
    	at org.apache.rampart.builder.BindingBuilder.getKerberosTokenBuilder(BindingBuilder.java:910) 
    	... 19 more 
    

    The above error occurs when the client program is unable to access the KDC server. This could possibly be due to an error in the krb5.conf file.

  3. [2011-12-15 11:09:40,816]  INFO {org.apache.xml.security.signature.Reference} -  Verification successful for URI "#Timestamp-1" 
    [2011-12-15 11:10:37,325] ERROR {org.apache.axis2.transport.http.AxisServlet} -  
    java.lang.NullPointerException 
    	at org.apache.rahas.impl.SAMLTokenIssuer.createHoKAssertion(SAMLTokenIssuer.java:412) 
    	... org.apache.axis2.receivers.AbstractInOutMessageReceiver.invokeBusinessLogic(AbstractInOutMessageReceiver.java:40) 
    	at org.apache.axis2.receivers.AbstractMessageReceiver.receive(AbstractMessageReceiver.java:110) 
    	at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:181) 
    	... org.eclipse.equinox.http.servlet.internal.ServletRegistration.handleRequest(ServletRegistration.java:90) 
    	at org.eclipse.equinox.http.servlet.internal.ProxyServlet.processAlias(ProxyServlet.java:111) 
    	…. org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304) 
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
    	... org.wso2.carbon.server.CarbonStuckThreadDetectionValve.invoke(CarbonStuckThreadDetectionValve.java:154) 
    	at org.wso2.carbon.server.TomcatServer$1.invoke(TomcatServer.java:254) 
    	...
    	at java.lang.Thread.run(Thread.java:662)
    

    The error below could occur when the URL entered in the STS differ from the URL requested in the client program.

  4. [2011-12-15 11:44:51,676] ERROR {org.apache.axis2.engine.AxisEngine} -  Version of the SAML token does not match with the required version 
    org.apache.axis2.AxisFault: Version of the SAML token does not match with the required version 
    	at org.apache.rampart.handler.RampartReceiver.setFaultCodeAndThrowAxisFault(RampartReceiver.java:180) 
    	…..
    	at java.lang.Thread.run(Thread.java:662) 
    Caused by: org.apache.rampart.RampartException: Version of the SAML token does not match with the required version.
    	…..
    

    As we can see, the token type entered in the policy and the token type entered in the issued token are different. Make sure there are no leading spaces in the token type specified in the policy. 

  5. 2011-12-15 12:04:01,176]  INFO {org.apache.xml.security.signature.Reference} -  Verification successful for URI "#_ba3de257c9e7e426b2b5284f19d1087a" 
    [2011-12-15 12:04:01,182]  INFO {org.apache.xml.security.signature.Reference} -  Verification successful for URI "#Id-41563810" 
    [2011-12-15 12:04:01,183]  INFO {org.apache.xml.security.signature.Reference} -  Verification successful for URI "#Timestamp-2" 
    [2011-12-15 12:04:26,641] ERROR {org.apache.axis2.engine.AxisEngine} -  SAML token does not contain certain mandatory claims 
    org.apache.axis2.AxisFault: SAML token does not contain certain mandatory claims 
    	at org.apache.rampart.handler.RampartReceiver.setFaultCodeAndThrowAxisFault(RampartReceiver.java:180) 
    	….
    Caused by: org.apache.rampart.RampartException: SAML token does not contain certain mandatory claims.
    	...
    

    This is a bug in the STS component (Early version of WSO2 Identity Server). As a workaround, put "First Name" to policy as follows. 

    	                                       <sp:RequestSecurityTokenTemplate 
    							xmlns:t="https://schemas.xmlsoap.org/ws/2005/02/trust"> 
    							<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType> 
    							<t:KeyType>https://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey</t:KeyType> 
    							<t:KeySize>256</t:KeySize> 
    							<t:Claims Dialect="https://wso2.org/claims" 
    								xmlns:ic="https://schemas.xmlsoap.org/ws/2005/05/identity"> 
    								<ic:ClaimType Uri="First Name" /> 
    							</t:Claims> 
    						</sp:RequestSecurityTokenTemplate>
    
 

About Author

  • Amila Jayasekara
  • Research Assistant
  • Indiana University Bloomington