10

I am trying to deploy keycloak using docker image (https://hub.docker.com/r/jboss/keycloak/ version 4.5.0-Final) and facing an issue with setting up SSL.

According to the docs

Keycloak image allows you to specify both a private key and a certificate for serving HTTPS. In that case you need to provide two files:

tls.crt - a certificate tls.key - a private key Those files need to be mounted in /etc/x509/https directory. The image will automatically convert them into a Java keystore and reconfigure Wildfly to use it.

I followed the given steps and provided the volume mount setting with a folder with the necessary files (tls.crt and tls.key), But I am facing issues with SSL handshake, getting

ERR_SSL_VERSION_OR_CIPHER_MISMATCH

error, blocking keycloak load in browser when trying to access it.

I have used letsencrypt to generate pem files and used openssl to create .crt and .key files. Also tried just openssl to create those files to narrow down issue and the behavior is same(some additional info if this should matter)

By default, when I simply specify just the port binding -p 8443:8443 without specifying the cert volume mount /etc/x509/https the keycloak server generates a self signed certificate and I don't see issue in viewing the app in browser

I guess this might be more of a certificate creation issue than anything specific to keycloak, But, unsure how to get this to working. Any help is appreciated

Vsoma
  • 483
  • 1
  • 5
  • 15
  • Using ```ports: - 8443:8443``` in the docker-compose but without mounting the `/etc/x509/https` volume worked for me. Keycloak created his own certs... – Doctor Jun 28 '19 at 10:19
  • 1
    Yes, But we want our certificates (for Ex: LetsEncrypt cert) to be in place – Vsoma Jul 02 '19 at 01:25
  • yeah :-)... But I needed some simple HTTPS availble and your solution of letting keycloak auto generate them was perfect to begin with ! By the way wouldn't you know where the certs are being generated ? Now Kubernetes needs the public key to communicate with Keycloak... – Doctor Jul 02 '19 at 08:02
  • You can exec to find it out of course, But, more easily just downloading it through browser and importing it to the trust store should work – Vsoma Jul 05 '19 at 02:44

3 Answers3

16

I also faced the issue of getting an ERR_SSL_VERSION_OR_CIPHER_MISMATCH error, using the jboss/keycloak Docker image and free certificates from letsencrypt. Even after considering the advices from the other comments. Now, I have a working (and quite easy) setup, which might also help you.

1) Generate letsencrypt certificate

At first, I generated my letsencrypt certificate for domain sub.example.com using the certbot. You can find detailed instructions and alternative ways to gain a certificate at https://certbot.eff.org/ and the user guide at https://certbot.eff.org/docs/using.html.

$ sudo certbot certonly --standalone
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c' to cancel): sub.example.com
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for sub.example.com
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/sub.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/sub.example.com/privkey.pem
   Your cert will expire on 2020-01-27. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

2) Prepare docker-compose environment

I use docker-compose to run keycloak via docker. The config and data files are stored in path /srv/docker/keycloak/.

  • Folder config contains the docker-compose.yml
  • Folder data/certs contains the certificates I generated via letsencrypt
  • Folder data/keycloack_db is mapped to the database container to make its data persistent.

Put the certificate files to the right path

When I first had issues using the original letscrypt certificates for keycloak, I tried the workaround of converting the certificates to another format, as mentioned in the comments of the former answers, which also failed. Eventually, I realized that my problem was caused by permissions set to the mapped certificate files.

So, what worked for me is to just to copy and rename the files provided by letsencrypt, and mount them to the container.

