Skip to content

Instantly share code, notes, and snippets.

@nmasse-itix
Last active March 11, 2019 16:54
Show Gist options
  • Save nmasse-itix/3237e88e311428228cde755d092a1194 to your computer and use it in GitHub Desktop.
Save nmasse-itix/3237e88e311428228cde755d092a1194 to your computer and use it in GitHub Desktop.
Using a public certificate with Red Hat Single Sign On

Using a public certificate with Red Hat Single Sign On

Context

When deploying Red Hat Single Sign On for a test or PoC, most users will choose to use a self-signed certificate as explained in the official documentation. The setup instructions are straightforward but this self-signed certificate will trigger certificate error messages in your web browser and can also prevent some clients such as Postman from working properly. This guide explains how to get a public certificate for Red Hat Single Sign On.

Are you using a public certificate ?

A simple and effective way to know if you are using a public certificate is to use curl.

$ curl https://sso.example.test/auth/realms/master
curl: (60) SSL certificate problem: self signed certificate in certificate chain
More details here: https://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.
HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.

If you get this output from curl, you are using a self-signed certificate that will cause you headaches later. Continue reading to learn fixing this !

Instructions

During the rest of this article, we will focus on a Red Hat Single Sign On 7.2 installation on OpenShift. We assume Red Hat Single Sign On has been installed as explained in the official documentation

First, move to the project in which you installed Red Hat SSO.

oc project sso

We will retrieve a public certificate from a Certification Authority named Let's Encrypt. Lego is a client that can speak with Let's Encrypt, it has several advantages such as: ease of use, packaged as a container image, etc.

Install Lego in the current project.

oc new-app --name lego xenolf/lego:latest
oc patch dc lego --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["/bin/sh", "-c", "while :; do sleep 1; done" ]}]'
oc expose dc/lego --port=8080

To get a public certificate for your Red Hat Single Sign On instance, we need to find the hostname of that instance. The hostname is a property of the OpenShift route that has been created as part of the Red Hat SSO installation.

oc get route sso

Query the hostname of your Red Hat SSO's route.

hostname=$(oc get route sso -o jsonpath='{.spec.host}')

This route to your Red Hat SSO instance needs to be replaced by a temporary route to Lego so that Let's Encrypt can perform the HTTP challenge. The easiest way to do so, is to delete your existing route and create a new one with the same hostname.

oc delete route sso
oc expose service lego --hostname="$hostname" --name=sso --target-port=8080

You can now trigger the certificate request from the running Lego pod:

pod=$(oc get pods -o name -l app=lego |head -n1)
oc rsh $pod lego --path /tmp/.lego --http :8080 -x dns-01 -x tls-alpn-01 -d "$hostname" --accept-tos run

The first command gets the name of the pod running Lego and sets a shell variable accordingly. The second command runs the lego command from the Lego container. This command has several switches:

  • --path /tmp/.lego will store the generated certificates in /tmp
  • --http :8080 asks Lego to listen on port 8080 to receive the ACME HTTP challenge
  • -x dns-01 -x tls-alpn-01 disables the other challenges that cannot succeed in our environment
  • -d "$hostname" sets the hostname of our SSO instance in the certificate request
  • --accept-tos means you read and accepted the Let's Encrypt Terms of Service
  • run triggers the certificate request

If you did everything correctly, you should see something like this:

2019/01/22 12:03:55 [INFO] [hostname] acme: Obtaining bundled SAN certificate
2019/01/22 12:03:56 [INFO] [hostname] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/[redacted]
2019/01/22 12:03:56 [INFO] [hostname] acme: Could not find solver for: tls-alpn-01
2019/01/22 12:03:56 [INFO] [hostname] acme: Trying to solve HTTP-01
2019/01/22 12:03:56 [INFO] [hostname] Served key authentication
2019/01/22 12:04:01 [INFO] [hostname] The server validated our request
2019/01/22 12:04:01 accept tcp [::]:8080: use of closed network connection
2019/01/22 12:04:01 [INFO] [hostname] acme: Validations succeeded; requesting certificates
2019/01/22 12:04:03 [INFO] [hostname] Server responded with a certificate.

Congratulations, you just issued your first public certificate !

Retrieve the freshly issued certificate from the Lego container and store it somewhere safe:

oc rsync $pod:/tmp/.lego ~/

You should now have the certificate stored in your home folder on your workstation.

$ find ~/.lego/certificates
/Users/redhat/.lego/certificates
/Users/redhat/.lego/certificates/<hostname>.key
/Users/redhat/.lego/certificates/<hostname>.issuer.crt
/Users/redhat/.lego/certificates/<hostname>.json
/Users/redhat/.lego/certificates/<hostname>.crt

You can now delete the temporary route and replace it with a new to route to the SSO pod.

oc delete route sso
oc create -f - <<EOF
apiVersion: v1
kind: Route
metadata:
  name: sso
spec:
  host: $hostname
  to:
    kind: Service
    name: sso
  tls:
    termination: edge
    key: |-
$(sed 's/^/      /' ~/.lego/certificates/$hostname.key)
    certificate: |-
$(sed 's/^/      /' ~/.lego/certificates/$hostname.crt)
    caCertificate: |-
$(sed 's/^/      /' ~/.lego/certificates/$hostname.issuer.crt)
EOF

Confirm that your SSO instance is now using a public certificate by running again the curl command.

curl https://$hostname/auth/realms/master

Lego does not need to run continuously, so between certificate renewals (every 90 days), you can scale it down.

oc scale dc/lego --replicas=0

Conclusion

Self-signed certificates are always a headache when delivering a proof of concept or a workshop. In this article we presented a very practical way to get a valid public certificate for your Red Hat Single Sign On instance.

Some aspects would need improvements to be used on longer periods or in production environments. For instance, we would need to use the proper Kubernetes concepts, Job and Cron to run Lego, handle the certificate renewal, use the DNS validation challenge (which requires a more complex setup but does not involves deleting the sso route).

Also, this article is about getting public certificates for your SSO instance that is publicly deployed on the Internet. If your SSO instance is deployed on your laptop for development purposes, the mkcert project can help you generate proper certificates and make them trusted in your web browser.

Nevertheless I hope this article will give you ideas and entice you to use public certificates for your SSO setups.

@kevprice83
Copy link

@nmasse-itix I have updated the steps here let me know if they look good to you. Some of the steps were specific to the environment we were running in so I guess there could be optional steps here. @lucamaf did I miss anything?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment