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

Usually Kerberos is used to authenticate users within a network. But we may need to access services outside network boundaries. Also we do not want to expose our LDAP server, JDBC server ports to outside world. But still we need to authenticate users with internal credentials when accessing those services outside the network. In such cases we can use a pattern with STS and Kerberos.

For this pattern to work, the external web service has to trust the STS service. i.e. the STS certificate should be imported to external web service.

Figure 1 : Pattern with Kerberos Security on STS

According to above figure the STS service is secured with Kerberos authentication. The client first authenticate himself to the STS using Kerberos. All the communication messages and paths explained in previous article will take place in this communication. Once client is authenticated using Kerberos protocol against STS, client will get a security token. The token is signed using STS's private key. Then client makes the web request with the obtained token. The end service can verify the token since it is signed using STS's private key and since end web service trusts the STS service.

Introduction

WSO2 Identity Server comes with a “Security Token Service (STS)”. STS is capable of issuing SAML tokens as well as secure conversation tokens. STS is again a simple web service. We can secure STS using any security mechanism we prefer. For this example we will use the ApacheDS based KDC server. I will not discuss details about setting up KDC server as this is already discussed in previous article. In this example I will secure a web service in ESB.

We can use STS token information to do authorization at end service level. The STS will issue a token a with subject attributes (claims). The end service can authorize user based on the provided attributes. We can use XACML to implement authorization at end service. But to keep this article simple we will not discuss details about XACML authorization.

Mainly we will discuss how you can secure STS service using Kerberos and how you can retrieve claims from STS and present them to the end web service. At the end of this article we will also discuss about few troubleshooting tips.

Use Case

In following sections we will discuss how a user can authenticate himself/herself using Kerberos to STS and retrieve a token. Then we will see how client program going to present this token to end web service and get authenticate himself/herself.

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

  1. Set-up KDC server
  2. Configure Kerberos security on STS
  3. Secure end web service using Trust policy
  4. Write a client program to invoke service

Set-up KDC server

  1. Download WSO2 Identity Server from here. Extract distribution to a preferred location.
  2. Configure ApacheDS based KDC server
    1. Open $IS_HOME/repository/conf/embedded-ldap.xml in your favorite editor – Using this configuration file you can control embedded LDAP server parameters.
    2. Locate configuration section.
    3. Under you will find following configuration element:
      <Property name="enabled">false</Property>
      Set above property value to “true”.
    4. In addition, we will disable time stamp “pre-authentication” for this example. To do that, navigate to “<Property name="preAuthenticationTimeStampEnabled">true</Property>” element in the same configuration section and set above property's value to “false”.
    5. Next, open $IS_HOME/repository/conf/user-mgt.xml in an editor.
    6. Set 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”.

Configure Kerberos security on STS

We are applying Kerberos security to the STS. First we need to place the Kerberos related configuration files in $IS_HOME/repository/conf directory. The related configuration files are as follows:

krb5.conf - IS will find KDC server, port, 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 the Kerberos is implemented with the help of GSSAPI. GSSAPI is implemented as a JAAS module. Therefore to provide certain authorisations we also need to place a jaas.conf file in $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/localhost@WSO2.ORG"; 
};

Restart the server for above configurations to get affected.