$ cp /etc/letsencrypt/live/sub.example.com/fullchain.pem /srv/docker/keycloak/data/certs/tls.crt
$ cp /etc/letsencrypt/live/sub.example.com/privkey.pem /srv/docker/keycloak/data/certs/tls.key
$ chmod 755 /srv/docker/keycloak/data/certs/
$ chmod 604 /srv/docker/keycloak/data/certs/*

docker-compose.yml

In my case, I needed to use the host network of my docker host. This is not best practice and should not be required for your case. Please find information about configuration parameters in the documentation at hub.docker.com/r/jboss/keycloak/.

version: '3.7'

networks:
  default:
    external:
      name: host

services:
  keycloak:
    container_name: keycloak_app
    image: jboss/keycloak
    depends_on:
      - mariadb
    restart: always
    ports:
      - "8080:8080"
      - "8443:8443"
    volumes:
      - "/srv/docker/keycloak/data/certs/:/etc/x509/https"   # map certificates to container
    environment:
      KEYCLOAK_USER: <user>
      KEYCLOAK_PASSWORD: <pw>
      KEYCLOAK_HTTP_PORT: 8080
      KEYCLOAK_HTTPS_PORT: 8443
      KEYCLOAK_HOSTNAME: sub.example.ocm
      DB_VENDOR: mariadb
      DB_ADDR: localhost
      DB_USER: keycloak
      DB_PASSWORD: <pw>
    network_mode: host

  mariadb:
    container_name: keycloak_db
    image: mariadb
    volumes:
      - "/srv/docker/keycloak/data/keycloak_db:/var/lib/mysql"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: <pw>
      MYSQL_DATABASE: keycloak
      MYSQL_USER: keycloak
      MYSQL_PASSWORD: <pw>
    network_mode: host

Final directory setup

This is how my final file and folder setup looks like.

$ cd /srv/docker/keycloak/
$ tree
.
├── config
│   └── docker-compose.yml
└── data
    ├── certs
    │   ├── tls.crt
    │   └── tls.key
    └── keycloak_db

Start container

Finally, I was able to start my software using docker-compose.

$ cd /srv/docker/keycloak/config/
$ sudo docker-compose up -d

We can see the mounted certificates within the container.

$ cd /srv/docker/keycloak/config/
$ sudo docker-compose up -d

We can doublecheck the mounted certificates within the container.

## open internal shell of keycloack container
$ sudo docker exec -it keycloak_app /bin/bash

## open directory of certificates
$ cd /etc/x509/https/
$ ll
-rw----r-- 1 root root 3586 Oct 30 14:21 tls.crt
-rw----r-- 1 root root 1708 Oct 30 14:20 tls.key

Considerung the setup from the docker-compose.yml, keycloak is now available at https://sub.example.com:8443

Kiwi
  • 303
  • 2
  • 5
  • Yes, I have figured that out later, it needed quite a bit of research! Finally, when my goal was to deploy everything onto GKP, I just ended up using ManagedCertificate, which cleanly handles the certificates and renewal!! – Vsoma Mar 06 '20 at 00:20
  • Using the mounted data/certs folder described in this answer, I was able to get it to work by setting the tls.* permission to 655, running docker, then changing the permission back to 600. Based on the answer in [this question](https://stackoverflow.com/questions/58137934/keycloak-from-docker-letsencrypt-cert-and-err-ssl-version-or-cipher-mismatch) – shaqb4 Oct 04 '20 at 23:43
  • just like @shaqb4 said, I too had to `chmod 655 /srv/docker/keycloak/data/certs/*` or "Creating HTTPS keystore via OpenShift's service serving x509 certificate secrets" failed – Rocco Jan 29 '21 at 20:38
6

After some research the following method worked (for self-signed certs, I still have to figure out how to do with letsencrypt CA for prod)

generate a self-signed cert using the keytool

keytool -genkey -alias localhost -keyalg RSA -keystore keycloak.jks -validity 10950

convert .jks to .p12

keytool -importkeystore -srckeystore keycloak.jks -destkeystore keycloak.p12 -deststoretype PKCS12

generate .crt from .p12 keystore

openssl pkcs12 -in keycloak.p12 -nokeys -out tls.crt

generate .key from .p12 keystore

openssl pkcs12 -in keycloak.p12 -nocerts -nodes -out tls.key

Then use the tls.crt and tls.key for volume mount /etc/x509/https

Also, on the securing app, in the keycloak.json file specify the following properties

"truststore" : "path/to/keycloak.jks",
"truststore-password" : "<jks-pwd>",
Vsoma
  • 483
  • 1
  • 5
  • 15
  • I got it working with letsencrypt certificates too, it just needs the covertion of .pem files to .crt and .key files with an intermediate transformation to .pk12. – Vsoma Oct 16 '18 at 12:00
  • Hi, how did you do that? I have same problem but creating .crt and .key with intermediate .p12 didn't worked, I'm still getting ERR_SSL_VERSION_OR_CIPHER_MISMATCH Thanks – lbaggi Jul 31 '19 at 15:49
  • Try this 1) openssl pkcs12 -export -out pkcskeystore.pkcs12 -in cert.pem -inkey privkey.pem 2) keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcskeystore.pkcs12 -srcstoretype PKCS12 3) Then do from "convert .jks to .p12" part in the answer – Vsoma Aug 05 '19 at 02:24
  • I'm also having problems with that. Unfortunately, all your answers didn't work for me. Do you have any other ideas? I'm also getting ERR_SSL_VERSION_OR_CIPHER_MISMATCH. I'd be very grateful! And where is this keycloak.json file? – Bloodlex Sep 27 '19 at 07:28
5

For anyone who is trying to run Keycloak with a passphrase protected private key file:

Keycloak runs the script /opt/jboss/tools/x509.sh to generate the keystore based on the provided files in /etc/x509/https as described in https://hub.docker.com/r/jboss/keycloak - Setting up TLS(SSL).

This script takes no passphrase into account unfortunately. But with a little modification at Docker build time you can fix it by yourself: Within your Dockerfile add:

RUN sed -i -e 's/-out "${KEYSTORES_STORAGE}\/${PKCS12_KEYSTORE_FILE}" \\/-out "${KEYSTORES_STORAGE}\/${PKCS12_KEYSTORE_FILE}" \\\n      -passin pass:"${SERVER_KEYSTORE_PASSWORD}" \\/' /opt/jboss/tools/x509.sh

This command modifies the script and appends the parameter to pass in the passphrase -passin pass:"${SERVER_KEYSTORE_PASSWORD}"

The value of the parameter is an environment variable which you are free to set: SERVER_KEYSTORE_PASSWORD

Tested with Keycloak 9.0.0

cbopp
  • 423
  • 1
  • 5
  • 8