Single Sign-On Part 1 - OpenLDAP
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:
* Name: cloudflare-apikey-secret
* Key: apikey
* Value: [Cloudflare API key](https://dash.cloudflare.com)
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](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.