Sunday, March 21, 2010

Oracle Service Bus and Kerberos

This topic has come up in a number of discussions at a number of customers in the past few weeks. The basic question is "How does OSB work with Kerberos (WCF) clients?". The short answer is "The simplest thing to do is just use OSB as a pass-through." I've given this answer a few times, and people seem disappointed. I think they expect that there is "more". I wanted to use this post to help people understand some of the details of OSB and Kerberos that make this the most sensible approach.



A topic that needs to be understood is the role of intermediaries in web-services security. The SOAP specification defines two types of intermediaries - Active and forwarding. Basically, and active intermediary does something with the message, and forwarding or pass-through doesn't. So, in these scenarios, the first thing to understand is which type of intermediary is OSB in your environment? The answer is likely "it depends" - meaning whatever type fits my ultimate requirements....fair enough.

The scenario that I've seen most commonly, and I am discussing in this post, is a WCF client calling through OSB to a WCF service. The number 1 question that drives the solution is "Do you need to propagate the original user's identity to the WCF or can it use a service account?". In this case, I mean propagating the identity so that the Windows OS sees the user. If the answer is yes, then go with OSB in pass-through mode. You have to change the WCF Service to ServiceBehavior(AddressFilterMode = AddressFilterMode.Any). This was because the wsa:To is set to the OSB Endpoint and WCF by default will check to make sure that the wsa:To header matches the host (in my case IIS). You may think you could just change the wsa:To field to the end service, but remember, in this case OSB is an intermediary (and WCF signs that header, so tampering with it will cause the entire message to fail).

If you don't need the identity pushed all the way to the OS, and want OSB as an active intermediary (say to perform authorization or audit the user), then consider using an STS, and have WCF pass OSB a SAML assertion. I covered this scenario a while back in this blog..

The other approach for using OSB as an active intermediary that is often suggested is to use SPNEGO. This means instead os using WS-Security Kerberos Token Profile to pass the Service Ticket use SPNEGO and pass it in the HTTP header. In theory, this is a reasonable approach, but in practice it can be a little tricky. The simple reason is that SPENGO isn't supported in OSB 10gR3. The way that OSB interacts with IdentityAsserters is pretty simple. In the pipeline, you configure a single HTTP header that OSB should look for the token and then pass to the IdentityAsserter. This works well you thinks like SSO cookies and sessions contained in HTTP headers. For SPNEGO, the protocol is a little more complicated.. When the user first comes to the service, they need to be challenged with WWW-Authenticate: Negotiate. The user can then respond with the Kerberos ticket using the HTTP Header Authorization: Negotiate xxxxx. You can map the IdentityAsserter to the HTTP header Authorization, the problem is that you need to tell the requesting agent that the service supports SPNEGO (send 401 with HTTP Header WWW-Authenticate: Negotiate). What I've seen as a "work-around" is using a custom IdentityAsserter and mapping the IdentityAsserter to a common HTTP Header like User-Agent. These means that the identity asserter will get called on every request, and from inside of the identity asserter, you can access the HttpServletRequest and HttpServletResponse and manage the SPNEGO steps. This may sound slightly "scary" but here's some sample code to get you started:


private AuthResult authentictaeSpnego(String authorization, HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException
{

int len = NEGOTIATE_HEADER.length();
String token = authorization.substring(len);
byte[] spnego = new BASE64Decoder().decodeBuffer(token);

String userName = null;
try
{
NegTokenInit negoToken = new NegTokenInit(spnego);
debug.debug("SPNEGO Token: "+negoToken);
byte[] krbToken = negoToken.getMechanismToken();

getServerCredential();
GSSContext context = gssManager.createContext(serverCreds);
context.acceptSecContext(krbToken, 0, krbToken.length);
GSSName srcName = context.getSrcName();
//drop the realm from the name and go with it...
String krbName = srcName.toString();
userName = krbName.substring(0, krbName.indexOf('@'));
} catch (GSSException e)
{
logger.error("Error validating SPNEGO Token: "+e.toString());
}



}

The NegTokenInit comes from jcifs.

The other approach from inside of OSB would be to have a pipeline do it - check to see if the Authorization: Negotiate was there and if it is forward it (including the header) to another proxy service that was configured with the OOTB SPNEGO IdentityAsserter. If its not there, then return a SOAP Fault including the HTTP Header WWW-Authenticate: Negotiate. You have to configure it on a per proxy basis, but doesn't require custom code like the 1st approach.

I think its important to understand how OSB and intermediaries are expected to work in these scenarios in order to build a proper solution. Kerberos has some intricacies that make is best suited for point-to-point scenarios - a user requesting access to a service. If you look at the rich possibilities inside of an SOA it might make more sense to look at swapping the token up-front for a SAML assertion, and then using it, along with PKI to achieve what you want. I'm pretty sure that if this scenario was either with Kerberos, MSFT would have shown it :)

I understand that deploying a bus as an active intermediary with end-to-end identity propagation is technically possible, but for these types of scenarios to work with Kerberos you either end up passing the end-user's TGT (basically like sending around your password) or granting the bus process the ability to invoke the S4U Kerberos extension, and this requires privileges in the OS that many administrators are unwilling to allow (you're like the domain controller).

I've tried to include some links to some of the gory details for people who are interested, and want to work through the details of doing something other than OSB as a pass-through. I'd be interested to hear your opinions, but after reviewing all of these possibilities, I feel that pass-through is the simplest and it works. You may have a compelling reason to do something different, but I think this approach, given the nature of Kerberos and intermediaries is the best.

3 comments:

  1. You wrote: "return a SOAP Fault including the HTTP Header WWW-Authenticate: Negotiate". I thought as you... but it does not work, possibly, because WWW-Authenticate header must be used only with 401 status.

    I have no idea how to send 401 status from message flow. Do you?

    ReplyDelete
  2. Any plans to write on the best way to do SAML assertions when you are doing SOAP over JMS between WLS and WLS or ServiceMix and WLS?

    ReplyDelete
  3. Hi ,
    I recently developed some custom Security Providers to cover the scenario that you described above with some extensions to it. I succeeded to cover all requested scenarios but I faced with some problems when its come to displaying custom mbean attributes in the WLS console. I had to define about 10-15 mbean attributes for each provider and unfortunately I couldn't find a way to specify attribute display order( and also put attribute descriptions ) in WLS console. Now my custom sec. providers user interfaces are mess in WLS console. Unfortunately the given samples in Oracle's site doesn't have a clue about how to do that.

    Can you guys please provide some tips & tricks to control display order of custom Mbean attributes & also defining attribute definitions for WLS console?

    Many Thanks.

    ReplyDelete

Note: Only a member of this blog may post a comment.