Tuesday, 24 April 2012

OAuth Service Discovery


This picking up from where I left off in any earlier post about OAuth.  To recap, this fronts a short-lived credential service with an OAuth 2.0 interface. This is now being integrated into Contrail described in this paper just submitted following ISGC.   There are few more design considerations to give some thought to though for its wider deployment. As well as use for managing delegation around entities in a cloud federation with Contrail, we are applying this to CEDA's applications and web services. We have a full Python implementation of both client and server side for OAuth 2.0 which I hope to push out to PyPI soon. We're using this to secure our CEDA OGC Web Processing Service and and Web Map Service. Here though, I want to focus on how we might integrate with federated identity management system for the Earth System Grid Federation (ESGF). This is foremost given our involvement with CMIP5.

It's firstly a question of service discovery, something that need not be addressed for Contrail. The Contrail system consists of federation layer which abstracts a set of underlying cloud provider interfaces. Users can manage resources from each of these collectively through the federation's interface. The access control system manages identity at the federation level. A user authenticates with a single federation IdP. When it comes to the OAuth delegation interface, there are single OAuth Authorisation server and Resource server instances associated with the IdP.

Now consider, ESGF. There are many IdPs integrated into the federation, so how does the OAuth infrastructure map to this? Imagine, for example a Web Processing Service hosted at CEDA. A user invokes a process. This process will access secured data from an OPeNDAP service. The WPS will therefore need a delegated credential. When the user requests the process then, the WPS triggers the OAuth flow redirecting their browser to an OAuth Server but how does the WPS know where that service is? A simple approach would be to configure the WPS with an endpoint in its start up settings. This server could be:
  1. a global one for the whole federation – clearly this won't scale given the deployment across many organisations
  2. Associated with the given Service Provider – in this case CEDA
  3. Associated with the user's IdP
2. could work but now consider the other steps in the flow: the user is prompted to authenticate with the OAuth server and approve the delegation i.e. allow the WPS to get a credential on their behalf. In a federated system, the user could be from anywhere, they will need to sign in with their IdP. In this case then, the user enters their OpenID at the OAuth Authorisation server. Already, this is sounding complicated and potentially confusing for the user: they've already been redirected away from the WPS to a central OAuth Authorisation server at CEDA, now they will be redirected again to their IdP probably somewhere else.

Also, consider the approval process. Rather then having to approve the delegation every single time the user invokes the WPS process, they would like the system to remember their approval decision. The OAuth authorisation server could record a set of user approvals associated with a profile stored their. However, if we manage approvals at CEDA, this information will only be scoped within the bounds of CEDA's services. If the user now goes to use services at another service provider, they will need to record a fresh set of delegation approvals. Not a smooth user experience.

Turning to 3., this could scale better. All approval decisions would be centralised with the OAuth Authorisation server associated with their IdP.  However, there is now a service discovery problem. Looking at the protocol flow again, the WPS does not know the location of the user's OAuth Authorisation server endpoint.  Given ESGF's already established use of OpenID, an obvious solution is to leverage Yadis.   During OpenID sign in, the Relying Party HTTP GETs the user's OpenID URL returning an XRDS document.  This contains the service endpoint for the OpenID Provider.  This has already been extended for ESGF to include the MyProxy and Attribute Service endpoints.   I'm told that there's some debate in the community about choice of discovery technology with SWD (Simple Web Discovery) as an alternative.  Given ESGF's legacy with XRD it makes logical sense to add in the address for the OAuth Authorisation server as an entry into the XRDS document returned.  

