Many of the services I deploy will require authentication of some type. Rather than maintaining a separate set of credentials for each one, I want to use single-sign on (SSO).To do this, I will deploy OpenLDAP and Keycloak. Some services may be able to utilize OpenLDAP directly, but most of the ones that can't should be able to support one of the protocols supported by Keycloak such as OpenID, OAuth, and SAML.

OpenLDAP

Before I install OpenLDAP, I need to take care of a few pre-requisites:

  • Create a namespace
  • Create a certificate using Cert-Manager
  • Create a persistent volume and a volume claim
  • Create a secret for the ldap admin and config passwords

The namespace is easy. In the default project, create a namespace (Namespace..Add Namespace) called 'ldap'. Because this service will not be exposed to the outside with an Ingress rule, I need to use the DNS01 challenge with Cloudflare DNS.

After creating a CNAME record for ldap.domain.tld in Cloudflare, I need to create a new Let's Encrypt certificate issuer within the ldap namespace by importing this YAML using kubectl or Workload...Import YAML...Namespace...ldap:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt-ldap
  namespace: ldap
spec:
    acme:
      # You must replace this email address with your own.
      email: name@domain.tld
      server: https://acme-v02.api.letsencrypt.org/directory
      privateKeySecretRef:
        # Secret resource used to store the account's private key.
        name: letsencrypt-ldap-privatekey
      # Add a DNS Challenge with Cloudflare
      solvers:
      - dns01:
          cloudflare:
            email: Cloudflare_Email
            apiKeySecretRef:
              name: cloudflare-apikey-secret
              key: apikey
        selector:
          dnsNames:
          - 'ldap.domain.tld'

Now, I create a secret for  the cloudflare API key in the ldap namespace:

Then create a certificate with this YAML:

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: ldap-cert
  namespace: ldap
spec:
  secretName: ldap-tls
  issuerRef:
    name: letsencrypt-ldap
  commonName: 'ldap.domain.tld'
  keyAlgorithm: rsa
  keySize: 4096
  dnsNames:
  - ldap.domain.tld

Once succesfull, the new certificate will appear under Secrets..Certificates.

I create a persistent volume using Cluster..Storage..Persistent volumes and Add Volume:

  • Name - openldap
  • Volume Plugin - NFS share
  • Capacity - 10GiB
  • Path - /Container/openldap
  • Server - 192.168.xx.xx
  • Read Only - No
  • Access Modes - Many Nodes Read Write

Then under Resources.. Workloads..Volume..Add Volume, create a new claim:

  • Name - ldap-vol
  • Namespace - ldap
  • Persistent volume - openldap

Now create a new secret for the passwords (Resources..Secrets):

  • Name - ldap-admin-pass
  • Namespace - ldap
  • Key - LDAP_ADMIN_PASSWORD with a strong password as the value
  • Key - LDAP_CONFIG_PASSWORD with a strong password as the value

Now to install using Helm using the Rancher Apps section (or command line helm):

  • Under Apps, Manage Catalogs and add https://github.com/helm/charts as a Global catalog
  • Launch catalog and search for OpenLDAP
  • Name - openldap
  • Namespace - (select existing namespace) ldap

Add the following answer (parameter) keys and values before clicking Launch:

  • existingSecret - ldap-admin-pass
  • tls.enabled - true
  • tls.secret - ldap-tls
  • persistence.enabled - true
  • persistence.accessMode - ReadWriteMany
  • persistence.existingClaim - ldap-vol

Once successfully launched, I need to modify some of the configuration parameters by going to Resources..Config and edit openldap-env:

  • LDAP_DOMAIN: domain.tld
  • LDAP_ORGANISATION: domain

Under Resources..Workloads, edit the workloads and add a new environment variable:

  • LDAP_TLS_VERIFY_CLIENT - try

Under Resources..Service Discovery, edit and show advanced options the add an external IP of one of the nodes. I use kubein because the LDAP port is not forwarded so it's not exposed to the Internet. Note that if this node goes down, it will still be accessible using it's internal name: openldap.ldap.svc.cluster.local.

To test and debug my new ldap service, I will deploy osixia/phpldapadmin:

  • Name: ldapadmin
  • Docker Image: osixia/phpldapadmin
  • Port mapping: web, 80, TCP, ClusterIP, Same as Container
  • Environment variable: PHPLDAPADMIN_LDAP_HOSTS set to openldap.ldap.svc.cluster.local
  • Environment variable: PHPLDAPADMIN_LDAP_HTTPS set to  false NGINX will take care of that

Do the magic with the ingress rules and letsencrypt so that I can then go to https://ldapadmin.domain.tld and login using the username "cn=admin,dc=domain,dc=tld" with the admin password which I set in the password secret.

I now have a a working LDAP directory which I can access with a web-based interface or using CLI by instinall ldap-tools package and using the ldapwhoami command. I can use "cn=admin,dc=domain,dc=tld" to manage the users in the system, but I will have to use the config user as cn=config to make configuration changs. The easiest way to do that is with root access on the openldap container iself.

I need to create a read-only user to enable applications to be able to search for users and determine whether they should have access or not. To do this:

  • In phpldapadmin, login as admin
  • On the left side, press Create New Entry
  • Select the Generic: Organisational Role template
  • Under Role CN, enter read. This will become the user name. (CN = common name)
  • Hit Create Object at the bottom
  • There should now be a cn=read object
  • On the cn=read page, under objectClass press add value
  • Add the simpleSecurityObject object class
  • The object page will now have an entry for a password, enter a password, select  sha512crypt next to it and press update
  • On next page, confirm update

The only way to grant any privileges to the read user is by using a file which adds olcAccess using the ldapmodify command and cn=config. The file is placed onto the data directory of the persistent storage which can be accessed via /var/lib/ldap from a container shell:

The file.ldif below will specifically add the olcAccess:

dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to *
  by dn="cn=read,dc=domain,dc=tld" read
root@openldap-8564f64cff-78gvt:/var/lib/ldap# ldapmodify -f file.ldif -Y EXTERNAL -H ldapi:///
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "olcDatabase={1}hdb,cn=config"

Run this command:

root@openldap-8564f64cff-78gvt:/var/lib/ldap# slapcat -b cn=config

and look for a line similar to this to verify that the read user now has access to everything:

olcAccess: {1}to * by dn="cn=read,dc=domain,dc=tld" read

When connecting an app to LDAP, I will provide it a bind user using the read credentials for the read user for it to verify the user for that app.

In the next post, I will set up Keycloak.