Securing STS (Security Token Service) with Kerberos
-
By Amila Jayasekara
- 7 Aug, 2012
Applicable Versions
- WSO2 Identity Server - 3.2.0
- WSO2 Identity Server - 3.2.2
- 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:
- Set-up KDC server
- Configure Kerberos security on STS
- Secure end web service using Trust policy
- Write a client program to invoke service
Set-up KDC server
- Download WSO2 Identity Server from here. Extract distribution to a preferred location.
-
Configure ApacheDS based KDC server
- Open $IS_HOME/repository/conf/embedded-ldap.xml in your favorite editor – Using this configuration file you can control embedded LDAP server parameters.
- Locate
configuration section. - Under
you will find following configuration element:
<Property name="enabled">false</Property>
Set above property value to “true”. - 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”.
- Next, open $IS_HOME/repository/conf/user-mgt.xml in an editor.
- Set following property to true, under “ApacheDSUserStoreManager” configuration,
<Property name="kdcEnabled">true</Property>. - 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
-
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.
-
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,
- First Name
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:
- Client obtaining a token from STS
- 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
-
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.
-
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.
-
[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.
-
[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.
-
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>