<?xml version="1.0"; encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
  <XRD>
    <Service priority="0">
      <Type>http://specs.openid.net/auth/2.0/signon</Type>
      <Type>http://openid.net/signon/1.0</Type>
      <URI>https://openid.provider.somewhere.ac.uk</URI>
      <LocalID>https://somewhere.ac.uk/openid/PJKershaw</LocalID>
    </Service>
    <Service priority="1">
      <Type>urn:esg:security:myproxy-service</Type>
      <URI>socket://myproxy-server.somewhere.ac.uk:7512</URI>
      <LocalID>https://somewhere.ac.uk/openid/PJKershaw</LocalID>
    </Service>
    <Service priority="10">
      <Type>urn:esg:security:oauth-authorisation-server</Type>
      <URI>https://oauth-authorisation-server.somewhere.ac.uk</URI>
      <LocalID>https://somewhere.ac.uk/openid/PJKershaw</LocalID>
    </Service>
    <Service priority="20">
      <Type>urn:esg:security:attribute-service</Type>
      <URI>https://attributeservice.somewhere.ac.uk</URI>
      <LocalID>https://somewhere.ac.uk/openid/PJKershaw</LocalID>
    </Service>
  </XRD>
</xrds:XRDS>
Bringing it together, the delegation flow would start with the Service Provider present an interface to enable the user to select their IdP.  This could be by entering a full OpenID URL or preferably picking one from a list of trusted IdPs.  The SP could then GET the URL and extract the OAuth Authorisation Server endpoint from the XRDS document returned.  From there, the standard OAuth flow would proceed as before.

Thursday, 9 February 2012

New Python HTTPS Client

I've added a new HTTPS client to the ndg_* list of packages in PyPI.  ndg_httpsclient as it's become has been on a todo list for a long time.  I've wanted to make use of all the features PyOpenSSL offers with convenience of use wrapping it in the standard httplib and urllib2 interfaces. 

Here's a simple example using the urllib2 interface.  First create an SSL context to set verification of the peer:
 
>>> from OpenSSL import SSL
>>> ctx = SSL.Context(SSL.SSLv3_METHOD)
>>> verify_callback = lambda conn, x509, errnum, errdepth, preverify_ok: preverify_ok 
>>> ctx.set_verify(SSL.VERIFY_PEER, verify_callback)
>>> ctx.load_verify_locations(None, './cacerts')

Create an opener adding in the context object and GET the URL.  The custom build opener adds in a new PyOpenSSL based HTTPSContextHandler.

>>> from ndg.httpsclient.urllib2_build_opener import build_opener
>>> opener = build_opener(ssl_context=ctx)
>>> res = opener.open('https://localhost/')
>>> print res.read()


The above verify callback above is just a placeholder.  For more a comprehensive implementation ndg_httpsclient includes a callback with support for checking of the peer FQDN against the subjectAltName in the certificate.  If subjectAltName is absent, it defaults to an attempted match against the certificate subject CommonName.

The callback is implemented as a class which is a callable.  This means that you can instantiate it, configuring the required settings and then pass the resulting object direct to the context's set_verify:

>>> from ndg.httpsclient.ssl_peer_verification import ServerSSLCertVerification
>>> verify_callback = ServerSSLCertVerification(hostname='localhost')

To get the subjectAltName support I needed pyasn1 with some help from this query to correctly parse the relevant certificate extension.  So adding this into the context creation steps above:
 
>>> from OpenSSL import SSL
>>> ctx = SSL.Context(SSL.SSLv3_METHOD)
>>> verify_callback = ServerSSLCertVerification(hostname='localhost')
>>> ctx.set_verify(SSL.VERIFY_PEER, verify_callback)
>>> ctx.load_verify_locations(None, './cacerts')

The package will work without pyasn1 but then you loose the subjectAltName support.  Warning messages will flag this up.  I can pass this context object to the urllib2 style opener as before, or using the httplib interface:

>>> from ndg.httpsclient.https import HTTPSConnection
>>> conn = HTTPSConnection('localhost', port=4443, ssl_context=ctx)
>>> conn.connect()
>>> conn.request('GET', '/')
>>> resp = conn.getresponse()
>>> resp.read()

A big thank you to Richard for his help getting this package written and ready for use.  Amongst other things he's added a suite of convenience wrapper functions and a command line script.

