Setup OpenLDAP for Charmed Kubeflow

Overview

Charmed Kubeflow delivers a powerful, sophisticated end-to-end MLOps platform which you can deploy in half an hour or less, using MicroK8s or another conformant Kubernetes distribution.

In this tutorial, we will learn how to configure Charmed Kubeflow for multi-user collaboration with LDAP for user authentication.

We’ll be using OpenLDAP for this tutorial.

What you’ll learn

  • How to install and set up an OpenLDAP directory server at the command line
  • How to add user credentials to an OpenLDAP directory server from the command line
  • How to secure access to your OpenLDAP directory server with LDAPS wire encryption and bind credentials
  • How to connect your Charmed Kubeflow MLOps platform to your OpenLDAP directory server

What you’ll need

  • A Kubernetes cluster (eg. MicroK8s running on Ubuntu 20.04 with one or more nodes running Charmed Kubeflow - see the install guide to get up and running
  • An Ubuntu 20.04 server on which to install OpenLDAP
  • Some familiarity with the command line will be helpful

Set environment variables

First, let’s set up some environment variables that we’ll use throughout this tutorial. On the server where you’ll install OpenLDAP, run the following commands. These commands will set up the LDAP domain and the admin password, as well as the remote bind user’s password, so you will want to change them to suit your requirements:

OL_ADMIN_PASS="changemefirst"
OL_BIND_PASS="changemenext"
DOMAIN="example.org"
ORG_NAME="Example Org"
OPENLDAP_HOST="$(hostname -f)"

Install the OpenLDAP server

Now we’re going to install the OpenLDAP server on your target machine. In order to make it straightforward, we’ll use the debconf-utils package to preconfigure the OpenLDAP installer wizard.

sudo apt update
sudo apt install -y debconf-utils
echo "slapd slapd/password1 password $OL_ADMIN_PASS" >> debconf-slapd.conf
echo "slapd slapd/password2 password $OL_ADMIN_PASS" >> debconf-slapd.conf
echo "slapd slapd/move_old_database boolean true" >> debconf-slapd.conf
echo "slapd slapd/domain string $DOMAIN" >> debconf-slapd.conf
echo "slapd shared/organization string $ORG_NAME" >> debconf-slapd.conf
echo "slapd slapd/no_configuration boolean false" >> debconf-slapd.conf
echo "slapd slapd/purge_database boolean false" >> debconf-slapd.conf
echo "slapd slapd/allow_ldap_v2 boolean false" >> debconf-slapd.conf
echo "slapd slapd/backend select MDB" >> debconf-slapd.conf
cat debconf-slapd.conf | sudo debconf-set-selections

Once that’s done, we can go ahead and install the OpenLDAP server, slapd, as well as some tools and scripts to help us manage the server:

sudo apt install -y slapd ldap-utils ldapscripts

Generate a server certificate

Next we want to generate a server certificate so that we can encrypt authentication queries between Charmed Kubeflow and the OpenLDAP server. We’re going to use a self-signed certificate in this tutorial. For production scenarios you might want to use a CA Service, for example Let’s Encrypt; or your enterprise’s own internal certificate authority service. Public key cryptography is built on trust, and self-signed certificates are fundamentally less trustworthy than signed certificates that have a verifiable path to a trusted certificate authority.

Run the following commands to create a self-signed certificate and ready it for OpenLDAP:

sudo openssl req -newkey rsa:4096 -x509 -nodes -out /etc/ldap/$(hostname -f).crt -keyout /etc/ldap/$(hostname -f).key -days 1095 -subj "/CN=$(hostname -f)"
sudo chown openldap:openldap /etc/ldap/$(hostname -f).crt
sudo chown openldap:openldap /etc/ldap/$(hostname -f).key
sudo chown openldap:openldap /etc/ldap/$(hostname -f).crt /etc/ldap/$(hostname -f).key

Enable TLS for OpenLDAP

The next step is to configure OpenLDAP to use our certificate to encrypt communications.

sudo sed -i "s/TLS_CACERT.*/TLS_CACERT\t\/etc\/ldap\/$(hostname -f).crt/g" /etc/ldap/ldap.conf
sudo chown openldap:openldap /etc/ldap/ldap.conf
sudo service slapd restart
sudo mkdir /etc/ldap/scratch

sudo bash -c 'cat > /etc/ldap/scratch/olcTLS.ldif <<EOF
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/$(hostname -f).crt
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/$(hostname -f).crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/$(hostname -f).key
EOF'
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f /etc/ldap/scratch/olcTLS.ldif

Add LDAP directory model

Next, we want to set up our LDAP directory model. Run the following commands to create the organisational hierarchy that will contain our users and groups:

sudo bash -c 'cat > /etc/ldap/scratch/add_ou.ldif <<EOF
dn: ou=people,dc=example,dc=org
objectClass: organizationalUnit
objectClass: top
ou: People

dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
objectClass: top
ou: Groups

dn: ou=system,dc=example,dc=org
objectClass: organizationalUnit
objectClass: top
ou: System
EOF'
sudo ldapadd -x -D 'cn=admin,dc=example,dc=org' -w "$OL_ADMIN_PASS" -H ldapi:/// -f /etc/ldap/scratch/add_ou.ldif

Create a user

In order to be able to secure our OpenLDAP directory server, we need to create a username and password that Charmed Kubeflow can use to authenticate with when it queries OpenLDAP over the network:

sudo bash -c "cat > /etc/ldap/scratch/add_idpuser.ldif <<EOF
dn: cn=idpuser,ou=system,dc=example,dc=org
objectClass: inetOrgPerson
cn: idpuser
sn: idpuser
givenName: idpuser
userPassword: $(slappasswd -s $OL_BIND_PASS)
EOF"
sudo ldapadd -x -D 'cn=admin,dc=example,dc=org' -w "$OL_ADMIN_PASS" -H ldapi:/// -f /etc/ldap/scratch/add_idpuser.ldif

Set bind user account

Now let’s assign the user account we just created to the role of bind user, so that Charmed Kubeflow can use this account to authenticate before querying OpenLDAP:

sudo bash -c 'cat > /etc/ldap/scratch/olcAcl.ldif <<EOF
dn: olcDatabase={1}mdb,cn=config
changeType: modify
replace: olcAccess
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
olcAccess: {1}to attrs=userPassword by self write by anonymous auth by dn="cn=admin,dc=example,dc=org" write by * none
olcAccess: {2}to dn.base="" by anonymous auth by * read
olcAccess: {3}to dn.base="cn=Subschema" by * read
olcAccess: {4}to * by dn.exact="cn=idpuser,ou=system,dc=example,dc=org" read by anonymous auth by self read
EOF'
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/scratch/olcAcl.ldif

Add MemberOf overlay

We want to be able to relate our users to groups, so we’ll add the MemberOf overlay next:

sudo bash -c 'cat > /etc/ldap/scratch/add_memberof.ldif <<EOF
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
olcModuleLoad: memberof
olcModulePath: /usr/lib/ldap

dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf
EOF'
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f /etc/ldap/scratch/add_memberof.ldif

Add indexes

We’ll add indexes to the OpenLDAP database now - run the following commands:

sudo bash -c 'cat > /etc/ldap/scratch/olcDbIndex.ldif <<EOF
dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcDbIndex
olcDbIndex: objectClass eq
olcDbIndex: member eq
olcDbIndex: cn pres,eq,sub
olcDbIndex: ou pres,eq,sub
olcDbIndex: uid pres,eq
olcDbIndex: entryUUID eq
olcDbIndex: sn pres,eq,sub
olcDbIndex: mail pres,eq,sub
EOF'
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f /etc/ldap/scratch/olcDbIndex.ldif

Add users to OpenLDAP

We’re getting there! Next we’re going to add some users to our OpenLDAP directory. We’ll use the slappasswd tool to generate an encrypted password for our users, and then we’ll add the users to the directory:

PASS=$(slappasswd -s s3cretPassw0rd)
sudo bash -c "cat > /etc/ldap/scratch/user1.ldif <<EOF
dn: uid=user1,ou=people,dc=example,dc=org
changetype: add
objectClass: inetOrgPerson
uid: user1
sn: User1
givenName: Test
cn: Test User1
displayName: Test User1
userPassword: $PASS
mail: test.user1@example.org

dn: uid=user2,ou=people,dc=example,dc=org
changetype: add
objectClass: inetOrgPerson
uid: user2
sn: User2
givenName: Test
cn: Test User2
displayName: Test User2
userPassword: $PASS
mail: test.user2@example.org
EOF"
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/scratch/user1.ldif

Disable anonymous bind to OpenLDAP

We don’t want just anyone to query our OpenLDAP directory, so we want to disable anonymous client queries of OpenLDAP. Let’s run the following commands:

sudo bash -c 'cat > /etc/ldap/scratch/disableAnonymousBind.ldif <<EOF
dn: cn=config
changetype: modify
add: olcDisallows
olcDisallows: bind_anon

dn: olcDatabase={-1}frontend,cn=config
changetype: modify
add: olcRequires
olcRequires: authc
EOF'
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f /etc/ldap/scratch/disableAnonymousBind.ldif

Configure Charmed Kubeflow to use OpenLDAP

You’ve reached the last step! We’ll set up Charmed Kubeflow to use our OpenLDAP server. If you’re running your Juju controller on another computer to the server running OpenLDAP, you’ll want to replace the variables in the code listing below with the values we defined in the first step of this tutorial before running the commands:

juju config dex-auth static-username='' static-password=''
cat <<EOF > connector.json
[{
"id": "ldap",
"name": "OpenLDAP",
"type": "ldap",
"config": {
"bindDN": "cn=idpuser,ou=system,dc=example,dc=org",
"bindPW": "$OL_BIND_PASS",
"rootCAData": "$(cat /etc/ldap/$(hostname -f).crt | base64 | tr -d "\n\r")",
"groupSearch": {
"baseDN": "ou=groups,dc=example,dc=org",
"filter": "",
"groupAttr": "member",
"nameAttr": "name",
"userAttr": "uid"
},
"host": "$OPENLDAP_HOST:389",
"insecureNoSSL": false,
"insecureSkipVerify": false,
"startTLS": true,
"userSearch": {
"baseDN": "ou=people,dc=example,dc=org",
"emailAttr": "cn",
"filter": "(objectClass=inetOrgPerson)",
"idAttr": "uid",
"nameAttr": "displayName",
"username": "uid",
"emailAttr": "mail"
},
"usernamePrompt": "Username"
}
}]
EOF
CONNECTOR_JSON=$(cat connector.json)
juju config dex-auth connectors="$CONNECTOR_JSON"

Check access

Let’s check that we can now log into our Charmed Kubeflow MLOps platform with one of the two user accounts we made previously. You should be able to navigate to the sign in screen below and successfully log in by choosing “Log in with OpenLDAP”:

It’s a wrap

Very well done! You should now have an OpenLDAP setup successfully connected to your Charmed Kubeflow MLOps platform! But if you’re having difficulties, don’t worry - head over to the forum to ask a question and get in touch with the community.

Further reading


Last updated 7 months ago.