Keycloak provides single sign-on services using multiple protocols and provides a proxy which can be used to add SSO for applications which don't natively support those protocols. It should be a valuable addition to a self-hosted Kubernetes cluster. It will use the OpenLDAP service which I installed previously as its credentials database and can be used as an interface to manage LDAP users. It can create users in LDAP when the users are added to Keycloak.

Namespace

Keycloak is going to get it's own namespace called keycloak in the default project.

Database

Keycloak will need a persistent database. For this, I will install a single MariaDB workload.

First, I need a new secret in the keycloak namespace called keycloakdb-secret with the following keys containing passwords as values:

  • MYSQL_DATABASE - keycloak
  • MYSQL_PASSWORD - <random password>
  • MYSQL_USER - keycloak
  • MYSQL_ROOT_PASSWORD - <random password>

Next, I need a keycloakdb persistent volume claim which will be mounted in the container at /var/lib/mysql. I use the same process as I did for OpenLDAP on the QNAP /Container share. The cluster storage volume is keycloakdb and the volume claim is called keycloadb-vol.

Next, I will deploy a new workload in the keycloak namespace specifying a specific tag from Docker Hub. Using latest is discouraged because it can randomly get updated when it restarts. At this writing, I'm using 10.4.12-bionic.

  • Name - keycloakdb
  • Docker Image - mariadb:10.4.12-bionic
  • Port Mapping - mariadb, 3306, TCP, ClusterIP
  • Environmental Variables - add external keycloakdb-secret
  • Volumes - add existing claim keycloakdb-vol as /var/lib/mysql

Keycloak

Next, I will install the Keycloak workload using a similar process in the keycloak namespace. No persistent volume is needed for keycloak itself.

The keycloak-secret needs keys for all of the password entries below and the rest can be just plain environment variables, but I like keeping everything together.

  • DB_VENDOR - mariadb
  • KEYCLOAK_USER - keycloak
  • KEYCLOAK_PASSWORD - <random password>
  • DB_ADDR - keycloakdb.keycloak.svc.cluster.local
  • DB_PORT - 3306
  • DB_USER - keycloak
  • DB_PASSWORD - <same as MYSQL_PASSWORD in keycloakdb-secret>
  • PROXY_ADDRESS_FORWARDING - true

Next, I need a new deploy:

  • Name - keycloak
  • Docker Image - jboss/keycloak:9.0.2 (as of this post)
  • Namespace - keycloak
  • Port mapping - web,8080,TCP,ClusterIP
  • Environment variables - use keycloak-secret

The last part to configure is the ingress rule with a Let's Encrypt certificate as I've done for other workloads. I made this funtional as https://sso.domain.tld. Accessing the new URL successfully shows the Keycloak page with a link tot he Administration Console.

Configure Keycloak for SSO

Following the Keycloak documentation, I first need to login to the Keycloak administrative console by using the KEYCLOAK_USER and KEYCLOAK_PASSWORD.

A realm manages a set of users, credentials, roles, and groups.  A user  belongs to and logs into a realm.  Realms are isolated from one another and can only manage and authenticate the users that they control. The Master realm provides for roles and permissions for managing Keycloak and creating, deleting, and modifying other realms.

Since I'm hosting services for multiple domains, I'm assuming that I will need a separate domain for each groups of users. Either way, I need a realm to get started. I made sure to enable "User-Managed Access" to allow users to set up their own two-factor authentication, e-mail address, password, and other settings. I also enabled a 10 minute wait after 10 failed login attempts in Security Defenses under Brute Force Detection.

Connect LDAP

Under User Federation for the new realm that I created, I can add a new conncetion to LDAP (hover over help is available for each option):

  • Console Display Name - Domain LDAP
  • Priority - 0
  • Import Users - On
  • Edit Mode - WRITABLE
  • Sync Registrations - On
  • Vendor - Other
  • Username LDAP attribute - uid
  • RDN LDAP atrribute - uid
  • UUID LDAP attribute - entryUUID
  • User Object Classes - inetOrgPerson, organizationalPerson
  • Connection URL - ldap://openldap.ldap.svc.cluster.local (NOTE: I could use ldaps://ldap.domain.tld since I do have a TLS certificate for it, but that would mean port forwarding or dealing with split DNS.)
  • Users DN - dc=domain,dc=tld
  • Bind Type - simple
  • Enable StartTLS - off (See note above)
  • BindDN - cn=admin,dc=domain,dc=tld
  • Bind Credential - <Admin user password>
  • Custom User LDAP Filter - empty
  • Search Scope - One Level
  • Validate Password Policy - True
  • Trust Email - Off
  • Connection Pooling - On
  • Conncetion Timeout - empty
  • Read Timeout - empty
  • Pagination - On
  • Allow Kerberos  authentication - Off
  • Use Kerberos For Password Authentication - Off
  • Batch Size - 100
  • Periodic Full Sync - On
  • Full Sync Period - 600
  • Period Changed Users Sync - On
  • Changed Users Sync Period - 600
  • Cache Policy - DEFAULT

Configure Email

Of course, an important step was to configure e-mail. I set up sso@domain.tld in order to send password password resets and Keycloak can verify e-mail addresses. I can also require two-factor authentication.

Create Users

The last step is to configure some  users and make sure they can login to the domain.

Next, I will set up the first real service which brings all of these pieces together.