Tuesday, November 10, 2015

Shibboleth-We Are Not Done Yet-Part 3

This will be quick: by applying the third solution described in the previous post the SP worked with no problem. The solution was based on ISA Server, Kentor.AuthServices and ASP.NET MVC. The ability to run sites on default HTTPS port made things simple. Linux rulez!

BUt...we are not done yet! Logout waits to be implemented.

VGhpcyB3aWxsIGJlIHF1aWNrOiBieSBhcHBseWluZyB0aGUgdGhpcmQgc29sdXRpb24gZGVzY3JpYmVkIGluIHRoZSBwcmV2aW91cyBwb3N0IHRoZSBTUCB3b3JrZWQgd2l0aCBubyBwcm9ibGVtLiBUaGUgc29sdXRpb24gd2FzIGJhc2VkIG9uIElTQSBTZXJ2ZXIsIEtlbnRvci5BdXRoU2VydmljZXMgYW5kIEFTUC5ORVQgTVZDLiBUaGUgYWJpbGl0eSB0byBydW4gc2l0ZXMgb24gZGVmYXVsdCBIVFRQUyBwb3J0IG1hZGUgdGhpbmdzIHNpbXBsZS4gTGludXggcnVsZXohDQoNCkJVdC4uLndlIGFyZSBub3QgZG9uZSB5ZXQhIExvZ291dCB3YWl0cyB0byBiZSBpbXBsZW1lbnRlZC4=

Sunday, November 8, 2015

Shibboleth-We Are Not Done Yet-Part 2

First tests were done in a LAN type environment; unfortunately life is not so simple: targeted solution should support users outside organization as well as Service Providers hosted in third party data centres. Since we do not want our IdP to be published directly on the Internet we have to use a reverse proxy which breaks our working solution. This might be particular (in terms of lack of out of the box solution-it might exist but I was unable to figure one) to ISA server but it looks (after google-ing a little bit) that a lot of people faced the same problem.

The Problem

Let's suppose our proxy has the public FQDN of proxy.mycompany.com while the internal FQDN of our Shibboleth IdP is idp.mycompany.local.

As a consequence external SP will have IdP metadata configured with URLs like https://proxy.mycompany.com/ipd/profile/SAML2/Redirect/SSO. When a request comes, the SAMLRequest will have AuthnRequest tag the attribute Destination set to this address while the proxy will forward (under default configuration) in relayed request's Host header the value of idp.mycompany.local:8443. This behaviour will lead to the failure of the request with this error message logged (sort of...) "SAML message intended destination endpoint 'https://proxy.mycompany.com/ipd/profile/SAML2/Redirect/SSO' did not match the recipient endpoint '{https://idp.mycompany.local:8443/ipd/profile/SAML2/Redirect/SSO'".

The error is generated in class org.opensaml.saml.common.binding.security.impl.ReceivedEndpointSecurityHandler, method checkEndpointURI.

We have two issues that make finding a solution non trivial:

  • We want to use HTTPS also for accessing our internal IdP (dropping this requirement will not actually lead to a working solution due to second issue)
  • Port translation from default 443 (not specified in external URL) to 8443

Usage of HTTPS for internal IdP

Initially Jetty was configured to use for SSL a certificate with common name of idp.mycompany.local with matches the URL configured in ISA publishing rule.

First solution to try was to change the default ISA behaviour in terms of Host header: check the option to forward the original header but another problem popped up: Jetty was refusing the connection since the Host header did not match any valid certificates in terms of common name.

Second solution tried was to generate a trusted (internally) certificate with common name of proxy.mycompany.com. Now ISA would reject it because it attempted a SSL connection on idp.mycompany.local and received a certificate on proxy.mycompany.com.