Wednesday, 23 November 2011

OAuth MyProxy Mash up

This is a revisit of OAuth from two perspectives, the first in that it was under consideration for the MashMyData project and the second, because this is looking at using OAuth with MyProxyCA, something already exploited with the CILogon project.

MashMyData explored solutions for delegation in a secured workflow involving a portal, an OGC Web Processing Service and OPeNDAP.   The aim was where possible, to re-use existing components a large part of which was the federated access control infrastructure developed for the Earth System Grid Federation.   ESGF supports single sign-on with PKI short-lived credentials but we had no use case requiring user delegation.  When it came to MashMyData the traditional solution with Proxy certificates won out.   So why bother revisiting OAuth?
  • OAuth 2.0 simplifies the protocol flow compared with OAuth 1.0.  Where OAuth 1.0 used signed artifacts in the HTTP headers, with OAuth 2.0, message confidentiality and integrity can be taken care of with SSL.  
  • Custom SSL verification code is required for consumers to correctly verify certificate chains involving proxy certificates.  We can dispense with this as OAuth uses an independent mechanism for delegation.
There are a number of different ways you could apply OAuth.  Here we're looking at MyProxyCA, a short-lived credential service (SLCS).  Usually, a user would make a request to the service for a new credential (X.509 certificate) and authenticate with username/password.  Applying OAuth, the user gives permission to another entity to retrieve a credential representing them on their behalf.

There's a progression to this solution as follows:
  1. MyProxyCA alters the standard MyProxy to enable certificates to be dynamically issued from a CA configured with the service.  User's input credentials (username/password) are authenticated against a PAM or SASL plugin.  This could link to a user database or some other mechanism.  MyProxyCA issues EECs.
  2. Front MyProxyCA with a web service interface and you gain all the benefits  - tools, middleware, support and widespread use of HTTP.
  3. If you have a HTTP interface you can easily front it with OAuth
2) Has been developed for CILogon and there is also MyProxyWebService.  This is how 3) could look:

The user agent could be a browser or command line client or some other HTTP rich client application.  The OAuth 2.0 Authorisation Server is implemented in this case as middleware fronting the online CA.   The user must authenticate with this so that the online CA can ensure that this user has the right to delegate to the client.  I would envisage it also keeping a record of previously approved clients for the user so that approvals can be made without user intervention should they wish.  I like this ability for the user to manage which clients may be delegated to offline of the process of brokering credentials.

Once the client has obtained an authorisation grant, it generates a key pair and puts the public key in a certificate request to despatch to the online CA.  The latter verifies the authorisation grant and returns an OAuth access token in the form of a new user certificate delegated to the client.  The client can then use this in requests to other services where they need to act on behalf of the user.  A big thank you to Jim Basney for his feedback in iterating towards this solution.

There is the question remaining of the provenance of credentials: how do I distinguish this delegated certificate to one obtain directly by the user?  With proxy certificates you at least have the chain of trust going back to the original user certificate issuer and the CN component of the certificate subject name added for each delegation step.  For ESGF, we added in a SAML attribute assertion into the certificates extension for issued EECs.  There's a profile for this with VOMS which we hope to adopt for Contrail and the same idea has been used elsewhere.   I'd like to be able to express provenance information in this same space.  I would think it would likewise be as an attribute assertion but it needs some more thought.  The certificate extension content could be extended with additional SAML to meet a couple of other objectives:
  • add an authorisation decision statement to restrict the scope of resources that the issued certificate is allowed to access
  • add level of assurance information via a SAML Authentication Context.
 

Thursday, 20 October 2011

Federated Clouds

Imagine the ability to seamlessly manage independent resources on multiple cloud providers with a single interface.  There are some immediate benefits to consider: avoiding vendor lock-in, migration of a resource from one cloud to another, replication of data ...

