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/
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
- Select "Sign Assertions"
- Select "Force Name ID Format".
- Valid Redirect URIs: https://localhost:9443/*
- Archive Format: JKS
- Key Alias: mytomrpkey
- Store password: tompass
- Import file: rp-ssl-key.jks
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" />
- 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/
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>
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
4) Testing the service
To test the service navigate to:
- https://localhost:9443/fedizhelloworld/secure/fedservlet
Hi Colm,
ReplyDeleteYou 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
Try turning on debug logging and see what the following line in AuthnRequestParser says is the cause of the exception:
ReplyDeleteLOG.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
Colm,
ReplyDeleteThe 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
Hi Colm,
ReplyDeleteI got HTTP Status 401 – Unauthorized when I logged in as alice, Did I miss any point?
Regards,
Hi Ahmad,
DeleteI 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
<roleURI>Role</roleURI>
Delete<claimTypesRequested>
<claimType type="Role" optional="false" />