Third solution (I haven't tried because it would not solve the problem anyway due to port translation) would be:

  • The certificate installed on ISA should be wild card type
  • We have control on both external and internal DNS
  • Add an external DNS alias of idp.mycompany.com resolving to the ip of the external interface of ISA
  • Add an internal DNS alias of idp.mycompany.com resolving to the ip of the internal interface of ISA
  • Generate a trusted certificate for idp.mycompany.com and configure Jetty to use it
  • Add a publish rule for idp.mycompany.com that forwards requests to the IP of idp.mycompany.local and has filled in "This rule appliest to this published site:" idp.mycompany.com so that header Host will contain this value.

Now we should have circumvented our first problem but it will be of no help since the checkEndpointURI (through calling compareEndpointURIs, etc) will do a string comparison which will obviously fail due to the additional substring ":8443" in the actual receiverEndpoint.

8443 Port

Looking at Jetty documentation we quickly find four solutions to overcome this problem:

  • Start Jetty as root and change HTPPS port to 443; we do not want to do that (would we?)
  • Solutions based on ipchains/iptable which is not actually what we need: there will be an internal kernel routing from 443 to 8443 and at the end of the day we will get into the same problem as mentioned earlier
  • Configuring SetUID Jetty feature; this looks like solving our problem but needs some additional know how which I was not willing to acquire (native compiling, running the service as root...)

The Solution

The solution was to use Jetty rewrite handler capabilities (http://www.eclipse.org/jetty/documentation/current/rewrite-handler.html).

Unfortunately, none of the out of the box handlers provided the required functionality so I quickly wrote one.

Before going into into writing/deploying the custom handler we have to perform some prerequisites:

  • Check to see if org.eclipse.jetty.server.Request supports updating hots and port properties (since standard HttpServletRequest does not)
  • Check how to specify updated port so that it will not be included in receiverEndpoint string
  • Check how rewrite handler is activated

First create a maven project having jetty-rewrite dependency and download sources...

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-rewrite</artifactId>
            <version>9.3.5.v20151012</version>
        </dependency>

First item check passed since looks like we could use

        Request jettyRequest=(Request) request;
        jettyRequest.getMetaData().getURI().setAuthority(host, port);

Second item check was also quick: org.eclipse.jetty.util.appendSchemeHostPort shows that if specifying default ports (depending on schema) or 0 they will not be included.

Last item requires the following steps:

Add

--module=rewrite
etc/rewrite-rules.xml

in $JETTY_BASE/start.ini, where rewrite-rules.xml contains the actual rules and should be located in $JETTY_BASE/etc/rewrite-rules.xml:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">

<!-- =============================================================== -->
<!-- Configure the demos                                             -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <!-- ============================================================= -->
  <!-- Add rewrite rules                                             -->
  <!-- ============================================================= -->
  <Ref refid="Rewrite">

      <!-- protect favicon handling -->
      <Call name="addRule">
        <Arg>
          <New class="ro.totalsoft.jetty.hostheaderrewriter.HostHeaderRewriter">
            <Set name="header">Host</Set>
            <Set name="host">proxy.mycompany.com</Set>
            <Set name="port">0</Set>
            <Set name="terminating">true</Set>
          </New>
        </Arg>
      </Call>
  </Ref>
</Configure>

In order to be selective (if we host also other applications in the same Jetty instance) we should also add the filtering condition

<Set name="headerValue">idp.mycompany.local:8443</Set>

The actual rewrite handler definition is located in $JETTY_HOME/etc/jetty-rewrite.xml as can be seen by inspection the $JETTY_HOME/module/rewrite.mod module definition file.

Remark: looking (as a newbie) in jetty-rewrite.xml might raise a question: what is oldhandler about? The naming is misleading, it should be something like firsthandler since it refers to the first handler in the chain of Jetty handlers. After Jetty applies the rewrite handler definition this will become the first handler in chain.

Now (supposing we have written our own header rule) we have to deploy it in the classpath known by Jetty. To do this we have to add the ext module:

--module=ext

--module=ext
The ext module will enable the lib/ext/*.jar logic.

If this module is activated, then all jar files found in the lib/ext/ paths will be automatically added to the Jetty Server Classpath.

Afterwards we can copy our jar to $JETTY_BASE/lib/ext/ and restart Jetty

**Important remark **: due to a bug (omission) in the logic that activates rules inside the rewrite handler if there is no rule defined a NullPointerException will be thrown in class org.eclipse.jetty.rewrite.handler.RuleContainer, method apply:

    protected String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
    {
        boolean original_set=_originalPathAttribute==null;
                
        for (Rule rule : _rules)
        {
            String applied=rule.matchAndApply(target,request, response);
            if (applied!=null)
            {       

If no rules are defined we will land here with _rules being null...

Epilogue

Ww are not done yet but have made significant progress having IdP side working. As we can assume, we will face same problem on SP side (we do not really want web sites exposed directly on the Internet).

So, next post about Kentor SSO. I would expect to apply the solution with external/internal DNS and using 443 default port (Windows/IIS looks more permissive in this area)

The Code

package com.mycompany.jetty.hostheaderrewriter;

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.rewrite.handler.HeaderRule;
import org.eclipse.jetty.server.Request;

public class HostHeaderRewriter extends HeaderRule{
    String host;
    int port=0;

    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    
    @Override
    protected String apply(String target, String value, HttpServletRequest request, HttpServletResponse response) throws IOException {
        Request jettyRequest=(Request) request;
        jettyRequest.getMetaData().getURI().setAuthority(host, port);
        return target;
    }
}
Rmlyc3QgdGVzdHMgd2VyZSBkb25lIGluIGEgTEFOIHR5cGUgZW52aXJvbm1lbnQ7IHVuZm9ydHVuYXRlbHkgbGlmZSBpcyBub3Qgc28gc2ltcGxlOiB0YXJnZXRlZCBzb2x1dGlvbiBzaG91bGQgc3VwcG9ydCB1c2VycyBvdXRzaWRlIG9yZ2FuaXphdGlvbiBhcyB3ZWxsIGFzIFNlcnZpY2UgUHJvdmlkZXJzIGhvc3RlZCBpbiB0aGlyZCBwYXJ0eSBkYXRhIGNlbnRyZXMuIFNpbmNlIHdlIGRvIG5vdCB3YW50IG91ciBJZFAgdG8gYmUgcHVibGlzaGVkIGRpcmVjdGx5IG9uIHRoZSBJbnRlcm5ldCB3ZSBoYXZlIHRvIHVzZSBhIHJldmVyc2UgcHJveHkgd2hpY2ggYnJlYWtzIG91ciB3b3JraW5nIHNvbHV0aW9uLiBUaGlzIG1pZ2h0IGJlIHBhcnRpY3VsYXIgKGluIHRlcm1zIG9mIGxhY2sgb2Ygb3V0IG9mIHRoZSBib3ggc29sdXRpb24taXQgbWlnaHQgZXhpc3QgYnV0IEkgd2FzIHVuYWJsZSB0byBmaWd1cmUgb25lKSB0byBJU0Egc2VydmVyIGJ1dCBpdCBsb29rcyAoYWZ0ZXIgZ29vZ2xlLWluZyBhIGxpdHRsZSBiaXQpIHRoYXQgYSBsb3Qgb2YgcGVvcGxlIGZhY2VkIHRoZSBzYW1lIHByb2JsZW0uDQoNCiMgVGhlIFByb2JsZW0NCg0KTGV0J3Mgc3VwcG9zZSBvdXIgcHJveHkgaGFzIHRoZSBwdWJsaWMgRlFETiBvZiAqKnByb3h5Lm15Y29tcGFueS5jb20qKiB3aGlsZSB0aGUgaW50ZXJuYWwgRlFETiBvZiBvdXIgU2hpYmJvbGV0aCBJZFAgaXMgKippZHAubXljb21wYW55LmxvY2FsKiouIA0KDQpBcyBhIGNvbnNlcXVlbmNlIGV4dGVybmFsIFNQIHdpbGwgaGF2ZSBJZFAgbWV0YWRhdGEgY29uZmlndXJlZCB3aXRoIFVSTHMgbGlrZSBgaHR0cHM6Ly9wcm94eS5teWNvbXBhbnkuY29tL2lwZC9wcm9maWxlL1NBTUwyL1JlZGlyZWN0L1NTT2AuIFdoZW4gYSByZXF1ZXN0IGNvbWVzLCB0aGUgU0FNTFJlcXVlc3Qgd2lsbCBoYXZlIEF1dGhuUmVxdWVzdCB0YWcgdGhlIGF0dHJpYnV0ZSAqKkRlc3RpbmF0aW9uKiogc2V0IHRvIHRoaXMgYWRkcmVzcyB3aGlsZSB0aGUgcHJveHkgd2lsbCBmb3J3YXJkICh1bmRlciBkZWZhdWx0IGNvbmZpZ3VyYXRpb24pIGluIHJlbGF5ZWQgcmVxdWVzdCdzICoqSG9zdCoqIGhlYWRlciB0aGUgdmFsdWUgb2YgKippZHAubXljb21wYW55LmxvY2FsOjg0NDMqKi4gDQpUaGlzIGJlaGF2aW91ciB3aWxsIGxlYWQgdG8gdGhlIGZhaWx1cmUgb2YgdGhlIHJlcXVlc3Qgd2l0aCB0aGlzIGVycm9yIG1lc3NhZ2UgbG9nZ2VkIChzb3J0IG9mLi4uKSAqIlNBTUwgbWVzc2FnZSBpbnRlbmRlZCBkZXN0aW5hdGlvbiBlbmRwb2ludCAnaHR0cHM6Ly9wcm94eS5teWNvbXBhbnkuY29tL2lwZC9wcm9maWxlL1NBTUwyL1JlZGlyZWN0L1NTTycgZGlkIG5vdCBtYXRjaCB0aGUgcmVjaXBpZW50IGVuZHBvaW50ICd7aHR0cHM6Ly9pZHAubXljb21wYW55LmxvY2FsOjg0NDMvaXBkL3Byb2ZpbGUvU0FNTDIvUmVkaXJlY3QvU1NPJyIqLg0KDQpUaGUgZXJyb3IgaXMgZ2VuZXJhdGVkIGluIGNsYXNzIGBvcmcub3BlbnNhbWwuc2FtbC5jb21tb24uYmluZGluZy5zZWN1cml0eS5pbXBsLlJlY2VpdmVkRW5kcG9pbnRTZWN1cml0eUhhbmRsZXJgLCBtZXRob2QgYGNoZWNrRW5kcG9pbnRVUklgLg0KDQpXZSBoYXZlIHR3byBpc3N1ZXMgdGhhdCBtYWtlIGZpbmRpbmcgYSBzb2x1dGlvbiBub24gdHJpdmlhbDoNCg0KKiBXZSB3YW50IHRvIHVzZSBIVFRQUyBhbHNvIGZvciBhY2Nlc3Npbmcgb3VyIGludGVybmFsIElkUCAoZHJvcHBpbmcgdGhpcyByZXF1aXJlbWVudCB3aWxsIG5vdCBhY3R1YWxseSBsZWFkIHRvIGEgd29ya2luZyBzb2x1dGlvbiBkdWUgdG8gc2Vjb25kIGlzc3VlKQ0KKiBQb3J0IHRyYW5zbGF0aW9uIGZyb20gZGVmYXVsdCA0NDMgKG5vdCBzcGVjaWZpZWQgaW4gZXh0ZXJuYWwgVVJMKSB0byA4NDQzDQoNCiMjIFVzYWdlIG9mIEhUVFBTIGZvciBpbnRlcm5hbCBJZFANCg0KSW5pdGlhbGx5IEpldHR5IHdhcyBjb25maWd1cmVkIHRvIHVzZSBmb3IgU1NMIGEgY2VydGlmaWNhdGUgd2l0aCBjb21tb24gbmFtZSBvZiAqKmlkcC5teWNvbXBhbnkubG9jYWwqKiB3aXRoIG1hdGNoZXMgdGhlIFVSTCBjb25maWd1cmVkIGluIElTQSBwdWJsaXNoaW5nIHJ1bGUuDQoNCkZpcnN0IHNvbHV0aW9uIHRvIHRyeSB3YXMgdG8gY2hhbmdlIHRoZSBkZWZhdWx0IElTQSBiZWhhdmlvdXIgaW4gdGVybXMgb2YgKipIb3N0KiogaGVhZGVyOiBjaGVjayB0aGUgb3B0aW9uIHRvIGZvcndhcmQgdGhlIG9yaWdpbmFsIGhlYWRlciBidXQgYW5vdGhlciBwcm9ibGVtIHBvcHBlZCB1cDogSmV0dHkgd2FzIHJlZnVzaW5nIHRoZSBjb25uZWN0aW9uIHNpbmNlIHRoZSAqKkhvc3QqKiBoZWFkZXIgZGlkIG5vdCBtYXRjaCBhbnkgdmFsaWQgY2VydGlmaWNhdGVzIGluIHRlcm1zIG9mIGNvbW1vbiBuYW1lLg0KDQpTZWNvbmQgc29sdXRpb24gdHJpZWQgd2FzIHRvIGdlbmVyYXRlIGEgdHJ1c3RlZCAoaW50ZXJuYWxseSkgY2VydGlmaWNhdGUgd2l0aCBjb21tb24gbmFtZSBvZiAqKnByb3h5Lm15Y29tcGFueS5jb20qKi4gTm93IElTQSB3b3VsZCByZWplY3QgaXQgYmVjYXVzZSBpdCBhdHRlbXB0ZWQgYSBTU0wgY29ubmVjdGlvbiBvbiAqKmlkcC5teWNvbXBhbnkubG9jYWwqKiBhbmQgcmVjZWl2ZWQgYSBjZXJ0aWZpY2F0ZSBvbiAqKnByb3h5Lm15Y29tcGFueS5jb20qKi4NCg0KVGhpcmQgc29sdXRpb24gKEkgaGF2ZW4ndCB0cmllZCBiZWNhdXNlIGl0IHdvdWxkIG5vdCBzb2x2ZSB0aGUgcHJvYmxlbSBhbnl3YXkgZHVlIHRvIHBvcnQgdHJhbnNsYXRpb24pIHdvdWxkIGJlOg0KDQoqIFRoZSBjZXJ0aWZpY2F0ZSBpbnN0YWxsZWQgb24gSVNBIHNob3VsZCBiZSB3aWxkIGNhcmQgdHlwZQ0KKiBXZSBoYXZlIGNvbnRyb2wgb24gYm90aCBleHRlcm5hbCBhbmQgaW50ZXJuYWwgRE5TDQoqIEFkZCBhbiBleHRlcm5hbCBETlMgYWxpYXMgb2YgKippZHAubXljb21wYW55LmNvbSoqIHJlc29sdmluZyB0byB0aGUgaXAgb2YgdGhlIGV4dGVybmFsIGludGVyZmFjZSBvZiBJU0ENCiogQWRkIGFuIGludGVybmFsIEROUyBhbGlhcyBvZiAqKmlkcC5teWNvbXBhbnkuY29tKiogcmVzb2x2aW5nIHRvIHRoZSBpcCBvZiB0aGUgaW50ZXJuYWwgaW50ZXJmYWNlIG9mIElTQQ0KKiBHZW5lcmF0ZSBhIHRydXN0ZWQgY2VydGlmaWNhdGUgZm9yICoqaWRwLm15Y29tcGFueS5jb20qKiBhbmQgY29uZmlndXJlIEpldHR5IHRvIHVzZSBpdA0KKiBBZGQgYSBwdWJsaXNoIHJ1bGUgZm9yICoqaWRwLm15Y29tcGFueS5jb20qKiB0aGF0IGZvcndhcmRzIHJlcXVlc3RzIHRvIHRoZSBJUCBvZiAgKippZHAubXljb21wYW55LmxvY2FsKiogYW5kIGhhcyBmaWxsZWQgaW4gIlRoaXMgcnVsZSBhcHBsaWVzdCB0byB0aGlzIHB1Ymxpc2hlZCBzaXRlOiIgKippZHAubXljb21wYW55LmNvbSoqIHNvIHRoYXQgaGVhZGVyIEhvc3Qgd2lsbCBjb250YWluIHRoaXMgdmFsdWUuDQoNCk5vdyB3ZSBzaG91bGQgaGF2ZSBjaXJjdW12ZW50ZWQgb3VyIGZpcnN0IHByb2JsZW0gYnV0IGl0IHdpbGwgYmUgb2Ygbm8gaGVscCBzaW5jZSB0aGUgYGNoZWNrRW5kcG9pbnRVUklgICh0aHJvdWdoIGNhbGxpbmcgYGNvbXBhcmVFbmRwb2ludFVSSXNgLCBldGMpIHdpbGwgZG8gYSBzdHJpbmcgY29tcGFyaXNvbiB3aGljaCB3aWxsIG9idmlvdXNseSBmYWlsIGR1ZSB0byB0aGUgYWRkaXRpb25hbCBzdWJzdHJpbmcgIjo4NDQzIiBpbiB0aGUgYWN0dWFsIHJlY2VpdmVyRW5kcG9pbnQuDQoNCiMjIDg0NDMgUG9ydA0KDQpMb29raW5nIGF0IEpldHR5IGRvY3VtZW50YXRpb24gd2UgcXVpY2tseSBmaW5kIGZvdXIgc29sdXRpb25zIHRvIG92ZXJjb21lIHRoaXMgcHJvYmxlbToNCg0KKiBTdGFydCBKZXR0eSBhcyByb290IGFuZCBjaGFuZ2UgSFRQUFMgcG9ydCB0byA0NDM7IHdlIGRvIG5vdCB3YW50IHRvIGRvIHRoYXQgKHdvdWxkIHdlPykNCiogU29sdXRpb25zIGJhc2VkIG9uIGlwY2hhaW5zL2lwdGFibGUgd2hpY2ggaXMgbm90IGFjdHVhbGx5IHdoYXQgd2UgbmVlZDogdGhlcmUgd2lsbCBiZSBhbiBpbnRlcm5hbCBrZXJuZWwgcm91dGluZyBmcm9tIDQ0MyB0byA4NDQzIGFuZCBhdCB0aGUgZW5kIG9mIHRoZSBkYXkgd2Ugd2lsbCBnZXQgaW50byB0aGUgc2FtZSBwcm9ibGVtIGFzIG1lbnRpb25lZCBlYXJsaWVyDQoqIENvbmZpZ3VyaW5nIFNldFVJRCBKZXR0eSBmZWF0dXJlOyB0aGlzIGxvb2tzIGxpa2Ugc29sdmluZyBvdXIgcHJvYmxlbSBidXQgbmVlZHMgc29tZSBhZGRpdGlvbmFsIGtub3cgaG93IHdoaWNoIEkgd2FzIG5vdCB3aWxsaW5nIHRvIGFjcXVpcmUgKG5hdGl2ZSBjb21waWxpbmcsIHJ1bm5pbmcgdGhlIHNlcnZpY2UgYXMgcm9vdC4uLikNCg0KIyBUaGUgU29sdXRpb24NCg0KVGhlIHNvbHV0aW9uIHdhcyB0byB1c2UgSmV0dHkgcmV3cml0ZSBoYW5kbGVyIGNhcGFiaWxpdGllcyAoW2h0dHA6Ly93d3cuZWNsaXBzZS5vcmcvamV0dHkvZG9jdW1lbnRhdGlvbi9jdXJyZW50L3Jld3JpdGUtaGFuZGxlci5odG1sXShodHRwOi8vd3d3LmVjbGlwc2Uub3JnL2pldHR5L2RvY3VtZW50YXRpb24vY3VycmVudC9yZXdyaXRlLWhhbmRsZXIuaHRtbCkpLg0KDQpVbmZvcnR1bmF0ZWx5LCBub25lIG9mIHRoZSBvdXQgb2YgdGhlIGJveCBoYW5kbGVycyBwcm92aWRlZCB0aGUgcmVxdWlyZWQgZnVuY3Rpb25hbGl0eSBzbyBJIHF1aWNrbHkgd3JvdGUgb25lLg0KDQpCZWZvcmUgZ29pbmcgaW50byBpbnRvIHdyaXRpbmcvZGVwbG95aW5nIHRoZSBjdXN0b20gaGFuZGxlciB3ZSBoYXZlIHRvIHBlcmZvcm0gc29tZSBwcmVyZXF1aXNpdGVzOg0KDQoqIENoZWNrIHRvIHNlZSBpZiBgb3JnLmVjbGlwc2UuamV0dHkuc2VydmVyLlJlcXVlc3RgIHN1cHBvcnRzIHVwZGF0aW5nIGhvdHMgYW5kIHBvcnQgcHJvcGVydGllcyAoc2luY2Ugc3RhbmRhcmQgSHR0cFNlcnZsZXRSZXF1ZXN0IGRvZXMgbm90KQ0KKiBDaGVjayBob3cgdG8gc3BlY2lmeSB1cGRhdGVkIHBvcnQgc28gdGhhdCBpdCB3aWxsIG5vdCBiZSBpbmNsdWRlZCBpbiByZWNlaXZlckVuZHBvaW50IHN0cmluZw0KKiBDaGVjayBob3cgcmV3cml0ZSBoYW5kbGVyIGlzIGFjdGl2YXRlZA0KDQogDQpGaXJzdCBjcmVhdGUgYSBtYXZlbiBwcm9qZWN0IGhhdmluZyBqZXR0eS1yZXdyaXRlIGRlcGVuZGVuY3kgYW5kIGRvd25sb2FkIHNvdXJjZXMuLi4NCg0KYGBgeG1sDQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmVjbGlwc2UuamV0dHk8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5qZXR0eS1yZXdyaXRlPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+OS4zLjUudjIwMTUxMDEyPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQpgYGAgDQpGaXJzdCBpdGVtIGNoZWNrIHBhc3NlZCBzaW5jZSBsb29rcyBsaWtlIHdlIGNvdWxkIHVzZSANCmBgYGphdmENCiAgICAgICAgUmVxdWVzdCBqZXR0eVJlcXVlc3Q9KFJlcXVlc3QpIHJlcXVlc3Q7DQogICAgICAgIGpldHR5UmVxdWVzdC5nZXRNZXRhRGF0YSgpLmdldFVSSSgpLnNldEF1dGhvcml0eShob3N0LCBwb3J0KTsNCg0KYGBgDQpTZWNvbmQgaXRlbSBjaGVjayB3YXMgYWxzbyBxdWljazogYG9yZy5lY2xpcHNlLmpldHR5LnV0aWwuYXBwZW5kU2NoZW1lSG9zdFBvcnRgIHNob3dzIHRoYXQgaWYgc3BlY2lmeWluZyBkZWZhdWx0IHBvcnRzIChkZXBlbmRpbmcgb24gc2NoZW1hKSBvciAwIHRoZXkgd2lsbCBub3QgYmUgaW5jbHVkZWQuDQoNCg0KTGFzdCBpdGVtIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgc3RlcHM6DQoNCkFkZCANCmBgYA0KLS1tb2R1bGU9cmV3cml0ZQ0KZXRjL3Jld3JpdGUtcnVsZXMueG1sDQpgYGANCmluIGAkSkVUVFlfQkFTRS9zdGFydC5pbmlgLCB3aGVyZSBgcmV3cml0ZS1ydWxlcy54bWxgIGNvbnRhaW5zIHRoZSBhY3R1YWwgcnVsZXMgYW5kIHNob3VsZCBiZSBsb2NhdGVkIGluICBgJEpFVFRZX0JBU0UvZXRjL3Jld3JpdGUtcnVsZXMueG1sYDoNCg0KYGBgeG1sDQo8P3htbCB2ZXJzaW9uPSIxLjAiPz4NCjwhRE9DVFlQRSBDb25maWd1cmUgUFVCTElDICItLy9KZXR0eS8vQ29uZmlndXJlLy9FTiIgImh0dHA6Ly93d3cuZWNsaXBzZS5vcmcvamV0dHkvY29uZmlndXJlXzlfMy5kdGQiPg0KDQo8IS0tID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAtLT4NCjwhLS0gQ29uZmlndXJlIHRoZSBkZW1vcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPg0KPCEtLSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gLS0+DQo8Q29uZmlndXJlIGlkPSJTZXJ2ZXIiIGNsYXNzPSJvcmcuZWNsaXBzZS5qZXR0eS5zZXJ2ZXIuU2VydmVyIj4NCg0KICA8IS0tID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gLS0+DQogIDwhLS0gQWRkIHJld3JpdGUgcnVsZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4NCiAgPCEtLSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IC0tPg0KICA8UmVmIHJlZmlkPSJSZXdyaXRlIj4NCg0KICAgICAgPCEtLSBwcm90ZWN0IGZhdmljb24gaGFuZGxpbmcgLS0+DQogICAgICA8Q2FsbCBuYW1lPSJhZGRSdWxlIj4NCiAgICAgICAgPEFyZz4NCiAgICAgICAgICA8TmV3IGNsYXNzPSJyby50b3RhbHNvZnQuamV0dHkuaG9zdGhlYWRlcnJld3JpdGVyLkhvc3RIZWFkZXJSZXdyaXRlciI+DQogICAgICAgICAgICA8U2V0IG5hbWU9ImhlYWRlciI+SG9zdDwvU2V0Pg0KICAgICAgICAgICAgPFNldCBuYW1lPSJob3N0Ij5wcm94eS5teWNvbXBhbnkuY29tPC9TZXQ+DQogICAgICAgICAgICA8U2V0IG5hbWU9InBvcnQiPjA8L1NldD4NCiAgICAgICAgICAgIDxTZXQgbmFtZT0idGVybWluYXRpbmciPnRydWU8L1NldD4NCiAgICAgICAgICA8L05ldz4NCiAgICAgICAgPC9Bcmc+DQogICAgICA8L0NhbGw+DQogIDwvUmVmPg0KPC9Db25maWd1cmU+DQpgYGANCg0KSW4gb3JkZXIgdG8gYmUgc2VsZWN0aXZlIChpZiB3ZSBob3N0IGFsc28gb3RoZXIgYXBwbGljYXRpb25zIGluIHRoZSBzYW1lIEpldHR5IGluc3RhbmNlKSB3ZSBzaG91bGQgYWxzbyBhZGQgdGhlIGZpbHRlcmluZyBjb25kaXRpb24NCg0KYGBgeG1sDQo8U2V0IG5hbWU9ImhlYWRlclZhbHVlIj5pZHAubXljb21wYW55LmxvY2FsOjg0NDM8L1NldD4NCmBgYA0KDQpUaGUgYWN0dWFsIHJld3JpdGUgaGFuZGxlciBkZWZpbml0aW9uIGlzIGxvY2F0ZWQgaW4gYCRKRVRUWV9IT01FL2V0Yy9qZXR0eS1yZXdyaXRlLnhtbGAgYXMgY2FuIGJlIHNlZW4gYnkgaW5zcGVjdGlvbiB0aGUgYCRKRVRUWV9IT01FL21vZHVsZS9yZXdyaXRlLm1vZGAgbW9kdWxlIGRlZmluaXRpb24gZmlsZS4NCg0KUmVtYXJrOiBsb29raW5nIChhcyBhIG5ld2JpZSkgaW4gYGpldHR5LXJld3JpdGUueG1sYCBtaWdodCByYWlzZSBhIHF1ZXN0aW9uOiB3aGF0IGlzIGBvbGRoYW5kbGVyYCBhYm91dD8gVGhlIG5hbWluZyBpcyBtaXNsZWFkaW5nLCBpdCBzaG91bGQgYmUgc29tZXRoaW5nIGxpa2UgYGZpcnN0aGFuZGxlcmAgc2luY2UgaXQgcmVmZXJzIHRvIHRoZSBmaXJzdCBoYW5kbGVyIGluIHRoZSBjaGFpbiBvZiBKZXR0eSBoYW5kbGVycy4gQWZ0ZXIgSmV0dHkgYXBwbGllcyB0aGUgcmV3cml0ZSBoYW5kbGVyIGRlZmluaXRpb24gdGhpcyB3aWxsIGJlY29tZSB0aGUgZmlyc3QgaGFuZGxlciBpbiBjaGFpbi4NCg0KTm93IChzdXBwb3Npbmcgd2UgaGF2ZSB3cml0dGVuIG91ciBvd24gaGVhZGVyIHJ1bGUpIHdlIGhhdmUgdG8gZGVwbG95IGl0IGluIHRoZSBjbGFzc3BhdGgga25vd24gYnkgSmV0dHkuIFRvIGRvIHRoaXMgd2UgaGF2ZSB0byBhZGQgdGhlICoqZXh0KiogbW9kdWxlOg0KDQotLW1vZHVsZT1leHQNCmBgYA0KLS1tb2R1bGU9ZXh0DQpUaGUgZXh0IG1vZHVsZSB3aWxsIGVuYWJsZSB0aGUgbGliL2V4dC8qLmphciBsb2dpYy4NCg0KSWYgdGhpcyBtb2R1bGUgaXMgYWN0aXZhdGVkLCB0aGVuIGFsbCBqYXIgZmlsZXMgZm91bmQgaW4gdGhlIGxpYi9leHQvIHBhdGhzIHdpbGwgYmUgYXV0b21hdGljYWxseSBhZGRlZCB0byB0aGUgSmV0dHkgU2VydmVyIENsYXNzcGF0aC4NCmBgYA0KDQpBZnRlcndhcmRzIHdlIGNhbiBjb3B5IG91ciBqYXIgdG8gJEpFVFRZX0JBU0UvbGliL2V4dC8gYW5kIHJlc3RhcnQgSmV0dHkNCg0KKipJbXBvcnRhbnQgcmVtYXJrICoqOiBkdWUgdG8gYSBidWcgKG9taXNzaW9uKSBpbiB0aGUgbG9naWMgdGhhdCBhY3RpdmF0ZXMgcnVsZXMgaW5zaWRlIHRoZSByZXdyaXRlIGhhbmRsZXIgaWYgdGhlcmUgaXMgbm8gcnVsZSBkZWZpbmVkIGEgTnVsbFBvaW50ZXJFeGNlcHRpb24gd2lsbCBiZSB0aHJvd24gaW4gY2xhc3MgYG9yZy5lY2xpcHNlLmpldHR5LnJld3JpdGUuaGFuZGxlci5SdWxlQ29udGFpbmVyYCwgbWV0aG9kIGBhcHBseWA6DQoNCmBgYGphdmENCiAgICBwcm90ZWN0ZWQgU3RyaW5nIGFwcGx5KFN0cmluZyB0YXJnZXQsIEh0dHBTZXJ2bGV0UmVxdWVzdCByZXF1ZXN0LCBIdHRwU2VydmxldFJlc3BvbnNlIHJlc3BvbnNlKSB0aHJvd3MgSU9FeGNlcHRpb24NCiAgICB7DQogICAgICAgIGJvb2xlYW4gb3JpZ2luYWxfc2V0PV9vcmlnaW5hbFBhdGhBdHRyaWJ1dGU9PW51bGw7DQogICAgICAgICAgICAgICAgDQogICAgICAgIGZvciAoUnVsZSBydWxlIDogX3J1bGVzKQ0KICAgICAgICB7DQogICAgICAgICAgICBTdHJpbmcgYXBwbGllZD1ydWxlLm1hdGNoQW5kQXBwbHkodGFyZ2V0LHJlcXVlc3QsIHJlc3BvbnNlKTsNCiAgICAgICAgICAgIGlmIChhcHBsaWVkIT1udWxsKQ0KICAgICAgICAgICAgeyAgICAgICANCg0KYGBgDQpJZiBubyBydWxlcyBhcmUgZGVmaW5lZCB3ZSB3aWxsIGxhbmQgaGVyZSB3aXRoIGBfcnVsZXNgIGJlaW5nIG51bGwuLi4NCg0KIyBFcGlsb2d1ZQ0KDQpXdyBhcmUgbm90IGRvbmUgeWV0IGJ1dCBoYXZlIG1hZGUgc2lnbmlmaWNhbnQgcHJvZ3Jlc3MgaGF2aW5nIElkUCBzaWRlIHdvcmtpbmcuIEFzIHdlIGNhbiBhc3N1bWUsIHdlIHdpbGwgZmFjZSBzYW1lIHByb2JsZW0gb24gU1Agc2lkZSAod2UgZG8gbm90IHJlYWxseSB3YW50IHdlYiBzaXRlcyBleHBvc2VkIGRpcmVjdGx5IG9uIHRoZSBJbnRlcm5ldCkuDQoNClNvLCBuZXh0IHBvc3QgYWJvdXQgS2VudG9yIFNTTy4gSSB3b3VsZCBleHBlY3QgdG8gYXBwbHkgdGhlIHNvbHV0aW9uIHdpdGggZXh0ZXJuYWwvaW50ZXJuYWwgRE5TIGFuZCB1c2luZyA0NDMgZGVmYXVsdCBwb3J0IChXaW5kb3dzL0lJUyBsb29rcyBtb3JlIHBlcm1pc3NpdmUgaW4gdGhpcyBhcmVhKQ0KDQojIFRoZSBDb2RlDQoNCmBgYGphdmENCnBhY2thZ2UgY29tLm15Y29tcGFueS5qZXR0eS5ob3N0aGVhZGVycmV3cml0ZXI7DQoNCmltcG9ydCBqYXZhLmlvLklPRXhjZXB0aW9uOw0KaW1wb3J0IGphdmF4LnNlcnZsZXQuaHR0cC5IdHRwU2VydmxldFJlcXVlc3Q7DQppbXBvcnQgamF2YXguc2VydmxldC5odHRwLkh0dHBTZXJ2bGV0UmVzcG9uc2U7DQppbXBvcnQgb3JnLmVjbGlwc2UuamV0dHkucmV3cml0ZS5oYW5kbGVyLkhlYWRlclJ1bGU7DQppbXBvcnQgb3JnLmVjbGlwc2UuamV0dHkuc2VydmVyLlJlcXVlc3Q7DQoNCnB1YmxpYyBjbGFzcyBIb3N0SGVhZGVyUmV3cml0ZXIgZXh0ZW5kcyBIZWFkZXJSdWxlew0KICAgIFN0cmluZyBob3N0Ow0KICAgIGludCBwb3J0PTA7DQoNCiAgICBwdWJsaWMgU3RyaW5nIGdldEhvc3QoKSB7DQogICAgICAgIHJldHVybiBob3N0Ow0KICAgIH0NCiAgICBwdWJsaWMgdm9pZCBzZXRIb3N0KFN0cmluZyBob3N0KSB7DQogICAgICAgIHRoaXMuaG9zdCA9IGhvc3Q7DQogICAgfQ0KDQogICAgcHVibGljIGludCBnZXRQb3J0KCkgew0KICAgICAgICByZXR1cm4gcG9ydDsNCiAgICB9DQogICAgcHVibGljIHZvaWQgc2V0UG9ydChpbnQgcG9ydCkgew0KICAgICAgICB0aGlzLnBvcnQgPSBwb3J0Ow0KICAgIH0NCiAgICANCiAgICBAT3ZlcnJpZGUNCiAgICBwcm90ZWN0ZWQgU3RyaW5nIGFwcGx5KFN0cmluZyB0YXJnZXQsIFN0cmluZyB2YWx1ZSwgSHR0cFNlcnZsZXRSZXF1ZXN0IHJlcXVlc3QsIEh0dHBTZXJ2bGV0UmVzcG9uc2UgcmVzcG9uc2UpIHRocm93cyBJT0V4Y2VwdGlvbiB7DQogICAgICAgIFJlcXVlc3QgamV0dHlSZXF1ZXN0PShSZXF1ZXN0KSByZXF1ZXN0Ow0KICAgICAgICBqZXR0eVJlcXVlc3QuZ2V0TWV0YURhdGEoKS5nZXRVUkkoKS5zZXRBdXRob3JpdHkoaG9zdCwgcG9ydCk7DQogICAgICAgIHJldHVybiB0YXJnZXQ7DQogICAgfQ0KfQ0KYGBg

Monday, November 2, 2015

Shibboleth-We Are Not Done Yet-Part 1

First thing that popped out in my mind after basic installation/configuration worked was to check how could be the login page customized. But where to find that page?

The answer is simple: login.vm (idp-conf\src\main\resources\views\login.vm), a template based on Velocity. It was easy to find: look at the id assigned to password field (for instance), j_password and do a recursive search in sources directory.

Note: If changing main.css is necessary please notice that the UI part is packed as idp.war...

This is not enough; we want to understand how it works and where to look if we get into trouble.

To start with: a lot of Shibbolet functionality (including UI behaviour) is based on Spring Web Flow. The main configuration file (webflow-config.xml) can be found by looking into web.config.

    <servlet>
        <servlet-name>idp</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>${idp.home}/system/conf/mvc-beans.xml, ${idp.home}/system/conf/webflow-config.xml</param-value>
        </init-param>
...

It defines application web-flows accessible through the registry (org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl, method getFlowDefinition, spring-webflow-2.4.1.RELEASE.jar).

The flow we are interested in is that with id="SAML2/Unsolicited/SSO". When we access HTTPS://<shibblolet-url>:<https-port>/idp/profile/SAML2/Unsolicited/SSO?providerId=<SP_entityID> DispatcherHandler will finally call org.springframework.webflow.mvc.servlet.FlowHandlerMapping, method getHandlerInternal, parameter HttpServletRequest request. Request properties are as follows:

  • contextPath: "/idp"
  • servletPath: "/profile"
  • pathInfo: "/SAML2/Unsolicited/SSO"

Based on pathInfo the web-flow with the matching id is activated. A breakpoint set in org.springframework.webflow.executor.FlowExecutorImpl, method launchExecution, after FlowDefinition flowDefinition = definitionLocator.getFlowDefinition(flowId); allows us to examine flowDefinition and see the states defined in the work-flow that is about to be executed.

Having a look in the flow definition (path="../system/flows/saml/saml2/sso-unsolicited-flow.xml") is quite confusing for the newbies: no actually flow description (according to what you will see in tutorials) but just two regular Spring beans definition: the point is that any such bean exposing an 'execute' method will be treated as a state-action and the methods will be called in the order of bean definition.

OK, but where comes in action our login.vm? The parent="saml2.sso.abstract" attribute in flow tag definition is the key: our flow inherits its definition from that flow (identified by this id in web-config.xml). Somehow (I haven't checked all the inheritance/definitions chain) the authn/Password sub-flow is activated, sub-flow that contains a view-state referring our template in the view attribute:

    <view-state id="DisplayUsernamePasswordPage" view="login">
        <on-render>
            <evaluate expression="environment" result="viewScope.environment" />
            <evaluate expression="opensamlProfileRequestContext" result="viewScope.profileRequestContext" />
            <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result="viewScope.authenticationContext" />
...

The transition <transition on="proceed" to="ExtractUsernamePasswordFromFormRequest"> is activated when the Login button is pressed (id='_eventId_proceed'-> proceed constant will become available to be evaluated by the transition condition based on the convention id=_eventId_... ).

Action state ExtractUsernamePasswordFromFormRequest will be executed:

    <action-state id="ExtractUsernamePasswordFromFormRequest">
        <evaluate expression="ExtractUsernamePasswordFromFormRequest" />
        <evaluate expression="'proceed'" />
    
        <!-- Let the validate action handle any problems later. -->        
        <transition to="ValidateUsernamePassword" />
    </action-state>

The <evaluate expression="ExtractUsernamePasswordFromFormRequest" /> has the role to call execute method of bean with id="ExtractUsernamePasswordFromFormRequest" ( class defined in module Shibboleth IdP :: Authentication Implementation) while <evaluate expression="'proceed'"/> just makes 'proceed' the current value to be evaluated by subsequent transitions (not actually used here but pattern used in may other places).

Finally we land in ValidateUsernamePasswordAgainstLDAP.execute method.

DQpGaXJzdCB0aGluZyB0aGF0IHBvcHBlZCBvdXQgaW4gbXkgbWluZCBhZnRlciBiYXNpYyBpbnN0YWxsYXRpb24vY29uZmlndXJhdGlvbiB3b3JrZWQgd2FzIHRvIGNoZWNrIGhvdyBjb3VsZCBiZSB0aGUgbG9naW4gcGFnZSBjdXN0b21pemVkLiAgQnV0IHdoZXJlIHRvIGZpbmQgdGhhdCBwYWdlPw0KDQpUaGUgYW5zd2VyIGlzIHNpbXBsZTogbG9naW4udm0gKGBpZHAtY29uZlxzcmNcbWFpblxyZXNvdXJjZXNcdmlld3NcbG9naW4udm1gKSwgYSB0ZW1wbGF0ZSBiYXNlZCBvbiBWZWxvY2l0eS4gSXQgd2FzIGVhc3kgdG8gZmluZDogbG9vayBhdCB0aGUgaWQgYXNzaWduZWQgdG8gcGFzc3dvcmQgZmllbGQgKGZvciBpbnN0YW5jZSksIGBqX3Bhc3N3b3JkYCAgYW5kIGRvIGEgcmVjdXJzaXZlIHNlYXJjaCBpbiAgc291cmNlcyBkaXJlY3RvcnkuDQoNCioqTm90ZSoqOiBJZiBjaGFuZ2luZyAqbWFpbi5jc3MqIGlzIG5lY2Vzc2FyeSBwbGVhc2Ugbm90aWNlIHRoYXQgdGhlIFVJIHBhcnQgaXMgcGFja2VkIGFzIGlkcC53YXIuLi4NCg0KVGhpcyBpcyBub3QgZW5vdWdoOyB3ZSB3YW50IHRvIHVuZGVyc3RhbmQgaG93IGl0IHdvcmtzIGFuZCB3aGVyZSB0byBsb29rIGlmIHdlIGdldCBpbnRvIHRyb3VibGUuDQoNClRvIHN0YXJ0IHdpdGg6IGEgbG90IG9mIFNoaWJib2xldCBmdW5jdGlvbmFsaXR5IChpbmNsdWRpbmcgVUkgYmVoYXZpb3VyKSBpcyBiYXNlZCBvbiBTcHJpbmcgV2ViIEZsb3cuIFRoZSBtYWluIGNvbmZpZ3VyYXRpb24gZmlsZSAoYHdlYmZsb3ctY29uZmlnLnhtbGApIGNhbiBiZSBmb3VuZCBieSBsb29raW5nIGludG8gYHdlYi5jb25maWdgLg0KYGBgeG1sDQogICAgPHNlcnZsZXQ+DQogICAgICAgIDxzZXJ2bGV0LW5hbWU+aWRwPC9zZXJ2bGV0LW5hbWU+DQogICAgICAgIDxzZXJ2bGV0LWNsYXNzPm9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLnNlcnZsZXQuRGlzcGF0Y2hlclNlcnZsZXQ8L3NlcnZsZXQtY2xhc3M+DQogICAgICAgIDxpbml0LXBhcmFtPg0KICAgICAgICAgICAgPHBhcmFtLW5hbWU+Y29udGV4dENvbmZpZ0xvY2F0aW9uPC9wYXJhbS1uYW1lPg0KICAgICAgICAgICAgPHBhcmFtLXZhbHVlPiR7aWRwLmhvbWV9L3N5c3RlbS9jb25mL212Yy1iZWFucy54bWwsICR7aWRwLmhvbWV9L3N5c3RlbS9jb25mL3dlYmZsb3ctY29uZmlnLnhtbDwvcGFyYW0tdmFsdWU+DQogICAgICAgIDwvaW5pdC1wYXJhbT4NCi4uLg0KYGBgDQoNCkl0IGRlZmluZXMgYXBwbGljYXRpb24gd2ViLWZsb3dzIGFjY2Vzc2libGUgdGhyb3VnaCB0aGUgcmVnaXN0cnkgKGBvcmcuc3ByaW5nZnJhbWV3b3JrLndlYmZsb3cuZGVmaW5pdGlvbi5yZWdpc3RyeS5GbG93RGVmaW5pdGlvblJlZ2lzdHJ5SW1wbGAsIG1ldGhvZCBgZ2V0Rmxvd0RlZmluaXRpb25gLCBgc3ByaW5nLXdlYmZsb3ctMi40LjEuUkVMRUFTRS5qYXJgKS4NCg0KVGhlIGZsb3cgd2UgYXJlIGludGVyZXN0ZWQgaW4gaXMgdGhhdCB3aXRoIGBpZD0iU0FNTDIvVW5zb2xpY2l0ZWQvU1NPImAuIFdoZW4gd2UgYWNjZXNzIGBIVFRQUzovLzxzaGliYmxvbGV0LXVybD46PGh0dHBzLXBvcnQ+L2lkcC9wcm9maWxlL1NBTUwyL1Vuc29saWNpdGVkL1NTTz9wcm92aWRlcklkPTxTUF9lbnRpdHlJRD5gIGBEaXNwYXRjaGVySGFuZGxlcmAgd2lsbCBmaW5hbGx5IGNhbGwgYG9yZy5zcHJpbmdmcmFtZXdvcmsud2ViZmxvdy5tdmMuc2VydmxldC5GbG93SGFuZGxlck1hcHBpbmdgLCBtZXRob2QgYGdldEhhbmRsZXJJbnRlcm5hbGAsIHBhcmFtZXRlciBgSHR0cFNlcnZsZXRSZXF1ZXN0IHJlcXVlc3RgLiBSZXF1ZXN0IHByb3BlcnRpZXMgYXJlIGFzIGZvbGxvd3M6DQoNCiogY29udGV4dFBhdGg6ICIvaWRwIg0KKiBzZXJ2bGV0UGF0aDogIi9wcm9maWxlIg0KKiBwYXRoSW5mbzogIi9TQU1MMi9VbnNvbGljaXRlZC9TU08iDQoNCkJhc2VkIG9uIGBwYXRoSW5mb2AgdGhlIHdlYi1mbG93IHdpdGggdGhlIG1hdGNoaW5nIGlkIGlzIGFjdGl2YXRlZC4gQSBicmVha3BvaW50IHNldCBpbiBgb3JnLnNwcmluZ2ZyYW1ld29yay53ZWJmbG93LmV4ZWN1dG9yLkZsb3dFeGVjdXRvckltcGxgLCBtZXRob2QgYGxhdW5jaEV4ZWN1dGlvbmAsIGFmdGVyIGBGbG93RGVmaW5pdGlvbiBmbG93RGVmaW5pdGlvbiA9IGRlZmluaXRpb25Mb2NhdG9yLmdldEZsb3dEZWZpbml0aW9uKGZsb3dJZCk7YCBhbGxvd3MgdXMgdG8gZXhhbWluZSBgZmxvd0RlZmluaXRpb25gIGFuZCBzZWUgdGhlIHN0YXRlcyBkZWZpbmVkIGluIHRoZSB3b3JrLWZsb3cgdGhhdCBpcyBhYm91dCB0byBiZSBleGVjdXRlZC4NCg0KSGF2aW5nIGEgbG9vayBpbiB0aGUgZmxvdyBkZWZpbml0aW9uIChgcGF0aD0iLi4vc3lzdGVtL2Zsb3dzL3NhbWwvc2FtbDIvc3NvLXVuc29saWNpdGVkLWZsb3cueG1sImApIGlzIHF1aXRlIGNvbmZ1c2luZyBmb3IgdGhlIG5ld2JpZXM6IG5vIGFjdHVhbGx5IGZsb3cgZGVzY3JpcHRpb24gKGFjY29yZGluZyB0byB3aGF0IHlvdSB3aWxsIHNlZSBpbiB0dXRvcmlhbHMpIGJ1dCBqdXN0IHR3byByZWd1bGFyIFNwcmluZyBiZWFucyBkZWZpbml0aW9uOiB0aGUgcG9pbnQgaXMgdGhhdCBhbnkgc3VjaCBiZWFuIGV4cG9zaW5nIGFuICdleGVjdXRlJyBtZXRob2Qgd2lsbCBiZSB0cmVhdGVkIGFzIGEgc3RhdGUtYWN0aW9uIGFuZCB0aGUgbWV0aG9kcyB3aWxsIGJlIGNhbGxlZCBpbiB0aGUgb3JkZXIgb2YgYmVhbiBkZWZpbml0aW9uLg0KDQpPSywgYnV0IHdoZXJlIGNvbWVzIGluIGFjdGlvbiBvdXIgYGxvZ2luLnZtYD8gVGhlIGBwYXJlbnQ9InNhbWwyLnNzby5hYnN0cmFjdCJgIGF0dHJpYnV0ZSBpbiBmbG93IHRhZyBkZWZpbml0aW9uIGlzIHRoZSBrZXk6IG91ciBmbG93IGluaGVyaXRzIGl0cyBkZWZpbml0aW9uIGZyb20gdGhhdCBmbG93IChpZGVudGlmaWVkIGJ5IHRoaXMgaWQgaW4gYHdlYi1jb25maWcueG1sYCkuIFNvbWVob3cgKEkgaGF2ZW4ndCBjaGVja2VkIGFsbCB0aGUgaW5oZXJpdGFuY2UvZGVmaW5pdGlvbnMgY2hhaW4pIHRoZSBgYXV0aG4vUGFzc3dvcmRgIHN1Yi1mbG93IGlzIGFjdGl2YXRlZCwgc3ViLWZsb3cgdGhhdCBjb250YWlucyBhIGB2aWV3LXN0YXRlYCByZWZlcnJpbmcgb3VyIHRlbXBsYXRlIGluIHRoZSB2aWV3IGF0dHJpYnV0ZToNCmBgYHhtbA0KICAgIDx2aWV3LXN0YXRlIGlkPSJEaXNwbGF5VXNlcm5hbWVQYXNzd29yZFBhZ2UiIHZpZXc9ImxvZ2luIj4NCiAgICAgICAgPG9uLXJlbmRlcj4NCiAgICAgICAgICAgIDxldmFsdWF0ZSBleHByZXNzaW9uPSJlbnZpcm9ubWVudCIgcmVzdWx0PSJ2aWV3U2NvcGUuZW52aXJvbm1lbnQiIC8+DQogICAgICAgICAgICA8ZXZhbHVhdGUgZXhwcmVzc2lvbj0ib3BlbnNhbWxQcm9maWxlUmVxdWVzdENvbnRleHQiIHJlc3VsdD0idmlld1Njb3BlLnByb2ZpbGVSZXF1ZXN0Q29udGV4dCIgLz4NCiAgICAgICAgICAgIDxldmFsdWF0ZSBleHByZXNzaW9uPSJvcGVuc2FtbFByb2ZpbGVSZXF1ZXN0Q29udGV4dC5nZXRTdWJjb250ZXh0KFQobmV0LnNoaWJib2xldGguaWRwLmF1dGhuLmNvbnRleHQuQXV0aGVudGljYXRpb25Db250ZXh0KSkiIHJlc3VsdD0idmlld1Njb3BlLmF1dGhlbnRpY2F0aW9uQ29udGV4dCIgLz4NCi4uLg0KYGBgICANClRoZSB0cmFuc2l0aW9uIGA8dHJhbnNpdGlvbiBvbj0icHJvY2VlZCIgdG89IkV4dHJhY3RVc2VybmFtZVBhc3N3b3JkRnJvbUZvcm1SZXF1ZXN0Ij5gIGlzIGFjdGl2YXRlZCB3aGVuIHRoZSBMb2dpbiBidXR0b24gaXMgcHJlc3NlZCAoYGlkPSdfZXZlbnRJZF9wcm9jZWVkJ2AtXD4gYHByb2NlZWRgIGNvbnN0YW50IHdpbGwgYmVjb21lIGF2YWlsYWJsZSB0byBiZSBldmFsdWF0ZWQgYnkgdGhlIHRyYW5zaXRpb24gY29uZGl0aW9uIGJhc2VkIG9uIHRoZSBjb252ZW50aW9uIGBpZD1fZXZlbnRJZF8uLi5gICkuDQoNCkFjdGlvbiBzdGF0ZSBgRXh0cmFjdFVzZXJuYW1lUGFzc3dvcmRGcm9tRm9ybVJlcXVlc3RgIHdpbGwgYmUgZXhlY3V0ZWQ6DQpgYGB4bWwNCiAgICA8YWN0aW9uLXN0YXRlIGlkPSJFeHRyYWN0VXNlcm5hbWVQYXNzd29yZEZyb21Gb3JtUmVxdWVzdCI+DQogICAgICAgIDxldmFsdWF0ZSBleHByZXNzaW9uPSJFeHRyYWN0VXNlcm5hbWVQYXNzd29yZEZyb21Gb3JtUmVxdWVzdCIgLz4NCiAgICAgICAgPGV2YWx1YXRlIGV4cHJlc3Npb249Iidwcm9jZWVkJyIgLz4NCiAgICANCiAgICAgICAgPCEtLSBMZXQgdGhlIHZhbGlkYXRlIGFjdGlvbiBoYW5kbGUgYW55IHByb2JsZW1zIGxhdGVyLiAtLT4gICAgICAgIA0KICAgICAgICA8dHJhbnNpdGlvbiB0bz0iVmFsaWRhdGVVc2VybmFtZVBhc3N3b3JkIiAvPg0KICAgIDwvYWN0aW9uLXN0YXRlPg0KYGBgDQpUaGUgYDxldmFsdWF0ZSBleHByZXNzaW9uPSJFeHRyYWN0VXNlcm5hbWVQYXNzd29yZEZyb21Gb3JtUmVxdWVzdCIgLz5gIGhhcyB0aGUgcm9sZSB0byBjYWxsIGBleGVjdXRlYCBtZXRob2Qgb2YgYmVhbiB3aXRoIGBpZD0iRXh0cmFjdFVzZXJuYW1lUGFzc3dvcmRGcm9tRm9ybVJlcXVlc3QiYCAoIGNsYXNzIGRlZmluZWQgaW4gbW9kdWxlIGBTaGliYm9sZXRoIElkUCA6OiBBdXRoZW50aWNhdGlvbiBJbXBsZW1lbnRhdGlvbmApIHdoaWxlIGA8ZXZhbHVhdGUgZXhwcmVzc2lvbj0iJ3Byb2NlZWQnIi8+YCBqdXN0IG1ha2VzIGAncHJvY2VlZCdgIHRoZSBjdXJyZW50IHZhbHVlIHRvIGJlIGV2YWx1YXRlZCBieSBzdWJzZXF1ZW50IHRyYW5zaXRpb25zIChub3QgYWN0dWFsbHkgdXNlZCBoZXJlIGJ1dCBwYXR0ZXJuIHVzZWQgaW4gbWF5IG90aGVyIHBsYWNlcykuDQoNCkZpbmFsbHkgd2UgbGFuZCBpbiBgVmFsaWRhdGVVc2VybmFtZVBhc3N3b3JkQWdhaW5zdExEQVAuZXhlY3V0ZWAgbWV0aG9kLg==

Friday, October 30, 2015

Shibboleth-Finally

After about two weeks of trial and error I finally succeeded to have a SSO/SAML POC based on open source components (Shibbolet/ApacheDS/KentorIT) working.

The last step was struggling with convincing Shibbolet to respond to SAML requests.

The main problem (for newbies) is the lack of any overview type information; almost all documentation (https://wiki.shibboleth.net/confluence/display/IDP30/Home) focuses on detailed specification aspects so you get lost in tens of .properties/.xml configuration files.

Debugging

Of course my first concern was about being able to set breakpoints and go step by step through sources.

Go to jetty_base and edit start.ini by adding the following lines:

-Xdebug 
-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n

Restart jetty.

Download latest source, open main pom.xml and rebuild project.

First steps

Supposing you have the metadata file for a target SP (we do not need the SP implementation for now) deploy it in shibbolet_sp/metadata (you will also find there idp_metadata.xml, the metadata related with IsP).

Edit conf/metadata-providers.xml by adding information about you SP; something like this:

  • <MetadataProvider id="LocalMetadata" xsi:type="FilesystemMetadataProvider" metadataFile="%{idp.home}/metadata/mySPmetadata.xml"/>

Edit conf/ldap.proprierties (watch out for useStartTLS):

  • idp.authn.LDAP.ldapURL = <ldap url>
  • idp.authn.LDAP.useStartTLS = false
  • idp.authn.LDAP.returnAttributes = <comma separated LDAP attributes>
  • idp.authn.LDAP.baseDN = <base DN for search)
  • idp.authn.LDAP.userFilter = (<LDAP attribute>={user})

Remark: This is enaough for default anonSearchAuthenticator; if bindSearchAuthenticator is required be sure to:

  • uncomment and change accordingly idp.authn.LDAP.authenticator = bindSearchAuthenticator
  • change the two configartion entries:
    • idp.authn.LDAP.bindDN = <DN of bind user>
    • idp.authn.LDAP.bindDNCredential = <password>

At this moment we can test authentication by using the IdP initiated login URL : HTTPS://<shibblolet-url>:<https-port>/idp/profile/SAML2/Unsolicited/SSO?providerId=<SP_entityID>

<SP_entityID> is the the SP EntityId as specified in metadata file.

Logout can be achieved through https://<shibblolet-url>:<https-port>/idp/profile/Logout

In order to debug the authentication proceeds have a look in net.shibboleth.idp.authn.impl.ValidateUsernamePasswordAgainstLDAP (method doExecute), module Shibboleth IdP :: Authentication Implementation; also look at org.ldaptive.auth.Authenticator from ladptive-1.0.0.jar (select the jar with right click->Download source, etc).

Configure Response

Now, even if you will get a browser error after going through the login UI process (since we dot have the actual SP implementation) you can use browser developer tools to check the SAMLResponse (use SSOCircle POST decoder). We notice that the Subject part is encrypted.

Response configuration can be done through conf/relying-party.xml; inside <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty"> change the SAML2.SSO attributes according to your needs:

  • (sample): <bean parent="SAML2.SSO" p:postAuthenticationFlows="attribute-release" p:encryptAssertions="false" p:encryptAttributes="false" p:encryptNameIDs="false"/>

Now we can see in clear text (after decoding) the Subject info but surprise(s):

  • NameID has a strange value (some Base64 encoded value)
  • No other assertions, even if we configured LDAP to return more attributes

NameID

I was used with ADFS where you can easily return sAMAcountName as NameID. It turned out to be a very difficult task so at the end of the day I gave up since I (somehow) understood that the role of NameID is different of what I wanted to achieve.

Some insights: You can have a look at (as a starting debugging point) getSAML2NameIDGenerator method from net.shibboleth.idp.saml.nameid.impl.NameIdentifierGenerationServiceImpl, module Shibboleth IdP :: SAML Profile Implementation. The main issue here is that the returned value (saml2Generator) is an implementation of org.opensaml.saml.saml2.profile.SAML2NameIDGenerator. Since in the module there is a compile dependency only to open-saml-api-3.1.1.jar you will have to add manually in pom.xml the dependency to the actual implementation.

        <dependency>
            <groupId>${opensaml.groupId}</groupId>
            <artifactId>opensaml-saml-impl</artifactId>
            <version>${opensaml.version}</version>
        </dependency>
 

Be sure to remove the test time dependency (a compile error will prevent to successfully compile the module anyway).

The default configured strategy relies on TransientSAML2NameIDGenerator and CryptoTransientIdGenerationStrategy (generate method).

Configuration of the strategy is done in conf/saml-nameid.xml. Initially I tried switching from <ref bean="shibboleth.SAML2TransientGenerator" /> to

    <!-- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress -->
    <bean parent="shibboleth.SAML2AttributeSourcedGenerator"
            p:format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
            p:attributeSourceIds="#{ {'cn'} }" />

Watch out of the p:format attribute value, changed from email to transient as the net.shibboleth.idp.saml.nameid.impl.AttributeSourcedSAML2NameIDGenerator (one of the subsequent calls to other classes methods) will check for attributes with this format.

Anyway, it didn't work because here we speak about SAML attributes (that will go into Assertions) not about LDAP attributes and since I didn't get in the response no Assertions...no NameID was also generated.

Finally Getting the Assertions

After further hours spent in analysing online wiki it was obvious that AttributeResolverConfiguration was next step to success.

Having a look in conf/attribute-resolver.xml and putting breakpoints in classes like from net.shibboleth.idp.attribute.resolver.ad.impl namespace like (PrincipalNameAttributeDefinition, ScopedAttributeDefinition, AbstractAttributeReleaseAction or AttributeResolverImpl) it looked that everything went Ok since I've got all 4 attributes with the expected values (by watching variables during breakpoint sessions) but no Assertions were generated.

So I had a look in conf/atribute-filter.xml and surprise, attribute generation was filtered by a fictious EntityId of https://sp.example.org. By changing the attribute value to the right value in <afp:PolicyRequirementRule xsi:type="basic:AttributeRequesterString" value="<mySPEntityID>" /> everything started to work as expected.

QWZ0ZXIgYWJvdXQgdHdvIHdlZWtzIG9mIHRyaWFsIGFuZCBlcnJvciBJIGZpbmFsbHkgc3VjY2VlZGVkIHRvIGhhdmUgYSBTU08vU0FNTCBQT0MgYmFzZWQgb24gb3BlbiBzb3VyY2UgY29tcG9uZW50cyAoU2hpYmJvbGV0L0FwYWNoZURTL0tlbnRvcklUKSB3b3JraW5nLiANCg0KVGhlIGxhc3Qgc3RlcCB3YXMgc3RydWdnbGluZyB3aXRoIGNvbnZpbmNpbmcgU2hpYmJvbGV0IHRvIHJlc3BvbmQgdG8gU0FNTCByZXF1ZXN0cy4NCg0KVGhlIG1haW4gcHJvYmxlbSAoZm9yIG5ld2JpZXMpIGlzIHRoZSBsYWNrIG9mIGFueSBvdmVydmlldyB0eXBlIGluZm9ybWF0aW9uOyBhbG1vc3QgYWxsIGRvY3VtZW50YXRpb24gKFtodHRwczovL3dpa2kuc2hpYmJvbGV0aC5uZXQvY29uZmx1ZW5jZS9kaXNwbGF5L0lEUDMwL0hvbWVdKGh0dHBzOi8vd2lraS5zaGliYm9sZXRoLm5ldC9jb25mbHVlbmNlL2Rpc3BsYXkvSURQMzAvSG9tZSkpIGZvY3VzZXMgb24gZGV0YWlsZWQgc3BlY2lmaWNhdGlvbiBhc3BlY3RzIHNvIHlvdSBnZXQgbG9zdCBpbiB0ZW5zIG9mIC5wcm9wZXJ0aWVzLy54bWwgY29uZmlndXJhdGlvbiBmaWxlcy4NCg0KIyBEZWJ1Z2dpbmcNCg0KT2YgY291cnNlIG15IGZpcnN0IGNvbmNlcm4gd2FzIGFib3V0IGJlaW5nIGFibGUgdG8gc2V0IGJyZWFrcG9pbnRzIGFuZCBnbyBzdGVwIGJ5IHN0ZXAgdGhyb3VnaCBzb3VyY2VzLg0KDQpHbyB0byBqZXR0eV9iYXNlIGFuZCBlZGl0IHN0YXJ0LmluaSBieSBhZGRpbmcgdGhlIGZvbGxvd2luZyBsaW5lczoNCg0KYGBgDQotWGRlYnVnIA0KLVhydW5qZHdwOnRyYW5zcG9ydD1kdF9zb2NrZXQsYWRkcmVzcz01MDA1LHNlcnZlcj15LHN1c3BlbmQ9bg0KYGBgDQoNClJlc3RhcnQgamV0dHkuDQoNCkRvd25sb2FkIGxhdGVzdCBzb3VyY2UsIG9wZW4gbWFpbiBwb20ueG1sIGFuZCByZWJ1aWxkIHByb2plY3QuDQoNCiMgRmlyc3Qgc3RlcHMNCg0KU3VwcG9zaW5nIHlvdSBoYXZlIHRoZSBtZXRhZGF0YSBmaWxlIGZvciBhIHRhcmdldCBTUCAod2UgZG8gbm90IG5lZWQgdGhlIFNQIGltcGxlbWVudGF0aW9uIGZvciBub3cpIGRlcGxveSBpdCBpbiBgc2hpYmJvbGV0X3NwL21ldGFkYXRhYCAoeW91IHdpbGwgYWxzbyBmaW5kIHRoZXJlIGlkcF9tZXRhZGF0YS54bWwsIHRoZSBtZXRhZGF0YSByZWxhdGVkIHdpdGggSXNQKS4NCg0KRWRpdCBgY29uZi9tZXRhZGF0YS1wcm92aWRlcnMueG1sYCBieSBhZGRpbmcgaW5mb3JtYXRpb24gYWJvdXQgeW91IFNQOyBzb21ldGhpbmcgbGlrZSB0aGlzOg0KKiAgICBgPE1ldGFkYXRhUHJvdmlkZXIgaWQ9IkxvY2FsTWV0YWRhdGEiICB4c2k6dHlwZT0iRmlsZXN5c3RlbU1ldGFkYXRhUHJvdmlkZXIiIG1ldGFkYXRhRmlsZT0iJXtpZHAuaG9tZX0vbWV0YWRhdGEvbXlTUG1ldGFkYXRhLnhtbCIvPmANCg0KDQpFZGl0IGNvbmYvbGRhcC5wcm9wcmllcnRpZXMgKHdhdGNoIG91dCBmb3IgdXNlU3RhcnRUTFMpOg0KDQoqIGlkcC5hdXRobi5MREFQLmxkYXBVUkwJCQkJPSA8bGRhcCB1cmxcPg0KKiBpZHAuYXV0aG4uTERBUC51c2VTdGFydFRMUyAgICAJCT0gZmFsc2UNCiogaWRwLmF1dGhuLkxEQVAucmV0dXJuQXR0cmlidXRlcyAgIAk9IFw8Y29tbWEgc2VwYXJhdGVkIExEQVAgYXR0cmlidXRlc1w+DQoqIGlkcC5hdXRobi5MREFQLmJhc2VETgkJCQkJPSA8YmFzZSBETiBmb3Igc2VhcmNoKQ0KKiBpZHAuYXV0aG4uTERBUC51c2VyRmlsdGVyICAgICAgICAgICAgID0gKFw8TERBUCBhdHRyaWJ1dGVcPj17dXNlcn0pDQoNCioqUmVtYXJrKio6IFRoaXMgaXMgZW5hb3VnaCBmb3IgZGVmYXVsdCAqYW5vblNlYXJjaEF1dGhlbnRpY2F0b3IqOyBpZiAqYmluZFNlYXJjaEF1dGhlbnRpY2F0b3IqIGlzIHJlcXVpcmVkIGJlIHN1cmUgdG86DQoNCiogdW5jb21tZW50IGFuZCBjaGFuZ2UgYWNjb3JkaW5nbHkgYGlkcC5hdXRobi5MREFQLmF1dGhlbnRpY2F0b3IgPSBiaW5kU2VhcmNoQXV0aGVudGljYXRvcmANCiogY2hhbmdlIHRoZSB0d28gY29uZmlnYXJ0aW9uIGVudHJpZXM6DQoJKiBpZHAuYXV0aG4uTERBUC5iaW5kRE4gICAgICAgICAgICAgICAgICAgICAgICAgICA9IFw8RE4gb2YgYmluZCB1c2VyXD4NCgkqIGlkcC5hdXRobi5MREFQLmJpbmRETkNyZWRlbnRpYWwgICAgICAgICAgICAgICAgID0gXDxwYXNzd29yZFw+DQoNCg0KDQpBdCB0aGlzIG1vbWVudCB3ZSBjYW4gdGVzdCBhdXRoZW50aWNhdGlvbiBieSB1c2luZyB0aGUgSWRQIGluaXRpYXRlZCBsb2dpbiBVUkwgOiBgSFRUUFM6Ly88c2hpYmJsb2xldC11cmw+OjxodHRwcy1wb3J0Pi9pZHAvcHJvZmlsZS9TQU1MMi9VbnNvbGljaXRlZC9TU08/cHJvdmlkZXJJZD08U1BfZW50aXR5SUQ+YA0KDQo8U1BfZW50aXR5SUQ+IGlzIHRoZSB0aGUgU1AgRW50aXR5SWQgYXMgc3BlY2lmaWVkIGluIG1ldGFkYXRhIGZpbGUuDQoNCkxvZ291dCBjYW4gYmUgYWNoaWV2ZWQgdGhyb3VnaCBgaHR0cHM6Ly88c2hpYmJsb2xldC11cmw+OjxodHRwcy1wb3J0Pi9pZHAvcHJvZmlsZS9Mb2dvdXRgDQoNCkluIG9yZGVyIHRvIGRlYnVnIHRoZSBhdXRoZW50aWNhdGlvbiBwcm9jZWVkcyBoYXZlIGEgbG9vayBpbiBgbmV0LnNoaWJib2xldGguaWRwLmF1dGhuLmltcGwuVmFsaWRhdGVVc2VybmFtZVBhc3N3b3JkQWdhaW5zdExEQVBgIChtZXRob2QgZG9FeGVjdXRlKSwgbW9kdWxlIGBTaGliYm9sZXRoIElkUCA6OiBBdXRoZW50aWNhdGlvbiBJbXBsZW1lbnRhdGlvbmA7IGFsc28gbG9vayBhdCBgb3JnLmxkYXB0aXZlLmF1dGguQXV0aGVudGljYXRvcmAgZnJvbSBsYWRwdGl2ZS0xLjAuMC5qYXIgKHNlbGVjdCB0aGUgamFyIHdpdGggcmlnaHQgY2xpY2stXD5Eb3dubG9hZCBzb3VyY2UsIGV0YykuDQoNCiMgQ29uZmlndXJlIFJlc3BvbnNlDQoNCk5vdywgZXZlbiBpZiB5b3Ugd2lsbCBnZXQgYSBicm93c2VyIGVycm9yIGFmdGVyIGdvaW5nIHRocm91Z2ggdGhlIGxvZ2luIFVJIHByb2Nlc3MgKHNpbmNlIHdlIGRvdCBoYXZlIHRoZSBhY3R1YWwgU1AgaW1wbGVtZW50YXRpb24pIHlvdSBjYW4gdXNlIGJyb3dzZXIgZGV2ZWxvcGVyIHRvb2xzIHRvIGNoZWNrIHRoZSBTQU1MUmVzcG9uc2UgKHVzZSBTU09DaXJjbGUgUE9TVCBkZWNvZGVyKS4gV2Ugbm90aWNlIHRoYXQgdGhlIFN1YmplY3QgcGFydCBpcyBlbmNyeXB0ZWQuDQoNClJlc3BvbnNlIGNvbmZpZ3VyYXRpb24gY2FuIGJlIGRvbmUgdGhyb3VnaCAgY29uZi9yZWx5aW5nLXBhcnR5LnhtbDsgaW5zaWRlIGAgPGJlYW4gaWQ9InNoaWJib2xldGguRGVmYXVsdFJlbHlpbmdQYXJ0eSIgcGFyZW50PSJSZWx5aW5nUGFydHkiPmAgY2hhbmdlIHRoZSBTQU1MMi5TU08gYXR0cmlidXRlcyBhY2NvcmRpbmcgdG8geW91ciBuZWVkczoNCg0KKiAoc2FtcGxlKTogYDxiZWFuIHBhcmVudD0iU0FNTDIuU1NPIiBwOnBvc3RBdXRoZW50aWNhdGlvbkZsb3dzPSJhdHRyaWJ1dGUtcmVsZWFzZSIgcDplbmNyeXB0QXNzZXJ0aW9ucz0iZmFsc2UiIHA6ZW5jcnlwdEF0dHJpYnV0ZXM9ImZhbHNlIiBwOmVuY3J5cHROYW1lSURzPSJmYWxzZSIvPmANCiANCk5vdyB3ZSBjYW4gc2VlIGluIGNsZWFyIHRleHQgKGFmdGVyIGRlY29kaW5nKSB0aGUgU3ViamVjdCBpbmZvIGJ1dCBzdXJwcmlzZShzKToNCg0KKiBOYW1lSUQgaGFzIGEgc3RyYW5nZSB2YWx1ZSAoc29tZSBCYXNlNjQgZW5jb2RlZCB2YWx1ZSkNCiogTm8gb3RoZXIgYXNzZXJ0aW9ucywgZXZlbiBpZiB3ZSBjb25maWd1cmVkIExEQVAgdG8gcmV0dXJuIG1vcmUgYXR0cmlidXRlcw0KDQojIE5hbWVJRA0KDQpJIHdhcyB1c2VkIHdpdGggQURGUyB3aGVyZSB5b3UgY2FuIGVhc2lseSByZXR1cm4gc0FNQWNvdW50TmFtZSBhcyBOYW1lSUQuIEl0IHR1cm5lZCBvdXQgdG8gYmUgYSB2ZXJ5IGRpZmZpY3VsdCB0YXNrIHNvIGF0IHRoZSBlbmQgb2YgdGhlIGRheSBJIGdhdmUgdXAgc2luY2UgSSAoc29tZWhvdykgdW5kZXJzdG9vZCB0aGF0IHRoZSByb2xlIG9mIE5hbWVJRCBpcyBkaWZmZXJlbnQgb2Ygd2hhdCBJIHdhbnRlZCB0byBhY2hpZXZlLg0KDQpTb21lIGluc2lnaHRzOg0KWW91IGNhbiBoYXZlIGEgbG9vayBhdCAoYXMgYSBzdGFydGluZyBkZWJ1Z2dpbmcgcG9pbnQpIGBnZXRTQU1MMk5hbWVJREdlbmVyYXRvcmAgbWV0aG9kIGZyb20gYG5ldC5zaGliYm9sZXRoLmlkcC5zYW1sLm5hbWVpZC5pbXBsLk5hbWVJZGVudGlmaWVyR2VuZXJhdGlvblNlcnZpY2VJbXBsYCwgbW9kdWxlIGBTaGliYm9sZXRoIElkUCA6OiBTQU1MIFByb2ZpbGUgSW1wbGVtZW50YXRpb25gLiBUaGUgbWFpbiBpc3N1ZSBoZXJlIGlzIHRoYXQgdGhlIHJldHVybmVkIHZhbHVlIChzYW1sMkdlbmVyYXRvcikgaXMgYW4gaW1wbGVtZW50YXRpb24gb2YgYG9yZy5vcGVuc2FtbC5zYW1sLnNhbWwyLnByb2ZpbGUuU0FNTDJOYW1lSURHZW5lcmF0b3JgLiBTaW5jZSBpbiB0aGUgbW9kdWxlIHRoZXJlIGlzIGEgY29tcGlsZSBkZXBlbmRlbmN5IG9ubHkgdG8gYG9wZW4tc2FtbC1hcGktMy4xLjEuamFyYCB5b3Ugd2lsbCBoYXZlIHRvIGFkZCBtYW51YWxseSBpbiBwb20ueG1sIHRoZSBkZXBlbmRlbmN5IHRvIHRoZSBhY3R1YWwgaW1wbGVtZW50YXRpb24uDQoNCmBgYHhtbA0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPiR7b3BlbnNhbWwuZ3JvdXBJZH08L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5vcGVuc2FtbC1zYW1sLWltcGw8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4ke29wZW5zYW1sLnZlcnNpb259PC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogDQpgYGANCg0KQmUgc3VyZSB0byByZW1vdmUgdGhlIHRlc3QgdGltZSBkZXBlbmRlbmN5IChhIGNvbXBpbGUgZXJyb3Igd2lsbCBwcmV2ZW50IHRvIHN1Y2Nlc3NmdWxseSBjb21waWxlIHRoZSBtb2R1bGUgYW55d2F5KS4NCg0KVGhlIGRlZmF1bHQgY29uZmlndXJlZCBzdHJhdGVneSByZWxpZXMgb24gYFRyYW5zaWVudFNBTUwyTmFtZUlER2VuZXJhdG9yYCBhbmQgYENyeXB0b1RyYW5zaWVudElkR2VuZXJhdGlvblN0cmF0ZWd5YCAoYGdlbmVyYXRlYCBtZXRob2QpLg0KDQpDb25maWd1cmF0aW9uIG9mIHRoZSBzdHJhdGVneSBpcyBkb25lIGluIGBjb25mL3NhbWwtbmFtZWlkLnhtbGAuIEluaXRpYWxseSBJIHRyaWVkIHN3aXRjaGluZyBmcm9tIGA8cmVmIGJlYW49InNoaWJib2xldGguU0FNTDJUcmFuc2llbnRHZW5lcmF0b3IiIC8+YCB0byANCmBgYA0KCTwhLS0gdXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIC0tPg0KCTxiZWFuIHBhcmVudD0ic2hpYmJvbGV0aC5TQU1MMkF0dHJpYnV0ZVNvdXJjZWRHZW5lcmF0b3IiDQogICAgICAgICAgICBwOmZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50Ig0KICAgICAgICAgICAgcDphdHRyaWJ1dGVTb3VyY2VJZHM9IiN7IHsnY24nfSB9IiAvPg0KDQpgYGANCldhdGNoIG91dCBvZiB0aGUgYHA6Zm9ybWF0YCBhdHRyaWJ1dGUgdmFsdWUsIGNoYW5nZWQgZnJvbSAqZW1haWwqIHRvICp0cmFuc2llbnQqIGFzIHRoZSBgbmV0LnNoaWJib2xldGguaWRwLnNhbWwubmFtZWlkLmltcGwuQXR0cmlidXRlU291cmNlZFNBTUwyTmFtZUlER2VuZXJhdG9yYCAob25lIG9mIHRoZSBzdWJzZXF1ZW50IGNhbGxzIHRvIG90aGVyIGNsYXNzZXMgbWV0aG9kcykgd2lsbCBjaGVjayAgZm9yIGF0dHJpYnV0ZXMgd2l0aCB0aGlzIGZvcm1hdC4NCg0KQW55d2F5LCBpdCBkaWRuJ3Qgd29yayBiZWNhdXNlIGhlcmUgd2Ugc3BlYWsgYWJvdXQgU0FNTCBhdHRyaWJ1dGVzICh0aGF0IHdpbGwgZ28gaW50byBBc3NlcnRpb25zKSAgbm90IGFib3V0IExEQVAgYXR0cmlidXRlcyBhbmQgc2luY2UgSSBkaWRuJ3QgZ2V0IGluIHRoZSByZXNwb25zZSBubyBBc3NlcnRpb25zLi4ubm8gTmFtZUlEIHdhcyBhbHNvIGdlbmVyYXRlZC4NCg0KIyBGaW5hbGx5IEdldHRpbmcgdGhlIEFzc2VydGlvbnMNCg0KQWZ0ZXIgZnVydGhlciBob3VycyBzcGVudCBpbiBhbmFseXNpbmcgb25saW5lIHdpa2kgaXQgd2FzIG9idmlvdXMgdGhhdCAgW0F0dHJpYnV0ZVJlc29sdmVyQ29uZmlndXJhdGlvbl0oaHR0cHM6Ly93aWtpLnNoaWJib2xldGgubmV0L2NvbmZsdWVuY2UvZGlzcGxheS9JRFAzMC9BdHRyaWJ1dGVSZXNvbHZlckNvbmZpZ3VyYXRpb24pIHdhcyBuZXh0IHN0ZXAgdG8gc3VjY2Vzcy4NCg0KSGF2aW5nIGEgbG9vayBpbiBgY29uZi9hdHRyaWJ1dGUtcmVzb2x2ZXIueG1sYCBhbmQgcHV0dGluZyBicmVha3BvaW50cyBpbiBjbGFzc2VzIGxpa2UgZnJvbSBgbmV0LnNoaWJib2xldGguaWRwLmF0dHJpYnV0ZS5yZXNvbHZlci5hZC5pbXBsYCBuYW1lc3BhY2UgbGlrZSAoYFByaW5jaXBhbE5hbWVBdHRyaWJ1dGVEZWZpbml0aW9uYCwgYFNjb3BlZEF0dHJpYnV0ZURlZmluaXRpb25gLCBgQWJzdHJhY3RBdHRyaWJ1dGVSZWxlYXNlQWN0aW9uYCBvciBgQXR0cmlidXRlUmVzb2x2ZXJJbXBsYCkgaXQgbG9va2VkIHRoYXQgZXZlcnl0aGluZyB3ZW50IE9rIHNpbmNlIEkndmUgZ290IGFsbCA0IGF0dHJpYnV0ZXMgd2l0aCB0aGUgZXhwZWN0ZWQgdmFsdWVzIChieSB3YXRjaGluZyB2YXJpYWJsZXMgZHVyaW5nIGJyZWFrcG9pbnQgc2Vzc2lvbnMpIGJ1dCBubyBBc3NlcnRpb25zIHdlcmUgZ2VuZXJhdGVkLg0KDQpTbyBJIGhhZCBhIGxvb2sgaW4gYGNvbmYvYXRyaWJ1dGUtZmlsdGVyLnhtbGAgYW5kIHN1cnByaXNlLCBhdHRyaWJ1dGUgZ2VuZXJhdGlvbiB3YXMgZmlsdGVyZWQgYnkgYSBmaWN0aW91cyBFbnRpdHlJZCBvZiAqaHR0cHM6Ly9zcC5leGFtcGxlLm9yZyouIEJ5IGNoYW5naW5nIHRoZSBhdHRyaWJ1dGUgKnZhbHVlKiB0byB0aGUgcmlnaHQgdmFsdWUgaW4gYDxhZnA6UG9saWN5UmVxdWlyZW1lbnRSdWxlIHhzaTp0eXBlPSJiYXNpYzpBdHRyaWJ1dGVSZXF1ZXN0ZXJTdHJpbmciIHZhbHVlPSI8bXlTUEVudGl0eUlEPiIgLz5gIGV2ZXJ5dGhpbmcgc3RhcnRlZCB0byB3b3JrIGFzIGV4cGVjdGVkLg0K

Wednesday, October 28, 2015

Shibboleth-Basic Installation

Jetty Base

As a prerequisite be sure you understand the Jetty Base concept: a separate directory structure containing just configuration info. In order to implement it you have to add JETTY_BASE environment variable to /etc/default/jetty or to /etc/environment pointing to the above mentioned directory.

Install Steps

Just things that went wrong during my installation (or worth mentioning) are captured here.

Run initial steps as described in general instructions section https://wiki.shibboleth.net/confluence/display/IDP30/Installation#Installation-Non-WindowsInstallation.

Go now to the container specific instructions (https://wiki.shibboleth.net/confluence/display/IDP30/Jetty93) in my case.

Create the JETTY_BASE directory structure as mentioned at and apply step by step the instructions. As I noticed afterwards, there is already a jetty_base directory in the unpacked structure (which is not copied to the target directory by the setup script). Some of the artefacts can be copied from this directory (like jars) but be aware that the content of configuration files is not exactly as what the instructions require. Delete for now all the optional files.

Configure Jetty Modules and JVM Settings

For start.ini:

  • Uncomment and change the line #-Didp.home=/path/to/shibboleth-idp to -Didp.home=/opt/shibboleth-idp even if the comment suggest to let it commented!!!
  • Add the following lines to the end of the file:
    • -Djava.io.tmpdir=tmp
    • -Dorg.eclipse.jetty.LEVEL=DEBUG (so that we have logging info for Jetty start-up)

Configure HTTP Connectors

Be sure that you deploy (and change the name and the password accordingly) a valid server side certificate as configured by the lines added in ssl.ini:

  • jetty.sslContext.keyStorePath=/opt/shibboleth-idp/credentials/idp-browser.p12
  • jetty.sslContext.keyStoreType=PKCS12
  • jetty.sslContext.keyStorePassword=thepasswordgoeshere

So far I didn't do any other configuration (neither logging nor SOAP sections).

Before trying to stop (Jetty is automatically launched by OS if configured as depicted in post used in previous blog) and start be sure you make jetty owner on all files in /opt/shibboleth-idp and opt/jetty directories, otherwise it will not be able to read idp.war or SSL certificate, for instance. Run sudo chown -R jetty:jetty /opt/shibbolet-idp or chmod -R a+r /opt/shibbolet-idp After (successful or not) start have a look in logging directories of jetty_base and shibbolet-idp.

You could also use java -jar opt/jetty//start.jar -DDEBUG=true -Dorg.eclipse.jetty.LEVEL=DEBUG jetty.port=8085 (from jetty_base!; don't forget to check if /etc/environment contains the proper definition for JAVA_HOME).

Other helpful commands:

  • service jetty status -l
  • netstat -tulpn ;use this command to see what processes keep open TCP ports(sometimes Jetty service does not stop/start properly and a java process remains hanged keeping opened ports for HTTP and HTTPS which prevents restarting the service)

jetty Default Environment Variables

I used the following /etc/defaults/jetty file:

JETTY_HOME=/opt/jetty
JETTY_BASE=/opt/shibboleth-idp/jetty-base
NO_START=0
JETTY_HOST=0.0.0.0
JETTY_USER=jetty
  • For a complete list of environment options have a look in /etc/init.d/jetty
  • I suppose most of the options can be moved in start.ini (apart from JETTY_HOME, JETTY_BASE and JETTY_USER, of course) or use specific Jetty configuration options from jetty_base/etc/*.ini (like jetty.ssl.host from ssl.ini)
IyBKZXR0eSBCYXNlDQoNCkFzIGEgcHJlcmVxdWlzaXRlIGJlIHN1cmUgeW91IHVuZGVyc3RhbmQgdGhlIEpldHR5IEJhc2UgY29uY2VwdDogYSBzZXBhcmF0ZSBkaXJlY3Rvcnkgc3RydWN0dXJlIGNvbnRhaW5pbmcganVzdCBjb25maWd1cmF0aW9uIGluZm8uIEluIG9yZGVyIHRvIGltcGxlbWVudCBpdCB5b3UgaGF2ZSB0byBhZGQgSkVUVFlfQkFTRSBlbnZpcm9ubWVudCB2YXJpYWJsZSB0byAvZXRjL2RlZmF1bHQvamV0dHkgb3IgdG8gL2V0Yy9lbnZpcm9ubWVudCBwb2ludGluZyB0byB0aGUgYWJvdmUgbWVudGlvbmVkIGRpcmVjdG9yeS4NCg0KIyBJbnN0YWxsIFN0ZXBzDQoNCkp1c3QgdGhpbmdzIHRoYXQgd2VudCB3cm9uZyBkdXJpbmcgbXkgaW5zdGFsbGF0aW9uIChvciB3b3J0aCBtZW50aW9uaW5nKSBhcmUgY2FwdHVyZWQgaGVyZS4NCg0KUnVuIGluaXRpYWwgc3RlcHMgYXMgZGVzY3JpYmVkIGluIGdlbmVyYWwgaW5zdHJ1Y3Rpb25zIHNlY3Rpb24gW2h0dHBzOi8vd2lraS5zaGliYm9sZXRoLm5ldC9jb25mbHVlbmNlL2Rpc3BsYXkvSURQMzAvSW5zdGFsbGF0aW9uI0luc3RhbGxhdGlvbi1Ob24tV2luZG93c0luc3RhbGxhdGlvbl0oaHR0cHM6Ly93aWtpLnNoaWJib2xldGgubmV0L2NvbmZsdWVuY2UvZGlzcGxheS9JRFAzMC9JbnN0YWxsYXRpb24jSW5zdGFsbGF0aW9uLU5vbi1XaW5kb3dzSW5zdGFsbGF0aW9uKS4NCg0KR28gbm93IHRvIHRoZSBjb250YWluZXIgc3BlY2lmaWMgaW5zdHJ1Y3Rpb25zIChbaHR0cHM6Ly93aWtpLnNoaWJib2xldGgubmV0L2NvbmZsdWVuY2UvZGlzcGxheS9JRFAzMC9KZXR0eTkzXShodHRwczovL3dpa2kuc2hpYmJvbGV0aC5uZXQvY29uZmx1ZW5jZS9kaXNwbGF5L0lEUDMwL0pldHR5OTMpKSBpbiBteSBjYXNlLg0KDQpDcmVhdGUgdGhlIEpFVFRZX0JBU0UgZGlyZWN0b3J5IHN0cnVjdHVyZSBhcyBtZW50aW9uZWQgYXQgIGFuZCBhcHBseSBzdGVwIGJ5IHN0ZXAgdGhlIGluc3RydWN0aW9ucy4gQXMgSSBub3RpY2VkIGFmdGVyd2FyZHMsIHRoZXJlIGlzIGFscmVhZHkgYSBqZXR0eV9iYXNlIGRpcmVjdG9yeSBpbiB0aGUgdW5wYWNrZWQgc3RydWN0dXJlICh3aGljaCBpcyBub3QgY29waWVkIHRvIHRoZSB0YXJnZXQgZGlyZWN0b3J5IGJ5IHRoZSBzZXR1cCBzY3JpcHQpLiBTb21lIG9mIHRoZSBhcnRlZmFjdHMgY2FuIGJlIGNvcGllZCBmcm9tIHRoaXMgZGlyZWN0b3J5IChsaWtlIGphcnMpIGJ1dCBiZSBhd2FyZSB0aGF0IHRoZSBjb250ZW50IG9mIGNvbmZpZ3VyYXRpb24gZmlsZXMgaXMgbm90IGV4YWN0bHkgYXMgd2hhdCB0aGUgaW5zdHJ1Y3Rpb25zIHJlcXVpcmUuICoqRGVsZXRlIGZvciBub3cgYWxsIHRoZSBvcHRpb25hbCBmaWxlcy4qKg0KDQoNCiMjIENvbmZpZ3VyZSBKZXR0eSBNb2R1bGVzIGFuZCBKVk0gU2V0dGluZ3MNCg0KRm9yIGBzdGFydC5pbmlgOg0KDQoqIFVuY29tbWVudCBhbmQgY2hhbmdlIHRoZSBsaW5lIGAjLURpZHAuaG9tZT0vcGF0aC90by9zaGliYm9sZXRoLWlkcGAgdG8gYC1EaWRwLmhvbWU9L29wdC9zaGliYm9sZXRoLWlkcGAgZXZlbiBpZiB0aGUgY29tbWVudCBzdWdnZXN0IHRvIGxldCBpdCBjb21tZW50ZWQhISENCiogQWRkIHRoZSBmb2xsb3dpbmcgbGluZXMgdG8gdGhlIGVuZCBvZiB0aGUgZmlsZTogDQoJKiBgLURqYXZhLmlvLnRtcGRpcj10bXBgDQoJKiBgLURvcmcuZWNsaXBzZS5qZXR0eS5MRVZFTD1ERUJVR2AgKHNvIHRoYXQgd2UgaGF2ZSBsb2dnaW5nIGluZm8gZm9yIEpldHR5IHN0YXJ0LXVwKQ0KDQojIyBDb25maWd1cmUgSFRUUCBDb25uZWN0b3JzDQoNCkJlIHN1cmUgdGhhdCB5b3UgZGVwbG95IChhbmQgY2hhbmdlIHRoZSBuYW1lIGFuZCB0aGUgcGFzc3dvcmQgYWNjb3JkaW5nbHkpIGEgdmFsaWQgc2VydmVyIHNpZGUgY2VydGlmaWNhdGUgYXMgY29uZmlndXJlZCBieSB0aGUgbGluZXMgYWRkZWQgaW4gc3NsLmluaToNCg0KKiBqZXR0eS5zc2xDb250ZXh0LmtleVN0b3JlUGF0aD0vb3B0L3NoaWJib2xldGgtaWRwL2NyZWRlbnRpYWxzLyoqaWRwLWJyb3dzZXIucDEyKioNCiogamV0dHkuc3NsQ29udGV4dC5rZXlTdG9yZVR5cGU9UEtDUzEyDQoqIGpldHR5LnNzbENvbnRleHQua2V5U3RvcmVQYXNzd29yZD0qKnRoZXBhc3N3b3JkZ29lc2hlcmUqKg0KDQoNClNvIGZhciBJIGRpZG4ndCBkbyBhbnkgb3RoZXIgY29uZmlndXJhdGlvbiAobmVpdGhlciBsb2dnaW5nIG5vciBTT0FQIHNlY3Rpb25zKS4NCg0KQmVmb3JlIHRyeWluZyB0byBzdG9wIChKZXR0eSBpcyBhdXRvbWF0aWNhbGx5IGxhdW5jaGVkIGJ5IE9TIGlmIGNvbmZpZ3VyZWQgYXMgZGVwaWN0ZWQgaW4gcG9zdCB1c2VkIGluIHByZXZpb3VzIGJsb2cpIGFuZCBzdGFydCBiZSBzdXJlIHlvdSBtYWtlIGpldHR5IG93bmVyIG9uIGFsbCBmaWxlcyBpbiBgL29wdC9zaGliYm9sZXRoLWlkcGAgYW5kIGBvcHQvamV0dHlgIGRpcmVjdG9yaWVzLCBvdGhlcndpc2UgaXQgd2lsbCBub3QgYmUgYWJsZSB0byByZWFkIGlkcC53YXIgb3IgU1NMIGNlcnRpZmljYXRlLCBmb3IgaW5zdGFuY2UuIFJ1biBgc3VkbyBjaG93biAtUiBqZXR0eTpqZXR0eSAvb3B0L3NoaWJib2xldC1pZHBgIG9yIGBjaG1vZCAtUiBhK3IgL29wdC9zaGliYm9sZXQtaWRwYA0KQWZ0ZXIgKHN1Y2Nlc3NmdWwgb3Igbm90KSBzdGFydCBoYXZlIGEgbG9vayBpbiBsb2dnaW5nIGRpcmVjdG9yaWVzIG9mIGpldHR5X2Jhc2UgYW5kIHNoaWJib2xldC1pZHAuDQoNCllvdSBjb3VsZCBhbHNvIHVzZSBgamF2YSAtamFyIG9wdC9qZXR0eS8vc3RhcnQuamFyICAtRERFQlVHPXRydWUgLURvcmcuZWNsaXBzZS5qZXR0eS5MRVZFTD1ERUJVRyBqZXR0eS5wb3J0PTgwODVgIChmcm9tIGpldHR5X2Jhc2UhOyBkb24ndCBmb3JnZXQgdG8gY2hlY2sgaWYgYC9ldGMvZW52aXJvbm1lbnRgIGNvbnRhaW5zIHRoZSBwcm9wZXIgZGVmaW5pdGlvbiBmb3IgSkFWQV9IT01FKS4NCg0KT3RoZXIgaGVscGZ1bCBjb21tYW5kczoNCg0KKiBgc2VydmljZSBqZXR0eSBzdGF0dXMgLWxgDQoqIGBuZXRzdGF0IC10dWxwbmAgO3VzZSB0aGlzIGNvbW1hbmQgdG8gc2VlIHdoYXQgcHJvY2Vzc2VzIGtlZXAgb3BlbiBUQ1AgcG9ydHMoc29tZXRpbWVzIEpldHR5IHNlcnZpY2UgZG9lcyBub3Qgc3RvcC9zdGFydCBwcm9wZXJseSBhbmQgYSBqYXZhIHByb2Nlc3MgcmVtYWlucyBoYW5nZWQga2VlcGluZyBvcGVuZWQgcG9ydHMgZm9yIEhUVFAgYW5kIEhUVFBTIHdoaWNoIHByZXZlbnRzIHJlc3RhcnRpbmcgdGhlIHNlcnZpY2UpDQoNCiMgamV0dHkgRGVmYXVsdCBFbnZpcm9ubWVudCBWYXJpYWJsZXMNCg0KSSB1c2VkIHRoZSBmb2xsb3dpbmcgYC9ldGMvZGVmYXVsdHMvamV0dHlgIGZpbGU6DQoNCmBgYA0KSkVUVFlfSE9NRT0vb3B0L2pldHR5DQpKRVRUWV9CQVNFPS9vcHQvc2hpYmJvbGV0aC1pZHAvamV0dHktYmFzZQ0KTk9fU1RBUlQ9MA0KSkVUVFlfSE9TVD0wLjAuMC4wDQpKRVRUWV9VU0VSPWpldHR5DQpgYGANCg0KKiBGb3IgYSBjb21wbGV0ZSBsaXN0IG9mIGVudmlyb25tZW50IG9wdGlvbnMgaGF2ZSBhIGxvb2sgaW4gYC9ldGMvaW5pdC5kL2pldHR5YA0KKiBJIHN1cHBvc2UgbW9zdCBvZiB0aGUgb3B0aW9ucyBjYW4gYmUgbW92ZWQgaW4gYHN0YXJ0LmluaWAgKGFwYXJ0IGZyb20gSkVUVFlfSE9NRSwgSkVUVFlfQkFTRSBhbmQgSkVUVFlfVVNFUiwgb2YgY291cnNlKSBvciB1c2Ugc3BlY2lmaWMgSmV0dHkgY29uZmlndXJhdGlvbiBvcHRpb25zIGZyb20gamV0dHlfYmFzZS9ldGMvKi5pbmkgKGxpa2UgYGpldHR5LnNzbC5ob3N0YCBmcm9tIGBzc2wuaW5pYCkNCg==

Shibboleth-Jetty Installation

I decided to use the following supporting software infrastructure:

  • Ubuntu Server 15.10
  • Jetty 9.3

Installing Jetty

I used this post as a reference: http://www.ubuntugeek.com/install-jetty-9-java-servlet-engine-and-webserver-on-ubuntu-15-04-server.html

Please notice that Shibboleth 3 installation instructions recommend usage of Oracle JDK 8 as opposed to OpenJDK so install this one if your Linux distribution does not already has it installed.

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer

Be sure to set in /etc/environment JAVA_HOME=/usr/bin/java

SSBkZWNpZGVkIHRvIHVzZSB0aGUgZm9sbG93aW5nIHN1cHBvcnRpbmcgc29mdHdhcmUgaW5mcmFzdHJ1Y3R1cmU6DQoNCiogVWJ1bnR1IFNlcnZlciAxNS4xMA0KKiBKZXR0eSA5LjMNCg0KIyBJbnN0YWxsaW5nIEpldHR5DQoNCkkgdXNlZCB0aGlzIHBvc3QgYXMgYSByZWZlcmVuY2U6IFtodHRwOi8vd3d3LnVidW50dWdlZWsuY29tL2luc3RhbGwtamV0dHktOS1qYXZhLXNlcnZsZXQtZW5naW5lLWFuZC13ZWJzZXJ2ZXItb24tdWJ1bnR1LTE1LTA0LXNlcnZlci5odG1sXShodHRwOi8vd3d3LnVidW50dWdlZWsuY29tL2luc3RhbGwtamV0dHktOS1qYXZhLXNlcnZsZXQtZW5naW5lLWFuZC13ZWJzZXJ2ZXItb24tdWJ1bnR1LTE1LTA0LXNlcnZlci5odG1sKQ0KDQpQbGVhc2Ugbm90aWNlIHRoYXQgU2hpYmJvbGV0aCAzIGluc3RhbGxhdGlvbiBpbnN0cnVjdGlvbnMgcmVjb21tZW5kIHVzYWdlIG9mIE9yYWNsZSBKREsgOCBhcyBvcHBvc2VkIHRvIE9wZW5KREsgc28gaW5zdGFsbCB0aGlzIG9uZSBpZiB5b3VyIExpbnV4IGRpc3RyaWJ1dGlvbiBkb2VzIG5vdCBhbHJlYWR5IGhhcyBpdCBpbnN0YWxsZWQuDQoNCmBgYA0Kc3VkbyBhZGQtYXB0LXJlcG9zaXRvcnkgcHBhOndlYnVwZDh0ZWFtL2phdmENCnN1ZG8gYXB0LWdldCB1cGRhdGUNCnN1ZG8gYXB0LWdldCBpbnN0YWxsIG9yYWNsZS1qYXZhOC1pbnN0YWxsZXINCmBgYA0KDQpCZSBzdXJlIHRvIHNldCBpbiAvZXRjL2Vudmlyb25tZW50IGBKQVZBX0hPTUU9L3Vzci9iaW4vamF2YWANCg0K

A Farewell to Gluu

This will be short: trying to go forward another step with gluu I noticed that embedded SAML IdP, Shibbolet, is version 2.x, announced EOL for next year, while latest stable version is 3.1.2. Since my target was only SAML IdP I decided to go directly for Shibbolet.

VGhpcyB3aWxsIGJlIHNob3J0OiB0cnlpbmcgdG8gZ28gZm9yd2FyZCBhbm90aGVyIHN0ZXAgd2l0aCBnbHV1IEkgbm90aWNlZCB0aGF0IGVtYmVkZGVkIFNBTUwgSWRQLCBTaGliYm9sZXQsIGlzIHZlcnNpb24gMi54LCBhbm5vdW5jZWQgRU9MIGZvciBuZXh0IHllYXIsIHdoaWxlIGxhdGVzdCBzdGFibGUgdmVyc2lvbiBpcyAzLjEuMi4gU2luY2UgbXkgdGFyZ2V0IHdhcyBvbmx5IFNBTUwgSWRQIEkgZGVjaWRlZCB0byBnbyBkaXJlY3RseSBmb3IgU2hpYmJvbGV0Lg==

Sunday, October 25, 2015

Gluu (strickes forward :-))

Version

At the moment of writing the current version for OxTrust on GitHub is 3.0.2-SNAPSHOT but the installed version through apt-get (I used Ubuntu 14.0.4 LTS) is 2.3.4.Final

Installation

Installation is straight forward apart from first step: echo "deb http://repo.gluu.org/ubuntu/ trusty main" > /etc/apt/sources.list.d/gluu-repo.list because sudo echo..will not work; anyway, Google is our friend for one more time (as an alternative you can do the echo in another non-secured location and use sudo cp.. afterwards).

Cache Refresh Configuration

Configuration procedure, as described in on-line documentation looks simple. Unfortunately it fails to provide all the required details. Just to mention several inconsistencies:

  • Configuration is done on one form (as described in Configuration->Cache Refresh) while really enabling the functionality is done inConfiguration->Organization (!)
  • There are several mentions like :'Use SSL: Please tick the checkbox because the SSL has to be activated.': nice option since it looks you have no other option...
  • It is very unclear why you have to provide the Inum LDAP Server (described as the internal LDAP of the Gluu Server: if it's internal why should you re-specify it?) and why you have to provide the Server IP address in the Attribute Mapping section? (maybe this configurations have to do with external LDAP usage and some clustering configurations, just guessing)
  • Defining Customer Backend Key and Attributes is somehow redundant (and might induce not consistent configuration) with Attribute Mapping section: what if I define some attributes in first section and use other (as source) in the later?
  • It is not clear what the Enable checkbox meaning is: after Updating the configuration and performing a form refresh it will anyway become unchecked and the configuration file (/opt/gluu-server/opt/apache-tomcat.../conf/oxTrustCacheRefresh.properties) neither the Velocity template (oxTrust/configuration/target/classes/conf/oxTrustCacheRefresh.properties.vm) do not have any reference to it.
  • And it's keep going...

Anyway, here are my findings.

Source code

Fortunately the source code is available (in Maven project format) so you can enable Debug and see what's going on.

Enable debugging by changing the /opt/gluu-server/opt/apache-tomcat.../conf/gluuTomcatWrapper.conf by adding the following lines in # Java Additional Parameters section

wrapper.java.additional.8=-Xdebug
wrapper.java.additional.9=-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n

Restart gluu-server, start Netbeans (for instance), open pom.xml for OxTrust Server, attach debugger and place some breakpoints in org.gluu.oxtrust.ldap.cache.service.CacheRefreshTimer. Method process() is called every minute trying to do a cache refresh. It might be helpful to check also org.gluu.site.ldap.LDAPConnectionProvider in OxLdap project.

You can also check the /opt/gluu-server/opt/apache-tomcat.../logs/oxtrust_cache_refresh.log for errors if something does not work as expected.

Important remark: almost all configuration is kept also in LDAP so you can browse it through any decent LDAP browser:

  • localhost:1636
  • cn=directory manager
  • <password provided for admin user>
  • Use SSL

Have a look at ou=appliances,o=gluu and its descendants

Customer Backend Key and Attributes

  • Key attribute: sAMAccountName (AD as a backend)
  • Object class: user
  • Source attribute:
    • sAMAccountName (I think it's not required since we already have it as key attribute, have to check once again the code)
    • cn
    • memberOf
    • sn (see Attribute mapping section)

Source Backend LDAP Servers

  • We need to fill-in Bind DN since AD does not allow anonymous binding (and have unchecked the related checkbox)
  • Max connections: do not leave it 0 as proposed initial value; Update will not trigger any warning/error but no connections will be created in the pool (see org.gluu.site.ldap.LDAPConnectionProvider )
  • Server: I had to provide it in <IP>:389 format; it didn't work with <FQDN>:389 (even if ping worked I've got an exception about IPV6 name resolution in above mentioned log; trying to force java VM to IPV4 didn't work either)
  • Base DN: watch out that Gluu does perform single level search so you have to provide the exact parent DN; bad luck if you have spread your users in the directory structure (based on Department for instance); source code shows using an array of baseDNs so it might that it supports defining more than one (lik eblank or semicollon separated, just guessing)
  • Enable: God knows what is used for
  • Don't forget to Change Bind Password

While it looks you can add several source servers there is only one Bind Password and list of fetched attributes so this is not really useful apart from a limited workaround to the restriction imposed by the single level search.

Inum LDAP Sever

I tested it only with one server (the Gluu-server internal OpenDJ) so I have no clue what you can achieve by adding several servers/Base DN (anyway there is again only one Bind DN/Password).

  • Bind DN: cn=directory manager
  • Max Connections: 2 (Do not leave it 0)
  • Base DN: ou=people,o=site

Base DN specifies ou=people,o=site; initially users were created here but due to the fact I missed to provide an Attribute mapping for sn (mandatory requirement) after struggling with various configurations and resets I deleted all entries (for users). Since no new entries appeared I looked in the other areas and I found all users (now correctly imported) in ou=people,o=@!A362.9654.1097.E56D!0001!B929.EB26,o=gluu (to mention that among them we have admin which is consistent with the hint for Keep external persons (see below). So understanding is as follows :

  • Current data (gathered as an union from all defined servers)is compared with last snapshot (if this is not present with the data specified as Base DN); updatdd/deleted data is passed to next processing data
  • The configured Base DN is actually where differential data is pushed in the second step of refresh
  • As long as the differential data is validated against configuration and internal LDAP rules it is stored in ou=people,o=@!A362.9654.1097.E56D!0001!B929.EB26,o=gluu

Refresh Method

I used copy and checked Keep external persons (another very nice hint in the documentation :'If you do not enable 'Keep External Person', your 'admin' user including all other test users will be gone after first Cache Refresh iteration.'; very promising apart from not being provided in the Configuration on-line help but in the overview area...)

Attribute Mapping

This will provide for sure headaches since there is no mention about a tough (but normal) requirement: since default target object classes for the newly created (cached) users are gluuPerson, person,organizationalPerson, inetOrgPerson, ox-A36296541097E56D0001B929EB26, top, eduPerson, some of the through inheritance of course, you have to use only the ones defined for the above mentioned classes and include in the target attributes all required ones. At the moment of writing these are just two: cn and sn. Anyway you will get a self explanatory exception message in the above mentioned log.

Remark:The list of default object classes is defined in JSON configuration, accessible through one of the configuration menu entries.

  • I did the following mappings:
    • sAMAccountName->uid (If I remember correctly from debugging sessions this is done automatically anyway based on Key Attribute definition)
    • sAMAccountName->sn (so we have something in required sn)
    • cn->cn
    • memberOf->memberOf
  • Server IP address: the IP address of the gluu-server (what if we use DHCP? I haven't tried with FQDN)
  • Snapshot folder: /tmp which is relative to /opt/gluu-server (service is launched with chroot)
IyBWZXJzaW9uDQoNCkF0IHRoZSBtb21lbnQgb2Ygd3JpdGluZyB0aGUgY3VycmVudCB2ZXJzaW9uIGZvciBPeFRydXN0IG9uIEdpdEh1YiBpcyAzLjAuMi1TTkFQU0hPVCBidXQgdGhlIGluc3RhbGxlZCB2ZXJzaW9uIHRocm91Z2ggYXB0LWdldCAoSSB1c2VkIFVidW50dSAxNC4wLjQgTFRTKSAgaXMgMi4zLjQuRmluYWwNCg0KIyBJbnN0YWxsYXRpb24NCg0KSW5zdGFsbGF0aW9uIGlzIHN0cmFpZ2h0IGZvcndhcmQgYXBhcnQgZnJvbSBmaXJzdCBzdGVwOiBgZWNobyAiZGViIGh0dHA6Ly9yZXBvLmdsdXUub3JnL3VidW50dS8gdHJ1c3R5IG1haW4iID4gL2V0Yy9hcHQvc291cmNlcy5saXN0LmQvZ2x1dS1yZXBvLmxpc3QgYCBiZWNhdXNlIGBzdWRvIGVjaG9gLi53aWxsIG5vdCB3b3JrOyBhbnl3YXksIEdvb2dsZSBpcyBvdXIgZnJpZW5kIGZvciBvbmUgbW9yZSB0aW1lIChhcyBhbiBhbHRlcm5hdGl2ZSB5b3UgY2FuIGRvIHRoZSBlY2hvIGluIGFub3RoZXIgbm9uLXNlY3VyZWQgbG9jYXRpb24gYW5kIHVzZSBgc3VkbyBjcC4uYCBhZnRlcndhcmRzKS4NCg0KIyBDYWNoZSBSZWZyZXNoIENvbmZpZ3VyYXRpb24NCg0KQ29uZmlndXJhdGlvbiBwcm9jZWR1cmUsIGFzIGRlc2NyaWJlZCBpbiBvbi1saW5lIGRvY3VtZW50YXRpb24gbG9va3Mgc2ltcGxlLiBVbmZvcnR1bmF0ZWx5IGl0IGZhaWxzIHRvIHByb3ZpZGUgYWxsIHRoZSByZXF1aXJlZCBkZXRhaWxzLiBKdXN0IHRvIG1lbnRpb24gc2V2ZXJhbCBpbmNvbnNpc3RlbmNpZXM6IA0KKiBDb25maWd1cmF0aW9uIGlzIGRvbmUgb24gb25lIGZvcm0gKGFzIGRlc2NyaWJlZCBpbiBgQ29uZmlndXJhdGlvbi0+Q2FjaGUgUmVmcmVzaGApIHdoaWxlIHJlYWxseSBlbmFibGluZyB0aGUgZnVuY3Rpb25hbGl0eSBpcyBkb25lIGluYCBDb25maWd1cmF0aW9uLT5Pcmdhbml6YXRpb25gICghKQ0KKiBUaGVyZSBhcmUgc2V2ZXJhbCBtZW50aW9ucyBsaWtlIDonKlVzZSBTU0wqOiBQbGVhc2UgdGljayB0aGUgY2hlY2tib3ggYmVjYXVzZSB0aGUgU1NMIGhhcyB0byBiZSBhY3RpdmF0ZWQuJzogbmljZSBvcHRpb24gc2luY2UgaXQgbG9va3MgeW91IGhhdmUgbm8gb3RoZXIgb3B0aW9uLi4uDQoqIEl0IGlzIHZlcnkgdW5jbGVhciB3aHkgeW91IGhhdmUgdG8gcHJvdmlkZSB0aGUgYEludW0gTERBUCBTZXJ2ZXJgIChkZXNjcmliZWQgYXMgYHRoZSBpbnRlcm5hbCBMREFQIG9mIHRoZSBHbHV1IFNlcnZlciBgOiBpZiBpdCdzIGludGVybmFsIHdoeSBzaG91bGQgeW91IHJlLXNwZWNpZnkgaXQ/KSBhbmQgd2h5IHlvdSBoYXZlIHRvIHByb3ZpZGUgdGhlIFNlcnZlciBJUCBhZGRyZXNzIGluIHRoZSBBdHRyaWJ1dGUgTWFwcGluZyBzZWN0aW9uPyAobWF5YmUgdGhpcyBjb25maWd1cmF0aW9ucyBoYXZlIHRvIGRvIHdpdGggZXh0ZXJuYWwgTERBUCB1c2FnZSBhbmQgc29tZSBjbHVzdGVyaW5nIGNvbmZpZ3VyYXRpb25zLCBqdXN0IGd1ZXNzaW5nKQ0KKiBEZWZpbmluZyBgQ3VzdG9tZXIgQmFja2VuZCBLZXkgYW5kIEF0dHJpYnV0ZXNgIGlzIHNvbWVob3cgcmVkdW5kYW50IChhbmQgbWlnaHQgaW5kdWNlIG5vdCBjb25zaXN0ZW50IGNvbmZpZ3VyYXRpb24pIHdpdGggYEF0dHJpYnV0ZSBNYXBwaW5nYCBzZWN0aW9uOiB3aGF0IGlmIEkgZGVmaW5lIHNvbWUgYXR0cmlidXRlcyBpbiBmaXJzdCBzZWN0aW9uIGFuZCB1c2Ugb3RoZXIgKGFzIHNvdXJjZSkgaW4gdGhlIGxhdGVyPyANCiogSXQgaXMgbm90IGNsZWFyIHdoYXQgdGhlIEVuYWJsZSBjaGVja2JveCBtZWFuaW5nIGlzOiBhZnRlciBVcGRhdGluZyB0aGUgY29uZmlndXJhdGlvbiBhbmQgcGVyZm9ybWluZyBhIGZvcm0gcmVmcmVzaCBpdCB3aWxsIGFueXdheSBiZWNvbWUgdW5jaGVja2VkIGFuZCB0aGUgY29uZmlndXJhdGlvbiBmaWxlIChgL29wdC9nbHV1LXNlcnZlci9vcHQvYXBhY2hlLXRvbWNhdC4uLi9jb25mL294VHJ1c3RDYWNoZVJlZnJlc2gucHJvcGVydGllc2ApIG5laXRoZXIgdGhlIFZlbG9jaXR5IHRlbXBsYXRlIChgb3hUcnVzdC9jb25maWd1cmF0aW9uL3RhcmdldC9jbGFzc2VzL2NvbmYvb3hUcnVzdENhY2hlUmVmcmVzaC5wcm9wZXJ0aWVzLnZtYCkgZG8gbm90IGhhdmUgYW55IHJlZmVyZW5jZSB0byBpdC4NCiogQW5kIGl0J3Mga2VlcCBnb2luZy4uLg0KDQpBbnl3YXksIGhlcmUgYXJlIG15IGZpbmRpbmdzLg0KDQojIyBTb3VyY2UgY29kZQ0KDQpGb3J0dW5hdGVseSB0aGUgc291cmNlIGNvZGUgaXMgYXZhaWxhYmxlIChpbiBNYXZlbiBwcm9qZWN0IGZvcm1hdCkgc28geW91IGNhbiBlbmFibGUgRGVidWcgYW5kIHNlZSB3aGF0J3MgZ29pbmcgb24uDQoNCkVuYWJsZSBkZWJ1Z2dpbmcgYnkgY2hhbmdpbmcgdGhlIGAvb3B0L2dsdXUtc2VydmVyL29wdC9hcGFjaGUtdG9tY2F0Li4uL2NvbmYvZ2x1dVRvbWNhdFdyYXBwZXIuY29uZmAgYnkgYWRkaW5nIHRoZSBmb2xsb3dpbmcgbGluZXMgaW4gYCMgSmF2YSBBZGRpdGlvbmFsIFBhcmFtZXRlcnNgIHNlY3Rpb24NCmBgYA0Kd3JhcHBlci5qYXZhLmFkZGl0aW9uYWwuOD0tWGRlYnVnDQp3cmFwcGVyLmphdmEuYWRkaXRpb25hbC45PS1YcnVuamR3cDp0cmFuc3BvcnQ9ZHRfc29ja2V0LGFkZHJlc3M9NTAwNSxzZXJ2ZXI9eSxzdXNwZW5kPW4NCmBgYA0KUmVzdGFydCBnbHV1LXNlcnZlciwgc3RhcnQgTmV0YmVhbnMgKGZvciBpbnN0YW5jZSksIG9wZW4gcG9tLnhtbCBmb3IgT3hUcnVzdCBTZXJ2ZXIsIGF0dGFjaCBkZWJ1Z2dlciBhbmQgcGxhY2Ugc29tZSBicmVha3BvaW50cyBpbiBgb3JnLmdsdXUub3h0cnVzdC5sZGFwLmNhY2hlLnNlcnZpY2UuQ2FjaGVSZWZyZXNoVGltZXJgLiBNZXRob2QgYHByb2Nlc3MoKWAgaXMgY2FsbGVkIGV2ZXJ5IG1pbnV0ZSB0cnlpbmcgdG8gZG8gYSBjYWNoZSByZWZyZXNoLiBJdCBtaWdodCBiZSBoZWxwZnVsIHRvIGNoZWNrIGFsc28gYG9yZy5nbHV1LnNpdGUubGRhcC5MREFQQ29ubmVjdGlvblByb3ZpZGVyYCBpbiBPeExkYXAgcHJvamVjdC4NCg0KWW91IGNhbiBhbHNvIGNoZWNrIHRoZSBgL29wdC9nbHV1LXNlcnZlci9vcHQvYXBhY2hlLXRvbWNhdC4uLi9sb2dzL294dHJ1c3RfY2FjaGVfcmVmcmVzaC5sb2dgIGZvciBlcnJvcnMgaWYgc29tZXRoaW5nIGRvZXMgbm90IHdvcmsgYXMgZXhwZWN0ZWQuDQoNCioqSW1wb3J0YW50IHJlbWFyayoqOiBhbG1vc3QgYWxsIGNvbmZpZ3VyYXRpb24gaXMga2VwdCBhbHNvIGluIExEQVAgc28geW91IGNhbiBicm93c2UgaXQgdGhyb3VnaCBhbnkgZGVjZW50IExEQVAgYnJvd3NlcjoNCiogbG9jYWxob3N0OjE2MzYNCiogY249ZGlyZWN0b3J5IG1hbmFnZXINCiogXDxwYXNzd29yZCBwcm92aWRlZCBmb3IgYWRtaW4gdXNlclw+DQoqIFVzZSBTU0wNCg0KSGF2ZSBhIGxvb2sgYXQgYG91PWFwcGxpYW5jZXMsbz1nbHV1YCBhbmQgaXRzIGRlc2NlbmRhbnRzDQoNCg0KIyMgQ3VzdG9tZXIgQmFja2VuZCBLZXkgYW5kIEF0dHJpYnV0ZXMNCg0KKiBLZXkgYXR0cmlidXRlOiBzQU1BY2NvdW50TmFtZSAoQUQgYXMgYSBiYWNrZW5kKQ0KKiBPYmplY3QgY2xhc3M6IHVzZXINCiogU291cmNlIGF0dHJpYnV0ZToNCgkqIHNBTUFjY291bnROYW1lIChJIHRoaW5rIGl0J3Mgbm90IHJlcXVpcmVkIHNpbmNlIHdlIGFscmVhZHkgaGF2ZSBpdCBhcyBrZXkgYXR0cmlidXRlLCBoYXZlIHRvIGNoZWNrIG9uY2UgYWdhaW4gdGhlIGNvZGUpDQoJKiBjbg0KCSogbWVtYmVyT2YNCgkqIHNuIChzZWUgQXR0cmlidXRlIG1hcHBpbmcgc2VjdGlvbikNCg0KIyMgU291cmNlIEJhY2tlbmQgTERBUCBTZXJ2ZXJzDQoNCiogV2UgbmVlZCB0byBmaWxsLWluIEJpbmQgRE4gc2luY2UgQUQgZG9lcyBub3QgYWxsb3cgYW5vbnltb3VzIGJpbmRpbmcgKGFuZCBoYXZlIHVuY2hlY2tlZCB0aGUgcmVsYXRlZCBjaGVja2JveCkNCiogTWF4IGNvbm5lY3Rpb25zOiBkbyBub3QgbGVhdmUgaXQgMCBhcyBwcm9wb3NlZCBpbml0aWFsIHZhbHVlOyBVcGRhdGUgd2lsbCBub3QgdHJpZ2dlciBhbnkgd2FybmluZy9lcnJvciBidXQgbm8gY29ubmVjdGlvbnMgd2lsbCBiZSBjcmVhdGVkIGluIHRoZSBwb29sIChzZWUgYG9yZy5nbHV1LnNpdGUubGRhcC5MREFQQ29ubmVjdGlvblByb3ZpZGVyYCApDQoqIFNlcnZlcjogSSBoYWQgdG8gcHJvdmlkZSBpdCBpbiBcPElQXD46Mzg5IGZvcm1hdDsgaXQgZGlkbid0IHdvcmsgd2l0aCBcPEZRRE5cPjozODkgKGV2ZW4gaWYgcGluZyB3b3JrZWQgSSd2ZSBnb3QgYW4gZXhjZXB0aW9uIGFib3V0IElQVjYgbmFtZSByZXNvbHV0aW9uIGluIGFib3ZlIG1lbnRpb25lZCBsb2c7IHRyeWluZyB0byBmb3JjZSBqYXZhIFZNIHRvIElQVjQgZGlkbid0IHdvcmsgZWl0aGVyKQ0KKiBCYXNlIEROOiB3YXRjaCBvdXQgdGhhdCBHbHV1IGRvZXMgcGVyZm9ybSAgc2luZ2xlIGxldmVsIHNlYXJjaCBzbyB5b3UgaGF2ZSB0byBwcm92aWRlIHRoZSBleGFjdCBwYXJlbnQgRE47IGJhZCBsdWNrIGlmIHlvdSBoYXZlIHNwcmVhZCB5b3VyIHVzZXJzIGluIHRoZSBkaXJlY3Rvcnkgc3RydWN0dXJlIChiYXNlZCBvbiBEZXBhcnRtZW50IGZvciBpbnN0YW5jZSk7IHNvdXJjZSBjb2RlIHNob3dzIHVzaW5nIGFuIGFycmF5IG9mIGJhc2VETnMgc28gaXQgbWlnaHQgdGhhdCBpdCBzdXBwb3J0cyBkZWZpbmluZyBtb3JlIHRoYW4gb25lIChsaWsgZWJsYW5rIG9yIHNlbWljb2xsb24gc2VwYXJhdGVkLCBqdXN0IGd1ZXNzaW5nKQ0KKiBFbmFibGU6ICoqR29kKioga25vd3Mgd2hhdCBpcyB1c2VkIGZvcg0KKiBEb24ndCBmb3JnZXQgdG8gYENoYW5nZSBCaW5kIFBhc3N3b3JkYA0KDQpXaGlsZSBpdCBsb29rcyB5b3UgY2FuIGFkZCBzZXZlcmFsIHNvdXJjZSBzZXJ2ZXJzIHRoZXJlIGlzIG9ubHkgb25lIEJpbmQgUGFzc3dvcmQgYW5kIGxpc3Qgb2YgZmV0Y2hlZCBhdHRyaWJ1dGVzIHNvIHRoaXMgaXMgbm90IHJlYWxseSB1c2VmdWwgYXBhcnQgZnJvbSBhIGxpbWl0ZWQgd29ya2Fyb3VuZCB0byB0aGUgcmVzdHJpY3Rpb24gaW1wb3NlZCBieSB0aGUgc2luZ2xlIGxldmVsIHNlYXJjaC4NCg0KIyMgSW51bSBMREFQIFNldmVyDQoNCkkgdGVzdGVkIGl0IG9ubHkgd2l0aCBvbmUgc2VydmVyICh0aGUgR2x1dS1zZXJ2ZXIgaW50ZXJuYWwgT3BlbkRKKSBzbyBJIGhhdmUgbm8gY2x1ZSB3aGF0IHlvdSBjYW4gYWNoaWV2ZSBieSBhZGRpbmcgc2V2ZXJhbCBzZXJ2ZXJzL0Jhc2UgRE4gKGFueXdheSB0aGVyZSBpcyBhZ2FpbiBvbmx5IG9uZSBCaW5kIEROL1Bhc3N3b3JkKS4NCg0KKiBCaW5kIEROOiBjbj1kaXJlY3RvcnkgbWFuYWdlcg0KKiBNYXggQ29ubmVjdGlvbnM6IDIgKCoqRG8gbm90IGxlYXZlIGl0IDAqKikNCiogQmFzZSBETjogb3U9cGVvcGxlLG89c2l0ZQ0KDQpCYXNlIEROIHNwZWNpZmllcyBgb3U9cGVvcGxlLG89c2l0ZWA7IGluaXRpYWxseSB1c2VycyB3ZXJlIGNyZWF0ZWQgaGVyZSBidXQgZHVlIHRvIHRoZSBmYWN0IEkgbWlzc2VkIHRvIHByb3ZpZGUgYW4gQXR0cmlidXRlIG1hcHBpbmcgZm9yIHNuIChtYW5kYXRvcnkgcmVxdWlyZW1lbnQpIGFmdGVyIHN0cnVnZ2xpbmcgd2l0aCB2YXJpb3VzIGNvbmZpZ3VyYXRpb25zIGFuZCByZXNldHMgSSBkZWxldGVkIGFsbCBlbnRyaWVzIChmb3IgdXNlcnMpLiBTaW5jZSBubyBuZXcgZW50cmllcyBhcHBlYXJlZCBJIGxvb2tlZCBpbiB0aGUgb3RoZXIgYXJlYXMgYW5kIEkgZm91bmQgYWxsIHVzZXJzIChub3cgY29ycmVjdGx5IGltcG9ydGVkKSBpbiBgb3U9cGVvcGxlLG89QCFBMzYyLjk2NTQuMTA5Ny5FNTZEITAwMDEhQjkyOS5FQjI2LG89Z2x1dWAgKHRvIG1lbnRpb24gdGhhdCBhbW9uZyB0aGVtIHdlIGhhdmUgYWRtaW4gd2hpY2ggaXMgY29uc2lzdGVudCB3aXRoIHRoZSBoaW50IGZvciBgS2VlcCBleHRlcm5hbCBwZXJzb25zYCAoc2VlIGJlbG93KS4gU28gdW5kZXJzdGFuZGluZyBpcyBhcyBmb2xsb3dzIDoNCiANCiogQ3VycmVudCBkYXRhIChnYXRoZXJlZCBhcyBhbiB1bmlvbiBmcm9tIGFsbCBkZWZpbmVkIHNlcnZlcnMpaXMgY29tcGFyZWQgd2l0aCBsYXN0IHNuYXBzaG90IChpZiB0aGlzIGlzIG5vdCBwcmVzZW50IHdpdGggdGhlIGRhdGEgc3BlY2lmaWVkIGFzIEJhc2UgRE4pOyB1cGRhdGRkL2RlbGV0ZWQgZGF0YSBpcyBwYXNzZWQgdG8gbmV4dCBwcm9jZXNzaW5nIGRhdGENCiogVGhlIGNvbmZpZ3VyZWQgQmFzZSBETiBpcyBhY3R1YWxseSB3aGVyZSBkaWZmZXJlbnRpYWwgZGF0YSBpcyBwdXNoZWQgaW4gdGhlIHNlY29uZCBzdGVwIG9mIHJlZnJlc2gNCiogQXMgbG9uZyBhcyB0aGUgZGlmZmVyZW50aWFsIGRhdGEgaXMgdmFsaWRhdGVkIGFnYWluc3QgY29uZmlndXJhdGlvbiBhbmQgaW50ZXJuYWwgTERBUCBydWxlcyBpdCBpcyBzdG9yZWQgaW4gYG91PXBlb3BsZSxvPUAhQTM2Mi45NjU0LjEwOTcuRTU2RCEwMDAxIUI5MjkuRUIyNixvPWdsdXVgDQoNCiMjIFJlZnJlc2ggTWV0aG9kDQoNCkkgdXNlZCBjb3B5IGFuZCBjaGVja2VkIGBLZWVwIGV4dGVybmFsIHBlcnNvbnNgIChhbm90aGVyIHZlcnkgbmljZSBoaW50IGluIHRoZSBkb2N1bWVudGF0aW9uIDonKklmIHlvdSBkbyBub3QgZW5hYmxlICdLZWVwIEV4dGVybmFsIFBlcnNvbicsIHlvdXIgJ2FkbWluJyB1c2VyIGluY2x1ZGluZyBhbGwgb3RoZXIgdGVzdCB1c2VycyB3aWxsIGJlIGdvbmUgYWZ0ZXIgZmlyc3QgQ2FjaGUgUmVmcmVzaCBpdGVyYXRpb24uKic7IHZlcnkgcHJvbWlzaW5nIGFwYXJ0IGZyb20gbm90IGJlaW5nIHByb3ZpZGVkIGluIHRoZSBDb25maWd1cmF0aW9uIG9uLWxpbmUgaGVscCBidXQgaW4gdGhlIG92ZXJ2aWV3IGFyZWEuLi4pDQoNCiMjIEF0dHJpYnV0ZSBNYXBwaW5nDQoNClRoaXMgd2lsbCBwcm92aWRlIGZvciBzdXJlIGhlYWRhY2hlcyBzaW5jZSB0aGVyZSBpcyBubyBtZW50aW9uIGFib3V0IGEgdG91Z2ggKGJ1dCBub3JtYWwpIHJlcXVpcmVtZW50OiBzaW5jZSBkZWZhdWx0IHRhcmdldCBvYmplY3QgY2xhc3NlcyBmb3IgdGhlIG5ld2x5IGNyZWF0ZWQgKGNhY2hlZCkgdXNlcnMgYXJlICBnbHV1UGVyc29uLCBwZXJzb24sb3JnYW5pemF0aW9uYWxQZXJzb24sIGluZXRPcmdQZXJzb24sIG94LUEzNjI5NjU0MTA5N0U1NkQwMDAxQjkyOUVCMjYsIHRvcCwgZWR1UGVyc29uLCBzb21lIG9mIHRoZSB0aHJvdWdoIGluaGVyaXRhbmNlIG9mIGNvdXJzZSwgeW91IGhhdmUgdG8gdXNlIG9ubHkgdGhlIG9uZXMgZGVmaW5lZCBmb3IgdGhlIGFib3ZlIG1lbnRpb25lZCBjbGFzc2VzIGFuZCBpbmNsdWRlIGluIHRoZSB0YXJnZXQgYXR0cmlidXRlcyBhbGwgcmVxdWlyZWQgb25lcy4gQXQgdGhlIG1vbWVudCBvZiB3cml0aW5nIHRoZXNlIGFyZSBqdXN0IHR3bzogY24gYW5kIHNuLiBBbnl3YXkgeW91IHdpbGwgZ2V0IGEgc2VsZiBleHBsYW5hdG9yeSBleGNlcHRpb24gbWVzc2FnZSBpbiB0aGUgYWJvdmUgbWVudGlvbmVkIGxvZy4NCg0KKipSZW1hcms6KipUaGUgbGlzdCBvZiBkZWZhdWx0IG9iamVjdCBjbGFzc2VzIGlzIGRlZmluZWQgaW4gSlNPTiBjb25maWd1cmF0aW9uLCBhY2Nlc3NpYmxlIHRocm91Z2ggb25lIG9mIHRoZSBjb25maWd1cmF0aW9uIG1lbnUgZW50cmllcy4NCg0KKiBJIGRpZCB0aGUgZm9sbG93aW5nIG1hcHBpbmdzOg0KCSogc0FNQWNjb3VudE5hbWUtPnVpZCAoSWYgSSByZW1lbWJlciBjb3JyZWN0bHkgZnJvbSBkZWJ1Z2dpbmcgc2Vzc2lvbnMgdGhpcyBpcyBkb25lIGF1dG9tYXRpY2FsbHkgYW55d2F5IGJhc2VkIG9uIEtleSBBdHRyaWJ1dGUgZGVmaW5pdGlvbikNCgkqIHNBTUFjY291bnROYW1lLT5zbiAoc28gd2UgaGF2ZSBzb21ldGhpbmcgaW4gcmVxdWlyZWQgc24pDQoJKiBjbi0+Y24NCgkqIG1lbWJlck9mLT5tZW1iZXJPZg0KKiBTZXJ2ZXIgSVAgYWRkcmVzczogdGhlIElQIGFkZHJlc3Mgb2YgdGhlIGdsdXUtc2VydmVyICh3aGF0IGlmIHdlIHVzZSBESENQPyBJIGhhdmVuJ3QgdHJpZWQgd2l0aCBGUUROKQ0KKiBTbmFwc2hvdCBmb2xkZXI6IC90bXAgd2hpY2ggaXMgcmVsYXRpdmUgdG8gL29wdC9nbHV1LXNlcnZlciAoc2VydmljZSBpcyBsYXVuY2hlZCB3aXRoIGNocm9vdCkNCg0KDQo=

Sunday, October 18, 2015

OpenAM (strikes back): Install/Configure Jetty for OpenIG-J2EE Agent

I have used Jetty as OpenIG container (version jetty-distribution-8.1.17.v20150415).

Install Jetty as a service

Download the Jetty distribution and unpack it in the traget directory. Follow instructions in http://www.eclipse.org/jetty/documentation/current/startup-windows-service.html

Several remarks:

  • In the install.bat batch change the set PR_JVMOPTIONS= line to set PR_JVMOPTIONS=-Duser.dir="%JETTY_BASE%";-Djetty.port=8081;-Djava.io.tmpdir="C:\jetty\temp";-Djetty.home="%JETTY_HOME%";-Djetty.base="%JETTY_BASE%";-Dopenig.base="C:\jetty\OpenIG";-Xdebug;-Xnoagent;-Djava.compiler=NONE;-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005; so that the used port will be 8081, the debug support will be enabled and the OpenIG base directory will be set up
  • Removing the service can be done with the command prunsrv.exe //DS/JettyService4OpenIG (JettyService4OpenIG is the chosen name for the service);
  • In order to be able to stop the service I had to remove comment the following lines in the install.bat:
    • REM set PR_STOPPARAMS=--stop;STOP.KEY="%STOPKEY%";STOP.PORT=%STOPPORT%;STOP.WAIT=10
    • REM --StartParams="%PR_STARTPARAMS%" ^

Deploy OpenIG

Rename the war to root.war and deploy it to webapps; remove test.xml from contexts (so that the test application is not deployed (with error))

Deploy J2EE Agent

Follow the instructions in https://backstage.forgerock.com/#!/docs/openam-policy-agents/3.5.0/jee-users-guide#chap-jetty in parallel with https://backstage.forgerock.com/#!/docs/openig/3.1.0/gateway-guide#chap-password-capture-replay-tutorial to deploy the agent on the same Jetty instance

Configure the Agent

  • Go to the newly defined realm (let's say it's named MyRealm) and select the Agents tab
  • Select the J2EE tab
  • Add a new Agent; be sure to use the port configured in Jetty (8081 in my case) in defining URLs:

Notices:

  • Even if the agent is defined as part for MyRealm configuration and the property com.sun.identity.agents.config.organization.name = /MyRealm in agents bootstrap properties (OpenSSOAgentBootstrap.properties) is set up properly the generated login URL (at runtime) will not contain the targeted realm so user authentication will take place against Top Level Realm.
  • Password relay will not work with XUI interface due to a bug solved in subsequent 12.0.0 releases (available only to subscribers) so the solution is to switch to classic UI (https://bugster.forgerock.org/jira/browse/OPENAM-5921). See https://backstage.forgerock.com/#!/docs/openam/12.0.0/install-guide/chap-custom-ui on how to disable XUI.
  • Changing /repaly to /replay/* will allow us to integrate more than one legacy applications in the system by using different specific routes in OpenIG; the ideea is that any GET to <OpenIG-URL>/relay/MyApp will trigger the same replay process by redirecting the user to OpenAM login dialog; the redirect back to OpenIG will be at the same URL which now will be forwarded to OpenIG which in turn based on this URL different routes can come into action (see OpenIG specifics below).

Configure OpenIG

There is not obvious at the first glance but we must define the config.js in config subdirectory of the configuread OpenIG base directory (parameter -Dopenig.base, see above) and the specific routes in config\routes.

config.js

{
    "handler": {
        "type": "Router",
        "audit": "global",
        "capture": "all"
    },
    "heap": [
        {
            "name": "LogSink",
            "type": "ConsoleLogSink",
            "config": {
                "level": "DEBUG"
            }
        },
        {
            "name": "JwtSession",
            "type": "JwtSession"
        },
        {
            "name": "ClientHandler",
            "type": "ClientHandler"
        },
        {
            "name": "capture",
            "type": "CaptureDecorator",
            "config": {
                "captureEntity": true,
                "_captureExchange": true
            }
        }
    ]
}

Routes for OTRS

Login route

{
    "handler": {
        "type": "Chain",
        "config": {
            "filters": [
                {
                    "type": "CryptoHeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "operation": "DECRYPT",
                        "algorithm": "DES/ECB/NoPadding",
                        "key": "xxx",
                        "keyType": "DES",
                        "charSet": "utf-8",
                        "headers": [
                            "password"
                        ]
                    }
                },
                {
                    "type": "AssignmentFilter",
                    "config": {
                        "onRequest": [
                            {
                                "target": "${exchange.authInfoUsername}",
                                "value": "${exchange.request.headers['username'][0]}"
                            },
                            {
                                "target": "${exchange.authInfoPassword}",
                                "value": "${exchange.request.headers['password'][0]}"
                            }
                        ]
                    }
                },
                {
                    "type": "HeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "remove": [
                            "password",
                            "username"
                        ]
                    }
                },
                {
                    "type": "StaticRequestFilter",
                    "config": {
                        "method": "POST",
                        "uri": "https://otrs-fqdn/otrs/customer.pl",
                        "form": {
                            "User": [
                                "${exchange.authInfoUsername}"
                            ],
                            "Password": [
                                "${exchange.authInfoPassword}"
                            ],
                            "Action":["Login"],
                            "RequestedURL":[""],
                            "Lang":["en"],
                            "TimeOffset":["-180"]
                        }
                    }
                }
            ],
            "handler": "ClientHandler"
        }
    },
    "condition": "${matches(exchange.request.uri.path, '^/replay/otrs')}"
}

Default route

{
    "handler": "ClientHandler",
    "condition": "${matches(exchange.request.uri.path, '^/otrs')}",
    "baseURI": "https://otrs-fqdn"

}

Routes for othe application (two steps login)

This application is JSF based so the POST at login view must have the session cookie set. The solution is to have a first step requesting GET before so that the session is created.

Login route

{
    "heap": [
        {
            "name": "DispatchHandler",
            "type": "DispatchHandler",
            "config": {
                "bindings": [
                    {
                        "handler": {
                            "type": "Chain",
                            "config": {
                                "filters": [
                                    {
                                        "type": "CryptoHeaderFilter",
                                        "config": {
                                            "messageType": "REQUEST",
                                            "operation": "DECRYPT",
                                            "algorithm": "DES/ECB/NoPadding",
                                            "key": "xxx",
                                            "keyType": "DES",
                                            "charSet": "utf-8",
                                            "headers": [
                                                "password"
                                            ]
                                        }
                                    },
                                    {
                                        "type": "AssignmentFilter",
                                        "config": {
                                            "onRequest": [
                                                {
                                                    "target": "${exchange.authInfoUsername}",
                                                    "value": "${exchange.request.headers['username'][0]}"
                                                },
                                                {
                                                    "target": "${exchange.authInfoPassword}",
                                                    "value": "${exchange.request.headers['password'][0]}"
                                                }
                                            ]
                                        }
                                    },
                                    {
                                        "type": "HeaderFilter",
                                        "config": {
                                            "messageType": "REQUEST",
                                            "remove": [
                                                "password",
                                                "username"
                                            ]
                                        }
                                    },
                                    
                                    {
                                        "type": "StaticRequestFilter",
                                        "config": {
                                            "method": "GET",
                                            "uri": "http://legacy-app-fqdn:8081/MyApp/faces/login.xhtml"
                                        }
                                    },
                                    {
                                        "type": "SwitchFilter",
                                        "config": {
                                            "onResponse": [
                                                {
                                                    "handler": "LoginRequestHandler"
                                                }
                                            ]
                                        }
                                    },
                                    {
                                        "type": "EntityExtractFilter",
                                        "config": {
                                            "messageType": "response",
                                            "target": "${exchange.viewState}",
                                            "bindings": [
                                                {
                                                    "key": "value",
                                                    "pattern":
                                                        "javax\\.faces\\.ViewState\"\\s.*value=\"(.*)\"\\s*autocomplete=",
                                                    "template": "$1"
                                                }
                                            ]
                                        }
                                    },
                                    {
                                        "type": "AssignmentFilter",
                                        "config": {
                                            "onResponse": [
                                                {
                                                    "target": "${exchange.sessionCookie}",
                                                    "value": "${split(exchange.response.headers['Set-Cookie'][0],';')[0]}"
                                                }
                                            ]
                                        }
                                    }

                                ],
                                "handler": "ClientHandler"
                            }
                        }
    
                    }
            
                ]
            }
        },
        {
            "name": "LoginRequestHandler",
            "type": "Chain",
            "config": {
                "filters": [
                
                {
                    "type": "StaticRequestFilter",
                    "config": {
                        "method": "POST",
                        "uri": "http://legacy-app-fqdn:8081/MyApp/faces/login.xhtml",
                        "form": {
                            "loginForm:j_username": [
                                "${exchange.authInfoUsername}"
                            ],
                            "loginForm:j_password": [
                                "${exchange.authInfoPassword}"
                            ],
                            "loginForm":["loginForm"],
                            "loginForm:j_idt17":[""],
                            "javax.faces.ViewState":["${exchange.viewState.value}"]
                        },
                        "headers": {
                            "Cookie": ["${exchange.sessionCookie}"]
                        }
                    }
                },
                {
                     "type": "HeaderFilter",
                     "config": {
                         "messageType": "RESPONSE",
                         "add": {
                             "Set-Cookie": [ "${exchange.sessionCookie}; path=/MyApp" ]
                         }
                     }
                }
                ],
                "handler": "ClientHandler"
            }
        }
    ],
    "handler": "DispatchHandler",
    "condition": "${matches(exchange.request.uri.path, '^/replay/MyApp')}"
}

Default route

{
    "handler": "ClientHandler",
    "condition": "${matches(exchange.request.uri.path, '^/MyApp')}",
    "baseURI": "http://legacy-app-fqdn:8081"

}

Several remarks

As I decided to abandon this solution there were things unconfigured or missconfigured. Just to mention two:

  • Logout process (you might end up with the simulation of SSO screwed up if the users logout of one the application trying to re-log-in as a different user)
  • Sniffing the network during tests I noticed announcement (POSTs if I remember corectly) from OpenAM to J2EE agent, messages not intercepted by the agent and forwarded (or dropped, depending on default router configuration) by OpenIG
SSBoYXZlIHVzZWQgSmV0dHkgYXMgT3BlbklHIGNvbnRhaW5lciAodmVyc2lvbiBqZXR0eS1kaXN0cmlidXRpb24tOC4xLjE3LnYyMDE1MDQxNSkuDQoNCiMgSW5zdGFsbCBKZXR0eSBhcyBhIHNlcnZpY2UNCg0KRG93bmxvYWQgdGhlIEpldHR5IGRpc3RyaWJ1dGlvbiBhbmQgdW5wYWNrIGl0IGluIHRoZSB0cmFnZXQgZGlyZWN0b3J5Lg0KRm9sbG93IGluc3RydWN0aW9ucyBpbiBbaHR0cDovL3d3dy5lY2xpcHNlLm9yZy9qZXR0eS9kb2N1bWVudGF0aW9uL2N1cnJlbnQvc3RhcnR1cC13aW5kb3dzLXNlcnZpY2UuaHRtbF0oaHR0cDovL3d3dy5lY2xpcHNlLm9yZy9qZXR0eS9kb2N1bWVudGF0aW9uL2N1cnJlbnQvc3RhcnR1cC13aW5kb3dzLXNlcnZpY2UuaHRtbCkNCg0KU2V2ZXJhbCByZW1hcmtzOg0KKiBJbiB0aGUgaW5zdGFsbC5iYXQgYmF0Y2ggY2hhbmdlIHRoZSBgc2V0IFBSX0pWTU9QVElPTlM9YCBsaW5lIHRvIGBzZXQgUFJfSlZNT1BUSU9OUz0tRHVzZXIuZGlyPSIlSkVUVFlfQkFTRSUiOy1EamV0dHkucG9ydD04MDgxOy1EamF2YS5pby50bXBkaXI9IkM6XGpldHR5XHRlbXAiOy1EamV0dHkuaG9tZT0iJUpFVFRZX0hPTUUlIjstRGpldHR5LmJhc2U9IiVKRVRUWV9CQVNFJSI7LURvcGVuaWcuYmFzZT0iQzpcamV0dHlcT3BlbklHIjstWGRlYnVnOy1Ybm9hZ2VudDstRGphdmEuY29tcGlsZXI9Tk9ORTstWHJ1bmpkd3A6dHJhbnNwb3J0PWR0X3NvY2tldCxzZXJ2ZXI9eSxzdXNwZW5kPW4sYWRkcmVzcz01MDA1O2Agc28gdGhhdCB0aGUgdXNlZCBwb3J0IHdpbGwgYmUgODA4MSwgdGhlIGRlYnVnIHN1cHBvcnQgd2lsbCBiZSBlbmFibGVkIGFuZCB0aGUgT3BlbklHIGJhc2UgZGlyZWN0b3J5IHdpbGwgYmUgc2V0IHVwDQoqIFJlbW92aW5nIHRoZSBzZXJ2aWNlIGNhbiBiZSBkb25lIHdpdGggdGhlIGNvbW1hbmQgYHBydW5zcnYuZXhlIC8vRFMvSmV0dHlTZXJ2aWNlNE9wZW5JR2AgKEpldHR5U2VydmljZTRPcGVuSUcgaXMgdGhlIGNob3NlbiBuYW1lIGZvciB0aGUgc2VydmljZSk7DQoqIEluIG9yZGVyIHRvIGJlIGFibGUgdG8gc3RvcCB0aGUgc2VydmljZSBJIGhhZCB0byByZW1vdmUgY29tbWVudCB0aGUgZm9sbG93aW5nIGxpbmVzIGluIHRoZSBpbnN0YWxsLmJhdDoNCgkqIGBSRU0gc2V0IFBSX1NUT1BQQVJBTVM9LS1zdG9wO1NUT1AuS0VZPSIlU1RPUEtFWSUiO1NUT1AuUE9SVD0lU1RPUFBPUlQlO1NUT1AuV0FJVD0xMGANCgkqIGBSRU0gIC0tU3RhcnRQYXJhbXM9IiVQUl9TVEFSVFBBUkFNUyUiIF5gDQoNCiMgRGVwbG95IE9wZW5JRw0KDQpSZW5hbWUgdGhlIHdhciB0byBgcm9vdC53YXJgIGFuZCBkZXBsb3kgaXQgdG8gd2ViYXBwczsgcmVtb3ZlIHRlc3QueG1sIGZyb20gY29udGV4dHMgKHNvIHRoYXQgdGhlIHRlc3QgYXBwbGljYXRpb24gaXMgbm90IGRlcGxveWVkICh3aXRoIGVycm9yKSkNCg0KIyBEZXBsb3kgSjJFRSBBZ2VudA0KDQpGb2xsb3cgdGhlIGluc3RydWN0aW9ucyBpbiBbaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5hbS1wb2xpY3ktYWdlbnRzLzMuNS4wL2plZS11c2Vycy1ndWlkZSNjaGFwLWpldHR5XShodHRwczovL2JhY2tzdGFnZS5mb3JnZXJvY2suY29tLyMhL2RvY3Mvb3BlbmFtLXBvbGljeS1hZ2VudHMvMy41LjAvamVlLXVzZXJzLWd1aWRlI2NoYXAtamV0dHkpIGluIHBhcmFsbGVsIHdpdGggW2h0dHBzOi8vYmFja3N0YWdlLmZvcmdlcm9jay5jb20vIyEvZG9jcy9vcGVuaWcvMy4xLjAvZ2F0ZXdheS1ndWlkZSNjaGFwLXBhc3N3b3JkLWNhcHR1cmUtcmVwbGF5LXR1dG9yaWFsXShodHRwczovL2JhY2tzdGFnZS5mb3JnZXJvY2suY29tLyMhL2RvY3Mvb3BlbmlnLzMuMS4wL2dhdGV3YXktZ3VpZGUjY2hhcC1wYXNzd29yZC1jYXB0dXJlLXJlcGxheS10dXRvcmlhbCkgdG8gZGVwbG95IHRoZSBhZ2VudCBvbiB0aGUgc2FtZSBKZXR0eSBpbnN0YW5jZQ0KDQojIENvbmZpZ3VyZSB0aGUgQWdlbnQNCg0KKiBHbyB0byB0aGUgbmV3bHkgZGVmaW5lZCByZWFsbSAobGV0J3Mgc2F5IGl0J3MgbmFtZWQgTXlSZWFsbSkgYW5kIHNlbGVjdCB0aGUgQWdlbnRzIHRhYg0KKiBTZWxlY3QgdGhlIEoyRUUgdGFiDQoqIEFkZCBhIG5ldyBBZ2VudDsgYmUgc3VyZSB0byB1c2UgdGhlIHBvcnQgY29uZmlndXJlZCBpbiBKZXR0eSAoODA4MSBpbiBteSBjYXNlKSBpbiBkZWZpbmluZyBVUkxzOg0KCSogU2V0IGBBZ2VudCBGaWx0ZXIgTW9kZWAgdG8gYFNTT19PTkxZYA0KCSogU2V0IHRoZSBgT3BlbkFNIExvZ2luIFVSTGAgdG8gY29udGFpbiB0aGUgYHJlYWxtPU15UmVhbG1gIHF1ZXJ5IHBhcmFtZXRlcioNCgkqIENoYW5nZSB0aGUgYHdlYmRlZmF1bHQueG1sYCBpbiBldGMgc3ViZGlyZWN0b3J5IG9mIEpldHR5IGluc3RhbGxhdGlvbiBhcyBtZW50aW9uZWQgaW4gSjJFRSBhZ2VudCBjb25maWd1cmF0aW9uIChbaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5pZy8zLjEuMC9nYXRld2F5LWd1aWRlI2NhcHR1cmUtcmVsYXktc2V0dXAtcGFdKGh0dHBzOi8vYmFja3N0YWdlLmZvcmdlcm9jay5jb20vIyEvZG9jcy9vcGVuaWcvMy4xLjAvZ2F0ZXdheS1ndWlkZSNjYXB0dXJlLXJlbGF5LXNldHVwLXBhKSkgYnV0IHVzZSBgPHVybC1wYXR0ZXJuPi9yZXBsYXkvKjwvdXJsLXBhdHRlcm4+YCBpbnN0ZWFkIG9mIGA8dXJsLXBhdHRlcm4+L3JlcGxheS88L3VybC1wYXR0ZXJuPmAgKHNlZSBiZWxvdy4uLikuDQoNCioqTm90aWNlczoqKiANCiogRXZlbiBpZiB0aGUgYWdlbnQgaXMgZGVmaW5lZCBhcyBwYXJ0IGZvciBNeVJlYWxtIGNvbmZpZ3VyYXRpb24gYW5kICB0aGUgcHJvcGVydHkgYGNvbS5zdW4uaWRlbnRpdHkuYWdlbnRzLmNvbmZpZy5vcmdhbml6YXRpb24ubmFtZSA9IC9NeVJlYWxtYCBpbiBhZ2VudHMgYm9vdHN0cmFwIHByb3BlcnRpZXMgKE9wZW5TU09BZ2VudEJvb3RzdHJhcC5wcm9wZXJ0aWVzKSBpcyBzZXQgdXAgcHJvcGVybHkgdGhlIGdlbmVyYXRlZCBsb2dpbiBVUkwgKGF0IHJ1bnRpbWUpIHdpbGwgbm90IGNvbnRhaW4gdGhlIHRhcmdldGVkIHJlYWxtIHNvIHVzZXIgYXV0aGVudGljYXRpb24gd2lsbCB0YWtlIHBsYWNlIGFnYWluc3QgVG9wIExldmVsIFJlYWxtLg0KKiBQYXNzd29yZCByZWxheSB3aWxsIG5vdCB3b3JrIHdpdGggWFVJIGludGVyZmFjZSBkdWUgdG8gYSBidWcgc29sdmVkIGluIHN1YnNlcXVlbnQgMTIuMC4wIHJlbGVhc2VzIChhdmFpbGFibGUgb25seSB0byBzdWJzY3JpYmVycykgc28gdGhlIHNvbHV0aW9uIGlzIHRvIHN3aXRjaCB0byBjbGFzc2ljIFVJIChbaHR0cHM6Ly9idWdzdGVyLmZvcmdlcm9jay5vcmcvamlyYS9icm93c2UvT1BFTkFNLTU5MjFdKGh0dHBzOi8vYnVnc3Rlci5mb3JnZXJvY2sub3JnL2ppcmEvYnJvd3NlL09QRU5BTS01OTIxKSkuIFNlZSBbaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5hbS8xMi4wLjAvaW5zdGFsbC1ndWlkZS9jaGFwLWN1c3RvbS11aV0oaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5hbS8xMi4wLjAvaW5zdGFsbC1ndWlkZS9jaGFwLWN1c3RvbS11aSkgb24gaG93IHRvIGRpc2FibGUgWFVJLg0KKiBDaGFuZ2luZyAvcmVwYWx5IHRvIC9yZXBsYXkvKiB3aWxsIGFsbG93IHVzIHRvIGludGVncmF0ZSBtb3JlIHRoYW4gb25lIGxlZ2FjeSBhcHBsaWNhdGlvbnMgaW4gdGhlIHN5c3RlbSBieSB1c2luZyBkaWZmZXJlbnQgc3BlY2lmaWMgcm91dGVzIGluIE9wZW5JRzsgdGhlIGlkZWVhIGlzIHRoYXQgYW55IEdFVCB0byA8T3BlbklHLVVSTD4vcmVsYXkvTXlBcHAgd2lsbCB0cmlnZ2VyIHRoZSBzYW1lIHJlcGxheSBwcm9jZXNzIGJ5IHJlZGlyZWN0aW5nIHRoZSB1c2VyIHRvIE9wZW5BTSBsb2dpbiBkaWFsb2c7IHRoZSByZWRpcmVjdCBiYWNrIHRvIE9wZW5JRyB3aWxsIGJlIGF0IHRoZSBzYW1lIFVSTCB3aGljaCBub3cgd2lsbCBiZSBmb3J3YXJkZWQgdG8gT3BlbklHIHdoaWNoIGluIHR1cm4gYmFzZWQgb24gdGhpcyBVUkwgZGlmZmVyZW50IHJvdXRlcyBjYW4gY29tZSBpbnRvIGFjdGlvbiAoc2VlIE9wZW5JRyBzcGVjaWZpY3MgYmVsb3cpLg0KDQojIENvbmZpZ3VyZSBPcGVuSUcNCg0KVGhlcmUgaXMgbm90IG9idmlvdXMgYXQgdGhlIGZpcnN0IGdsYW5jZSBidXQgd2UgbXVzdCBkZWZpbmUgdGhlIGNvbmZpZy5qcyBpbiBjb25maWcgc3ViZGlyZWN0b3J5IG9mIHRoZSBjb25maWd1cmVhZCBPcGVuSUcgYmFzZSBkaXJlY3RvcnkgKHBhcmFtZXRlciAtRG9wZW5pZy5iYXNlLCBzZWUgYWJvdmUpIGFuZCB0aGUgc3BlY2lmaWMgcm91dGVzIGluIGNvbmZpZ1xyb3V0ZXMuDQoNCg0KIyMgY29uZmlnLmpzDQoNCmBgYEpTT04NCnsNCiAgICAiaGFuZGxlciI6IHsNCiAgICAgICAgInR5cGUiOiAiUm91dGVyIiwNCiAgICAgICAgImF1ZGl0IjogImdsb2JhbCIsDQogICAgICAgICJjYXB0dXJlIjogImFsbCINCiAgICB9LA0KICAgICJoZWFwIjogWw0KICAgICAgICB7DQogICAgICAgICAgICAibmFtZSI6ICJMb2dTaW5rIiwNCiAgICAgICAgICAgICJ0eXBlIjogIkNvbnNvbGVMb2dTaW5rIiwNCiAgICAgICAgICAgICJjb25maWciOiB7DQogICAgICAgICAgICAgICAgImxldmVsIjogIkRFQlVHIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9LA0KICAgICAgICB7DQogICAgICAgICAgICAibmFtZSI6ICJKd3RTZXNzaW9uIiwNCiAgICAgICAgICAgICJ0eXBlIjogIkp3dFNlc3Npb24iDQogICAgICAgIH0sDQogICAgICAgIHsNCiAgICAgICAgICAgICJuYW1lIjogIkNsaWVudEhhbmRsZXIiLA0KICAgICAgICAgICAgInR5cGUiOiAiQ2xpZW50SGFuZGxlciINCiAgICAgICAgfSwNCiAgICAgICAgew0KICAgICAgICAgICAgIm5hbWUiOiAiY2FwdHVyZSIsDQogICAgICAgICAgICAidHlwZSI6ICJDYXB0dXJlRGVjb3JhdG9yIiwNCiAgICAgICAgICAgICJjb25maWciOiB7DQogICAgICAgICAgICAgICAgImNhcHR1cmVFbnRpdHkiOiB0cnVlLA0KICAgICAgICAgICAgICAgICJfY2FwdHVyZUV4Y2hhbmdlIjogdHJ1ZQ0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgXQ0KfQ0KYGBgDQoNCiMjIFJvdXRlcyBmb3IgT1RSUw0KDQojIyMgTG9naW4gcm91dGUNCg0KYGBgSlNPTg0Kew0KCSJoYW5kbGVyIjogew0KCQkidHlwZSI6ICJDaGFpbiIsDQoJCSJjb25maWciOiB7DQoJCQkiZmlsdGVycyI6IFsNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIkNyeXB0b0hlYWRlckZpbHRlciIsDQoJCQkJCSJjb25maWciOiB7DQoJCQkJCQkibWVzc2FnZVR5cGUiOiAiUkVRVUVTVCIsDQoJCQkJCQkib3BlcmF0aW9uIjogIkRFQ1JZUFQiLA0KCQkJCQkJImFsZ29yaXRobSI6ICJERVMvRUNCL05vUGFkZGluZyIsDQoJCQkJCQkia2V5IjogInh4eCIsDQoJCQkJCQkia2V5VHlwZSI6ICJERVMiLA0KCQkJCQkJImNoYXJTZXQiOiAidXRmLTgiLA0KCQkJCQkJImhlYWRlcnMiOiBbDQoJCQkJCQkJInBhc3N3b3JkIg0KCQkJCQkJXQ0KCQkJCQl9DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIkFzc2lnbm1lbnRGaWx0ZXIiLA0KCQkJCQkiY29uZmlnIjogew0KCQkJCQkJIm9uUmVxdWVzdCI6IFsNCgkJCQkJCQl7DQoJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS5hdXRoSW5mb1VzZXJuYW1lfSIsDQoJCQkJCQkJCSJ2YWx1ZSI6ICIke2V4Y2hhbmdlLnJlcXVlc3QuaGVhZGVyc1sndXNlcm5hbWUnXVswXX0iDQoJCQkJCQkJfSwNCgkJCQkJCQl7DQoJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS5hdXRoSW5mb1Bhc3N3b3JkfSIsDQoJCQkJCQkJCSJ2YWx1ZSI6ICIke2V4Y2hhbmdlLnJlcXVlc3QuaGVhZGVyc1sncGFzc3dvcmQnXVswXX0iDQoJCQkJCQkJfQ0KCQkJCQkJXQ0KCQkJCQl9DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIkhlYWRlckZpbHRlciIsDQoJCQkJCSJjb25maWciOiB7DQoJCQkJCQkibWVzc2FnZVR5cGUiOiAiUkVRVUVTVCIsDQoJCQkJCQkicmVtb3ZlIjogWw0KCQkJCQkJCSJwYXNzd29yZCIsDQoJCQkJCQkJInVzZXJuYW1lIg0KCQkJCQkJXQ0KCQkJCQl9DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIlN0YXRpY1JlcXVlc3RGaWx0ZXIiLA0KCQkJCQkiY29uZmlnIjogew0KCQkJCQkJIm1ldGhvZCI6ICJQT1NUIiwNCgkJCQkJCSJ1cmkiOiAiaHR0cHM6Ly9vdHJzLWZxZG4vb3Rycy9jdXN0b21lci5wbCIsDQoJCQkJCQkiZm9ybSI6IHsNCgkJCQkJCQkiVXNlciI6IFsNCgkJCQkJCQkJIiR7ZXhjaGFuZ2UuYXV0aEluZm9Vc2VybmFtZX0iDQoJCQkJCQkJXSwNCgkJCQkJCQkiUGFzc3dvcmQiOiBbDQoJCQkJCQkJCSIke2V4Y2hhbmdlLmF1dGhJbmZvUGFzc3dvcmR9Ig0KCQkJCQkJCV0sDQoJCQkJCQkJIkFjdGlvbiI6WyJMb2dpbiJdLA0KCQkJCQkJCSJSZXF1ZXN0ZWRVUkwiOlsiIl0sDQoJCQkJCQkJIkxhbmciOlsiZW4iXSwNCgkJCQkJCQkiVGltZU9mZnNldCI6WyItMTgwIl0NCgkJCQkJCX0NCgkJCQkJfQ0KCQkJCX0NCgkJCV0sDQoJCQkiaGFuZGxlciI6ICJDbGllbnRIYW5kbGVyIg0KCQl9DQoJfSwNCgkiY29uZGl0aW9uIjogIiR7bWF0Y2hlcyhleGNoYW5nZS5yZXF1ZXN0LnVyaS5wYXRoLCAnXi9yZXBsYXkvb3RycycpfSINCn0NCmBgYA0KDQojIyMgRGVmYXVsdCByb3V0ZQ0KDQpgYGBKU09ODQp7DQogICAgImhhbmRsZXIiOiAiQ2xpZW50SGFuZGxlciIsDQoJImNvbmRpdGlvbiI6ICIke21hdGNoZXMoZXhjaGFuZ2UucmVxdWVzdC51cmkucGF0aCwgJ14vb3RycycpfSIsDQoJImJhc2VVUkkiOiAiaHR0cHM6Ly9vdHJzLWZxZG4iDQoNCn0NCmBgYA0KDQojIyBSb3V0ZXMgZm9yIG90aGUgYXBwbGljYXRpb24gKHR3byBzdGVwcyBsb2dpbikNCg0KVGhpcyBhcHBsaWNhdGlvbiBpcyBKU0YgYmFzZWQgc28gdGhlIFBPU1QgYXQgbG9naW4gdmlldyBtdXN0IGhhdmUgdGhlIHNlc3Npb24gY29va2llIHNldC4gVGhlIHNvbHV0aW9uIGlzIHRvIGhhdmUgYSBmaXJzdCBzdGVwIHJlcXVlc3RpbmcgR0VUIGJlZm9yZSBzbyB0aGF0IHRoZSBzZXNzaW9uIGlzIGNyZWF0ZWQuDQoNCiMjIyBMb2dpbiByb3V0ZQ0KDQpgYGBKU09ODQp7DQoJImhlYXAiOiBbDQogICAgICAgIHsNCiAgICAgICAgICAgICJuYW1lIjogIkRpc3BhdGNoSGFuZGxlciIsDQogICAgICAgICAgICAidHlwZSI6ICJEaXNwYXRjaEhhbmRsZXIiLA0KICAgICAgICAgICAgImNvbmZpZyI6IHsNCiAgICAgICAgICAgICAgICAiYmluZGluZ3MiOiBbDQogICAgICAgICAgICAgICAgICAgIHsNCgkJCQkJCSJoYW5kbGVyIjogew0KCQkJCQkJCSJ0eXBlIjogIkNoYWluIiwNCgkJCQkJCQkiY29uZmlnIjogew0KCQkJCQkJCQkiZmlsdGVycyI6IFsNCgkJCQkJCQkJCXsNCgkJCQkJCQkJCQkidHlwZSI6ICJDcnlwdG9IZWFkZXJGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJtZXNzYWdlVHlwZSI6ICJSRVFVRVNUIiwNCgkJCQkJCQkJCQkJIm9wZXJhdGlvbiI6ICJERUNSWVBUIiwNCgkJCQkJCQkJCQkJImFsZ29yaXRobSI6ICJERVMvRUNCL05vUGFkZGluZyIsDQoJCQkJCQkJCQkJCSJrZXkiOiAieHh4IiwNCgkJCQkJCQkJCQkJImtleVR5cGUiOiAiREVTIiwNCgkJCQkJCQkJCQkJImNoYXJTZXQiOiAidXRmLTgiLA0KCQkJCQkJCQkJCQkiaGVhZGVycyI6IFsNCgkJCQkJCQkJCQkJCSJwYXNzd29yZCINCgkJCQkJCQkJCQkJXQ0KCQkJCQkJCQkJCX0NCgkJCQkJCQkJCX0sDQoJCQkJCQkJCQl7DQoJCQkJCQkJCQkJInR5cGUiOiAiQXNzaWdubWVudEZpbHRlciIsDQoJCQkJCQkJCQkJImNvbmZpZyI6IHsNCgkJCQkJCQkJCQkJIm9uUmVxdWVzdCI6IFsNCgkJCQkJCQkJCQkJCXsNCgkJCQkJCQkJCQkJCQkidGFyZ2V0IjogIiR7ZXhjaGFuZ2UuYXV0aEluZm9Vc2VybmFtZX0iLA0KCQkJCQkJCQkJCQkJCSJ2YWx1ZSI6ICIke2V4Y2hhbmdlLnJlcXVlc3QuaGVhZGVyc1sndXNlcm5hbWUnXVswXX0iDQoJCQkJCQkJCQkJCQl9LA0KCQkJCQkJCQkJCQkJew0KCQkJCQkJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS5hdXRoSW5mb1Bhc3N3b3JkfSIsDQoJCQkJCQkJCQkJCQkJInZhbHVlIjogIiR7ZXhjaGFuZ2UucmVxdWVzdC5oZWFkZXJzWydwYXNzd29yZCddWzBdfSINCgkJCQkJCQkJCQkJCX0NCgkJCQkJCQkJCQkJXQ0KCQkJCQkJCQkJCX0NCgkJCQkJCQkJCX0sDQoJCQkJCQkJCQl7DQoJCQkJCQkJCQkJInR5cGUiOiAiSGVhZGVyRmlsdGVyIiwNCgkJCQkJCQkJCQkiY29uZmlnIjogew0KCQkJCQkJCQkJCQkibWVzc2FnZVR5cGUiOiAiUkVRVUVTVCIsDQoJCQkJCQkJCQkJCSJyZW1vdmUiOiBbDQoJCQkJCQkJCQkJCQkicGFzc3dvcmQiLA0KCQkJCQkJCQkJCQkJInVzZXJuYW1lIg0KCQkJCQkJCQkJCQldDQoJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJfSwNCgkJCQkJCQkJCQ0KCQkJCQkJCQkJew0KCQkJCQkJCQkJCSJ0eXBlIjogIlN0YXRpY1JlcXVlc3RGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJtZXRob2QiOiAiR0VUIiwNCgkJCQkJCQkJCQkJInVyaSI6ICJodHRwOi8vbGVnYWN5LWFwcC1mcWRuOjgwODEvTXlBcHAvZmFjZXMvbG9naW4ueGh0bWwiDQoJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJfSwNCgkJCQkJCQkJCXsNCgkJCQkJCQkJCQkidHlwZSI6ICJTd2l0Y2hGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJvblJlc3BvbnNlIjogWw0KCQkJCQkJCQkJCQkJew0KCQkJCQkJCQkJCQkJCSJoYW5kbGVyIjogIkxvZ2luUmVxdWVzdEhhbmRsZXIiDQoJCQkJCQkJCQkJCQl9DQoJCQkJCQkJCQkJCV0NCgkJCQkJCQkJCQl9DQoJCQkJCQkJCQl9LA0KCQkJCQkJCQkJew0KCQkJCQkJCQkJCSJ0eXBlIjogIkVudGl0eUV4dHJhY3RGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJtZXNzYWdlVHlwZSI6ICJyZXNwb25zZSIsDQoJCQkJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS52aWV3U3RhdGV9IiwNCgkJCQkJCQkJCQkJImJpbmRpbmdzIjogWw0KCQkJCQkJCQkJCQkJew0KCQkJCQkJCQkJCQkJCSJrZXkiOiAidmFsdWUiLA0KCQkJCQkJCQkJCQkJCSJwYXR0ZXJuIjoNCgkJCQkJCQkJCQkJCQkJImphdmF4XFwuZmFjZXNcXC5WaWV3U3RhdGVcIlxccy4qdmFsdWU9XCIoLiopXCJcXHMqYXV0b2NvbXBsZXRlPSIsDQoJCQkJCQkJCQkJCQkJInRlbXBsYXRlIjogIiQxIg0KCQkJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJCQldDQoJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJfSwNCgkJCQkJCQkJCXsNCgkJCQkJCQkJCQkidHlwZSI6ICJBc3NpZ25tZW50RmlsdGVyIiwNCgkJCQkJCQkJCQkiY29uZmlnIjogew0KCQkJCQkJCQkJCQkib25SZXNwb25zZSI6IFsNCgkJCQkJCQkJCQkJCXsNCgkJCQkJCQkJCQkJCQkidGFyZ2V0IjogIiR7ZXhjaGFuZ2Uuc2Vzc2lvbkNvb2tpZX0iLA0KCQkJCQkJCQkJCQkJCSJ2YWx1ZSI6ICIke3NwbGl0KGV4Y2hhbmdlLnJlc3BvbnNlLmhlYWRlcnNbJ1NldC1Db29raWUnXVswXSwnOycpWzBdfSINCgkJCQkJCQkJCQkJCX0NCgkJCQkJCQkJCQkJXQ0KCQkJCQkJCQkJCX0NCgkJCQkJCQkJCX0NCg0KCQkJCQkJCQldLA0KCQkJCQkJCQkiaGFuZGxlciI6ICJDbGllbnRIYW5kbGVyIg0KCQkJCQkJCX0NCgkJCQkJCX0NCiAgICANCgkJCQkJfQ0KCQkJDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQogICAgICAgICAgICAibmFtZSI6ICJMb2dpblJlcXVlc3RIYW5kbGVyIiwNCiAgICAgICAgICAgICJ0eXBlIjogIkNoYWluIiwNCiAgICAgICAgICAgICJjb25maWciOiB7DQogICAgICAgICAgICAgICAgImZpbHRlcnMiOiBbDQoJCQkJDQoJCQkJew0KCQkJCQkidHlwZSI6ICJTdGF0aWNSZXF1ZXN0RmlsdGVyIiwNCgkJCQkJImNvbmZpZyI6IHsNCgkJCQkJCSJtZXRob2QiOiAiUE9TVCIsDQoJCQkJCQkidXJpIjogImh0dHA6Ly9sZWdhY3ktYXBwLWZxZG46ODA4MS9NeUFwcC9mYWNlcy9sb2dpbi54aHRtbCIsDQoJCQkJCQkiZm9ybSI6IHsNCgkJCQkJCQkibG9naW5Gb3JtOmpfdXNlcm5hbWUiOiBbDQoJCQkJCQkJCSIke2V4Y2hhbmdlLmF1dGhJbmZvVXNlcm5hbWV9Ig0KCQkJCQkJCV0sDQoJCQkJCQkJImxvZ2luRm9ybTpqX3Bhc3N3b3JkIjogWw0KCQkJCQkJCQkiJHtleGNoYW5nZS5hdXRoSW5mb1Bhc3N3b3JkfSINCgkJCQkJCQldLA0KCQkJCQkJCSJsb2dpbkZvcm0iOlsibG9naW5Gb3JtIl0sDQoJCQkJCQkJImxvZ2luRm9ybTpqX2lkdDE3IjpbIiJdLA0KCQkJCQkJCSJqYXZheC5mYWNlcy5WaWV3U3RhdGUiOlsiJHtleGNoYW5nZS52aWV3U3RhdGUudmFsdWV9Il0NCgkJCQkJCX0sDQoJCQkJCQkiaGVhZGVycyI6IHsNCgkJCQkJCQkiQ29va2llIjogWyIke2V4Y2hhbmdlLnNlc3Npb25Db29raWV9Il0NCgkJCQkJCX0NCgkJCQkJfQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkgInR5cGUiOiAiSGVhZGVyRmlsdGVyIiwNCgkJCQkJICJjb25maWciOiB7DQoJCQkJCQkgIm1lc3NhZ2VUeXBlIjogIlJFU1BPTlNFIiwNCgkJCQkJCSAiYWRkIjogew0KCQkJCQkJCSAiU2V0LUNvb2tpZSI6IFsgIiR7ZXhjaGFuZ2Uuc2Vzc2lvbkNvb2tpZX07IHBhdGg9L015QXBwIiBdDQoJCQkJCQkgfQ0KCQkJCQkgfQ0KCQkJCX0NCiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJoYW5kbGVyIjogIkNsaWVudEhhbmRsZXIiDQoJCQl9DQogICAgICAgIH0NCgldLA0KCSJoYW5kbGVyIjogIkRpc3BhdGNoSGFuZGxlciIsDQoJImNvbmRpdGlvbiI6ICIke21hdGNoZXMoZXhjaGFuZ2UucmVxdWVzdC51cmkucGF0aCwgJ14vcmVwbGF5L015QXBwJyl9Ig0KfQ0KYGBgIA0KDQojIyMgRGVmYXVsdCByb3V0ZQ0KDQpgYGBKU09ODQp7DQogICAgImhhbmRsZXIiOiAiQ2xpZW50SGFuZGxlciIsDQoJImNvbmRpdGlvbiI6ICIke21hdGNoZXMoZXhjaGFuZ2UucmVxdWVzdC51cmkucGF0aCwgJ14vTXlBcHAnKX0iLA0KCSJiYXNlVVJJIjogImh0dHA6Ly9sZWdhY3ktYXBwLWZxZG46ODA4MSINCg0KfQ0KYGBgDQoNCiMgU2V2ZXJhbCByZW1hcmtzDQoNCkFzIEkgZGVjaWRlZCB0byBhYmFuZG9uIHRoaXMgc29sdXRpb24gdGhlcmUgd2VyZSB0aGluZ3MgdW5jb25maWd1cmVkIG9yIG1pc3Njb25maWd1cmVkLiBKdXN0IHRvIG1lbnRpb24gdHdvOg0KKiBMb2dvdXQgcHJvY2VzcyAoeW91IG1pZ2h0IGVuZCB1cCB3aXRoIHRoZSBzaW11bGF0aW9uIG9mIFNTTyBzY3Jld2VkIHVwIGlmIHRoZSB1c2VycyBsb2dvdXQgb2Ygb25lIHRoZSBhcHBsaWNhdGlvbiB0cnlpbmcgdG8gcmUtbG9nLWluIGFzIGEgZGlmZmVyZW50IHVzZXIpDQoqIFNuaWZmaW5nIHRoZSBuZXR3b3JrIGR1cmluZyB0ZXN0cyBJIG5vdGljZWQgYW5ub3VuY2VtZW50IChQT1NUcyBpZiBJIHJlbWVtYmVyIGNvcmVjdGx5KSBmcm9tIE9wZW5BTSB0byBKMkVFIGFnZW50LCBtZXNzYWdlcyBub3QgaW50ZXJjZXB0ZWQgYnkgdGhlIGFnZW50IGFuZCBmb3J3YXJkZWQgKG9yIGRyb3BwZWQsIGRlcGVuZGluZyBvbiBkZWZhdWx0IHJvdXRlciBjb25maWd1cmF0aW9uKSBieSBPcGVuSUc=