You might be excused for thinking it's a little ambitious but a colleague on the Contrail project drew my attention to this article on Cloud Brokering.  As Lorenzo said, you don't have to pay for the full article to get the jist but it seems from a rudimentary search that there are a number of commercial products already ventured into this area:

http://www.datacenterknowledge.com/archives/2009/07/27/cloud-brokers-the-next-big-opportunity/

Federating clouds is a core objective of Contrail, and from what I heard at the Internet of Services meeting I attended last month, there's plenty of research interest in this topic.  Picking out some points raised in the discussions (with some of my own thoughts mixed in):
  • the importance of federating clouds for the European model.  Cloud infrastructures deployed in smaller member states can't match the resources available to the large US enterprises but if those smaller infrastructures are joined in a federation their resources can be pooled to make something greater.
  • Standards are essential for federated clouds to succeed (an obvious point really) but that existing standards such as OVF and OCCI provide incomplete coverage of what is needed across the spectrum of cloud architecture related concerns.
  • The problem of funding and continuity of work, general to many research efforts but cloud technology by its nature surely needs a long term strategy for it to flourish. 
  • The need for longer term research goals with 10-15 year gestation, short-term goals will be absorbed by commercial companies.  There's a danger of simply following rather than leading.
So on the last point then, it's all right to be ambitious ;)


Friday, 7 October 2011

Federated Identity Workshop Coming up...

This is a plug for a workshop on Federated Identity Management for Scientific Collaborations coming at RAL 2-3 November:

http://indico.cern.ch/conferenceDisplay.py?ovw=True&confId=157486

It's a follows the first of it's kind held earlier this year held at CERN and brought together experts in the field and representatives from a range of different scientific communities to present the state of play for federated identity management in each of the various fields and draw together a roadmap for future development.   See the minutes for a full account.

Picking out just a few themes that were of interest to me: inter-federation trust came up a number of times and the need for services to translate credentials from one domain to another.  I read that as a healthy sign that a) various federated identity management systems have bed down and become established and b), that there is not a fight of competing security technologies for one to take over all, rather a facing up to realities of how can we make it work so that these co-exist along side each other.

Credential translation brings in another two interesting issues: provenance and levels of assurance that actually also arose independently in some of the talks and discussions.  If I have a credential that is as a result of a translation of another credential from a different domain how much information is transferred between the two, is it lossy are the identity concepts and various attributes semantically the same?   The same issues arise perhaps to a lesser degree with delegation technologies. 

Levels of assurance is another issue that is surely going to crop up more and more as different authentication mechanisms are mixed together in systems: the same user can enter a federated system with different methods how do we ensure that they are assigned access rights accordingly.   Some complicated issues to tackle but the fact that they can begin to be addressed shows the progress that has been made building on the foundations of established federated systems.


Friday, 19 August 2011

Java SSL Whitelisting of Peer Certificate Subject Names

Helping a colleague just today has reminded me to finish this post drafted a long time ago. Last year I was dipping into the Java SSL libraries to write a short piece of code to make a call to a service running over HTTPS where mutual authentication is required. - The client authenticates the server based on the server's X.509 certificate passed in the SSL handshake but in addition, the client must pass a certificate to enable the server to authenticate it the client.

By default, the Java SSL trust manager will trust peer certificates provided that they are issued by any of the CAs (Certificate Authorities) whose certificates appear in the default trust store for the JVM.   It's possible to customise the trust manager to use a given trust store to give more fine grained control but what if we want to trust only a certain subset of certificates issued by a given CA or CAs?  

One way to achieve this is to whitelist based on the peer certificate DN or Distinguished Name.   This is something that is straightforward to do on the server side with, for example, Apache using the SSLRequire directive. It's also a practice used in Grid computing authorisation middleware with ACLs (Access Control Lists).  Rather than the protection of some server-side resource, the problem to solve in this case is a client invocation.