We also need to associate STS service with a “Service Principal”. To do that go to management console (https://localhost:9443/carbo) and login with default credentials (user - “admin”, password - “admin”). Navigate to “Configure” –> “Service Principals”. You will see a screen as in below image.

Figure 2 : Service Principals

Go to “Add Service Principal” and give following parameters.

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

With above step we have finished creating the “Service Principal”. The next action would be to secure “STS” service using Kerberos. For that go to Main → Security Token Service → Apply Security Policy

Figure 3 : STS Apply Security

Apply security scenario 16 and click “Next”. In the following screen you can bind STS service to the service principal you created above. Give “Service Principal Name” as “sts/localhost” and password “qazqaz”.

Figure 4 : Bind Service Principal

Now we have completed securing “Security Token Service” with Kerberos security. You can make sure STS is secured with Kerberos by accessing STS service URL. (https://localhost:9443/services/wso2carbon-sts?wsdl). See the wsdl, you will find “KerberosToken” used in the policy.

Secure end web service using Trust policy

  1. Download and install WSO2 ESB -

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

  2. Creating and Securing proxy service -

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

    1. First Name
    2. Email

    Also we need to set the correct issuer address in the policy. This information resides within “SupportingToken” of a policy.

    		<sp:SupportingTokens 
    				xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> 
    				<wsp:Policy> 
    					<sp:IssuedToken 
    						sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"> 
    						<Issuer xmlns="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> 
    							<Address xmlns="http://www.w3.org/2005/08/addressing"> 
    								https://localhost:9443/services/wso2carbon-sts 
    							</Address> 
    						</Issuer> 
    						<sp:RequestSecurityTokenTemplate 
    							xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"> 
    							<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType> 
    							<t:KeyType>http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey</t:KeyType> 
    							<t:KeySize>256</t:KeySize> 
    							<t:Claims Dialect="https://wso2.com/claims" 
    								xmlns:ic="http://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 policy to Governance Registry space.

    Login to ESB management console using default user name and password. Go to “Registry” → “Browse” and expand the tree. Go to “/_system/governance” and add a new collection called “policies”.

    Figure 5 : Creating Policy collection

    Add a policy as “Resource”.

    Figure 6 : Adding Policy

    Securing “EchoProxy” service :

    To apply 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 registry. To select the policy path select “Governance Registry”. Expand “policies” collection and select the uploaded policy.

    Figure 7 : Selecting Policy from Registry

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

    Figure 8 : Selecting Trust Store

    With above we have completed setting up security to “EchoProxy” service. So far we completed configuring KDC server and we also completed configuring STS and now we are done with setting up security to “EchoProxy” service. What is left is to write the client program which can talk to the service.

    Configuring “EchoProxy” as a trusted service

    We need to configure STS to issue tokens to “EchoProxy” service. For this login to 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 key alias that should be used to sign the assertion.

    For our example we will use the default wso2carbon alias. In addition you also need to export public key certificate of above key to ESB as a trusted certificate. Since we are using default wso2carbon we don't need to export and import certificate.

    All the users who are added to WSO2 IS will be treated as “Client Principals”. For this example I am going to create a user called “amilaj” with password “wsxwsx”. To add a user, login to WSO2 IS and navigate to Configure → Users.

    Figure 9 : Add Users

    Also in our request we request for claims. Therefore we need to setup claim values for users (“Client Principals”). To set values for claim values go to “Configure” → Users → Select the user (“amilaj” in this case) → Select “User Profile” → Select “default” profile and set appropriate values.

    Figure 10: Change Profile Data

Write a client program to invoke service

Client program first need to talk to STS to obtain a token. Then only the client will be able to communicate with the ESB “EchoProxy”. To obtain a STS token, client program first needs to talk to Kerberos KDC server and need to full-fill Kerberos communication. Therefore when talking to STS, client should use a Kerberos token policy, further when communicating with “EchoProxy” client needs to communicate based on a trust policy.

So the program flow can be mainly divided into two:

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

So in the client program we will be using 2 policies. 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 STS and Proxy service URLs appropriately you can run the service using “ant” command. Run “ant run”. This will invoke the service.

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

<Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsi="http://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 some of the older version of WSO2 IS (3.2.0, 3.2.2, 3.2.3) you may get an exception quite similar to following:

    [2011-12-15 10:42:02,171]  WARN {org.apache.axis2.addressing.AddressingFaultsHelper} -  triggerActionNotSupportedFault: messageContext: [MessageContext: logID=172769bb006e0db76be2fc5210ae610cfba312bb9498931f] problemAction: http://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 rampart from product's registry. i.e. Main → Registry → Browse. Go to following registry path:/_system/config/repository/axis2/modules. From there, remove “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 
    

    Above error occurs when client program is unable to access KDC server. This could possibly be due to an error in 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)
    

    Probably the URL entered in the STS differs from the URL requested in 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.
    	…..
    

    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 sts component (Early version of WSO2 IS). As a work around put "First Name" to policy as follows:

    	                                       <sp:RequestSecurityTokenTemplate 
    							xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"> 
    							<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType> 
    							<t:KeyType>http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey</t:KeyType> 
    							<t:KeySize>256</t:KeySize> 
    							<t:Claims Dialect="https://wso2.com/claims" 
    								xmlns:ic="http://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