Friday, May 18, 2018

SAML SSO support for the Apache CXF Fediz plugins

Apache CXF Fediz originated as a way of securing web applications using Single Sign-On via the WS-Federation Passive Requestor Profile. Plugins were written to support the most popular web application containers, such as Apache Tomcat, Jetty, Spring, Websphere, etc. Fediz then shipped an IdP which could be used to perform authentication using the container plugins. For the 1.3.0 release, support was added to the IdP to also support the SAML SSO protocol. From the next 1.4.4 release, the Tomcat 8 plugin can authenticate to a SAML SSO IdP, instead of using WS-Federation, with a few simple configuration changes. This makes it very easy to upgrade your Fediz-secured containers to use SAML SSO instead of WS-Federation.

In this article, we will secure the 'fedizhelloworld' application example that ships with Fediz, which is deployed in Apache Tomcat, using Keycloak as the SAML SSO IdP. We will show how to deploy and secure the application both manually and also by a docker image that I have created for quick deployment.

1) Download and configure Keycloak

Download and install the latest Keycloak distribution (tested with 3.4.3).

1.1) Create users in Keycloak


Start keycloak in standalone mode by running 'sh bin/standalone.sh'. First we need to create an admin user by navigating to the following URL, and entering a password:
  • http://localhost:8080/auth/
Click on the "Administration Console" link, logging on using the admin user credentials. You will see the configuration details of the "Master" realm. For the purposes of this demo, we will create a new realm. Hover the mouse pointer over "Master" in the top left-hand corner, and click on "Add realm". Create a new realm called "fediz-samlsso". Now we will create a new user in this realm. Click on "Users" and select "Add User", specifying "alice" as the username. Click "save" and then go to the "Credentials" tab for "alice", and specify a password, unselecting the "Temporary" checkbox, and reset the password.

1.2) Create a new client application in Keycloak

Now we will create a new client application for 'fedizhelloworld' in Keycloak. Select "Clients" in the left-hand menu, and click on "Create". Specify the following values:
  • Client ID: urn:org:apache:cxf:fediz:fedizhelloworld
  • Client protocol: saml
  • Client SAML Endpoint: https://localhost:9443/fedizhelloworld/secure
Once the client is created you will see more configuration options:
  • Select "Sign Assertions"
  • Select "Force Name ID Format".
  • Valid Redirect URIs: https://localhost:9443/*
Click 'Save'. Now go to the "SAML Keys" tab of the newly created client. Here we will have to import the certificate of the Fediz RP so that Keycloak can validate the signed SAML requests. Click "Import" and specify:
  • Archive Format: JKS
  • Key Alias: mytomrpkey
  • Store password: tompass
  • Import file: rp-ssl-key.jks
1.3) Export the Keycloak signing certificate

Finally, we need to export the Keycloak signing certificate so that the Fediz plugin can validate the signed SAML Response from Keycloak. Select "Realm Settings" (for "fediz-samlsso") and click on the "Keys" tab. Copy and save the value specified in the "Certificate" textfield. 

2) Manually configure 'fedizhelloworld' application in Apache Tomcat

In this section, we'll look at manually configuring the 'fedizhelloworld' application in Apache Tomcat. To use a docker image skip to the next section. Download and extract Apache Tomcat 8 (tested with 8.5.32) to ${catalina.home}. Download Fediz 1.4.4 and build the source with "mvn clean install -DskipTests". Copy 'apache-fediz/target/apache-fediz-1.4.4' to a new directory (${fediz.home}).

2.1) Secure the Apache Tomcat container with the Fediz plugin