Returning to the SSL API then, this can be achieved by implementing javax.net.ssl.X509TrustManager interface. The key method for client side checking of server certificates is checkServerTrusted. The relevant hooks can be set in here to check the peer certificate against a whitelist:

    public void checkServerTrusted(X509Certificate[] chain, String authType)
        throws CertificateException {
     
        // Default trust manager may throw a certificate exception
        pkixTrustManager.checkServerTrusted(chain, authType);
        
        // If chain is OK following previous check, then execute whitelisting 
        // of DN
        X500Principal peerCertDN = null;
  
        if (certificateDnWhiteList == null || 
            certificateDnWhiteList.isEmpty())
            return;
  
        int basicConstraints = -1;
        for (X509Certificate cert : chain) {
            // Check for CA certificate first - ignore if this is the case
            basicConstraints = cert.getBasicConstraints();
            if (basicConstraints > -1)
                continue;

            peerCertDN = cert.getSubjectX500Principal();
            for (X500Principal dn : certificateDnWhiteList)
                if (peerCertDN.getName().equals(dn.getName()))
                    return;

            throw new CertificateException("No match for peer certificate \"" + 
  peerCertDN + "\" against Certificate DN whitelist");
        }
    }

pkixTrustManager is the default trust manager whilst certificateDnWhiteList is a list of accepted DNs as X500Principal types.  These can be initialised in the classes' constructor from a properties file or some other input.  The pkixTrustManager.checkServerTrusted call applies the default verification of the peer's certificate based on the CA certificates present in the client's trust store. If this succeeds, a loop then iterates over the certificate chain returned by the peer skipping any CA certificates*.  Once the peer certificate is found, its DN is extracted and checked against the whitelist. If matched, it returns silently to the caller indicating all is OK. If no match is found, a CertificateException is thrown to indicate that the peer certificate is not in the accepted list of DNs.  This could easily be extended to do more sophisticated matching for example using regular expressions.

This technical article provides some more background (scroll down a long way to the Trust Manager heading).  The full source for the example above is available here.

[* The peer can of course pass back not only its own certificate, but any intermediate CA certificates needed to complete the chain of trust to a root CA certificate held by the client.]

Thursday, 10 February 2011

Proxy certificates and delegation with netCDF Beta release

I've written previously about the extensions to the netCDF C API to enable SSL client based authentication.  It's been great to see how something slotted in at the base of a software stack filters down to benefit all the dependents: colleagues have been testing Ferret, ncview and Python bindings built against the updated libraries and used to query ESG-secured OPeNDAP services.  This links with another thread, the MashMyData project extends this SSL client based authentication mechanism from EECs (End Entity Certificates) - the current currency for short lived credentials in ESGF (Earth System Grid Federation) - to RFC3820 proxy certificates.   This is necessitated by the need for delegation in chain of operations in our use case: a chain linking a portal to an OGC Web Processing Service which itself calls an OPeNDAP service.  So, on to trying out the netCDF C client with a proxy certificate:
  • Get ESG-enabled netCDF - now in 4.1.2 beta2
  • build simple client against this version of the library
  • Get  EEC and delegate (need Globus Toolkit for this example)
So expanding the last step:

1) Get short lived EEC from home MyProxy server:

$ myproxy-logon -s <my idp's myproxy host address> -o user.pem -b

2) Delegate to obtain proxy certificate:

$ grid-proxy-init -cert user.pem -key user.pem -out ./credentials.pem -rfc

3) Update netCDF configuration to pick up credentials:

CURL.VERBOSE=1
CURL.COOKIEJAR=.dods_cookies
CURL.SSL.VALIDATE=1
CURL.SSL.CERTIFICATE=<path>/credentials.pem
CURL.SSL.KEY=<path>/credentials.pem
CURL.SSL.CAPATH=<home path>.globus/certificates

Calling the netCDF client makes the underlying Curl library invocation and correctly passes the certificate chain comprising proxy certificate and EEC that issued it (grid-proxy-init step).  The OPeNDAP server and associated security middleware is correctly configured to accept proxy certificates.  I get my data back :).