First we will secure the Apache Tomcat container with the Fediz plugin:
  • Create a new directory: ${catalina.home}/lib/fediz
  • Edit ${catalina.home}/conf/catalina.properties and append ',${catalina.home}/lib/fediz/*.jar' to the 'common.loader' property.
  • Copy ${fediz.home}/plugins/tomcat8/lib/* to ${catalina.home}/lib/fediz
  • Edit the TLS Connector in ${catalina.home}/conf/server.xml', and change the ports to avoid conflict with Keycloak, i.e. switch 8080 to 9080, 8443 to 9443, etc.
  • In the same file, add configuration for the TLS port: <Connector port="9443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="rp-ssl-key.jks" keystorePass="tompass" />
2.2) Deploy 'fedizhelloworld' to Tomcat
  • Do a "mvn clean install" in ${fediz.home}/examples/simpleWebapp
  • Copy ${fediz.home}/examples/simpleWebapp/target/fedizhelloworld.war to ${catalina.home}/webapps.
  • Copy ${fediz.home}/examples/samplekeys/rp-ssl-key.jks to ${catalina.home}.
  • Copy ${fediz.home}/examples/simpleWebapp/src/main/config/fediz_config.xml to ${catalina.home}/conf/
2.3) Configure 'fediz_config.xml'
 
Now we need to configure '${catalina.home}/conf/fediz_config.xml' that we copied from the Fediz example in the previous section:
  • Under 'contextConfig', specify the key we are using to sign the SAML Request: <signingKey keyAlias="mytomrpkey" keyPassword="tompass">
                <keyStore file="rp-ssl-key.jks" password="tompass" type="JKS" />
            </signingKey>
  • Change the trustManager keystore to: <keyStore file="keycloak.cert" type="PEM" />
  • In the 'Protocol' section, change 'federationProtocolType' to 'samlProtocolType'.
  • Change the 'Issuer' value to: http://localhost:8080/auth/realms/fediz-samlsso/protocol/saml
  • Under 'Protocol' add: <signRequest>true</signRequest and <disableDeflateEncoding>true</disableDeflateEncoding>
Finally, create a file called 'keycloak.cert' in ${catalina.home}. In between "-----BEGIN CERTIFICATE----- / -----END CERTIFICATE-----" tags, paste the Keycloak signing certificate as retrieved in step "1.3" above. 

3) Using a docker image to deploy 'fedizhelloworld'

I have created a simple docker project which can be used to package and deploy the 'fedizhelloworld.war' into Apache Tomcat, which is secured with Fediz. The project is available on github here. Clone the project and build and run with the following steps:
  • Edit 'keycloak.cert' and paste in the signing certificate as retrieved in step 1.3 above.
  • Build with: docker build -t coheigea/fediz-samlsso-rp .
  • Run with: docker run -p 9443:8443 coheigea/fediz-samlsso-rp
At the time of writing, the docker file references a SNAPSHOT build of Fediz, as 1.4.4 is not yet released.

4) Testing the service

To test the service navigate to:
  • https://localhost:9443/fedizhelloworld/secure/fedservlet
You should be redirected to the Keycloak authentication page. Enter the user credentials you have created, and you will be redirected back to the 'fedizhelloworld' application successfully.


6 comments:

  1. Hi Colm,

    You helped me with the problem I had trying to use Apache CXF Fediz plugin/ SAML SSO / Keycloak IdP described here. It works fine. Thanks!

    I also wanted to try Apache CXF Fediz plugin/ SAML SSO with Apache CXF Fediz IdP and noticed you have all the docker images available (https://github.com/coheigea/testcases/tree/master/apache/docker/fediz).

    So, I started the IdP docker containers (fediz-idp) and example app container (fediz-helloworld) built using fediz_config_saml.xml. I am getting 401 Unauthorized error when I access https://localhost:8443/fedizhelloworld/secure/fedservlet and the following exception is thrown on IdP side (snip from idp.log) this time -

    2019-03-27 06:32:40,222 [https-openssl-nio-8443-exec-7] DEBUG org.springframework.webflow.engine.support.TransitionExecutingFlowExecutionExceptionHandler - Handling flow execution exception org.springframework.webflow.execution.ActionExecutionException: Exception thrown executing [AnnotatedAction@e118a1 targetAction = [EvaluateAction@29f2fc65 expression = authnRequestParser.parseSAMLRequest(flowRequestContext, flowScope.idpConfig, flowScope.SAMLRequest, flowScope.SigAlg, flowScope.Signature, flowScope.RelayState), resultExpression = [null]], attributes = map[[empty]]] in state 'parseSAMLRequest' of flow 'saml' -- action execution attributes were 'map[[empty]]'
    org.springframework.webflow.execution.ActionExecutionException: Exception thrown executing [AnnotatedAction@e118a1 targetAction = [EvaluateAction@29f2fc65 expression = authnRequestParser.parseSAMLRequest(flowRequestContext, flowScope.idpConfig, flowScope.SAMLRequest, flowScope.SigAlg, flowScope.Signature, flowScope.RelayState), resultExpression = [null]], attributes = map[[empty]]] in state 'parseSAMLRequest' of flow 'saml' -- action execution attributes were 'map[[empty]]'
    at org.springframework.webflow.execution.ActionExecutor.execute(ActionExecutor.java:60)
    ...
    at org.apache.cxf.fediz.service.idp.service.security.GrantedAuthorityEntitlements.doFilter(GrantedAuthorityEntitlements.java:61)
    ...
    at org.apache.cxf.fediz.service.idp.STSPortFilter.doFilter(STSPortFilter.java:74)
    ...
    Caused by: org.springframework.expression.ExpressionInvocationTargetException: A problem occurred when trying to execute method 'parseSAMLRequest' on object of type [org.apache.cxf.fediz.service.idp.beans.samlsso.AuthnRequestParser]
    at org.springframework.expression.spel.ast.MethodReference.throwSimpleExceptionIfPossible(MethodReference.java:237)
    ...
    Caused by: org.apache.cxf.fediz.core.exception.ProcessingException: The specified request is not understood
    at org.apache.cxf.fediz.service.idp.beans.samlsso.AuthnRequestParser.parseSAMLRequest(AuthnRequestParser.java:160)
    ...

    Can you please help with the interpreting of the exception? Is there anything else that needs to be configured on IdP side maybe besides just starting the docker container?

    Thanks.

    Regards,
    Anton

    ReplyDelete
  2. Try turning on debug logging and see what the following line in AuthnRequestParser says is the cause of the exception:

    LOG.debug("Error validating SAML Signature", ex);

    Did you follow the steps here? http://coheigea.blogspot.com/2018/08/experimenting-with-apache-cxf-fediz-in.html

    ReplyDelete
  3. Colm,

    The post you mentioned helped, I found what I was missing. Certificates again.. Everything works fine now.

    Thanks again for your help and pointing to the right direction!

    Regards,
    Anton

    ReplyDelete
  4. Hi Colm,

    I got HTTP Status 401 – Unauthorized when I logged in as alice, Did I miss any point?

    Regards,

    ReplyDelete
    Replies
    1. Hi Ahmad,
      I got the same unauthorized response using keycloak-6.0.1. After some debugging found a working configuration:
      1. fediz_config.xml
      Role


      2. fediz-samlsso realm
      Roles - Add Role -> Role Name: User
      Users - alice - Role Mappings -> select 'User' from Available Roles and Add selected

      Delete
    2. <roleURI>Role</roleURI>
      <claimTypesRequested>
      <claimType type="Role" optional="false" />

      Delete