# LDAP integration

By default, only a kubeadmin user exists on your cluster after the initial deployment. To specify an identity provider, you must create an OCP custom resource (CR) that describes the identity provider and then add it to the cluster. This solution provides a playbook to assist in the creation of such a custom resource for an LDAP identity provider.

# About LDAP authentication in OCP

During authentication, the LDAP directory is searched for an entry that matches the provided user name. If a single unique match is found, a simple bind is attempted using the distinguished name (DN) of the entry plus the provided password. If the LDAP directory requires authentication to search, you must specify a bindDN and bindPassword for the account used when performing the entry search.

For more information on using LDAP, see the OpenShift Container Platform 4.2 documentation for Configuring LDAP Identity Provider

# Configuring LDAP variables

All variables relating to LDAP configuration are described in the table below.

Variable File Description
ldap_bind_user_dn group_vars/all/vars.yml The name (or Bind DN) of the LDAP user required to perform the search.
vault.ldap_bind_user_password group_vars/all/vault.yml Password for the LDAP user account used to perform the search.
ldap_ca_file group_vars/all/vars.yml Location of the CA Bundle of the LDAP server, exported in PEM format. A sample file is provided in playbooks/roles/ldap/files/ca.pem
ldap_cr_file group_vars/all/vars.yml Location of the Custom Resource used to configure an Identity Provider. A sample file is provided in playbooks/roles/ldap/vars/ldap_cr.yml

Extracting the certificate authority (CA) bundle is specific to your particular LDAP server and is beyond the scope of this guide. Once you have obtained the information in the correct PEM format, you should store it in a file on your Ansible controller. The location of this file is used as the value of the ldap_ca_file variable.

The parameters and values in the Custom Resource file are highly dependent on your particular environment and, as a result, these cannot be generalized in the solution. Appendix B provides a detailed overview of the sample Custom Resource file that comes with this solution and more information can be found in the OCP online documentation in the article Understanding identity provider configuration.

# Testing the configuration

Before running the playbook, it can be helpful to manually test your configuration. Install a tool such as ldapsearch using dnf install -y openldap-clients and attempt to perform a query using the configuration information you have established. Initially, it is easier to use insecure ldap access, rather than secure ldaps, to perform the search. The following example uses the bind DN of adreader and, as a result of specifying the -W flag, you will be prompted to enter the corresponding password. You will need to adapt the URI ldap://mars-adds.am2.cloudra.local and the parameters to your own environment.

For the example to be successful, you must have added a user adocpuser1 in the directory.

$ dnf install -y openldap-clients

$ ldapsearch -H ldap://mars-adds.am2.cloudra.local \
         -x -W -D "cn=adreader,cn=Users,dc=am2,dc=cloudra,dc=local" \
         -b "cn=Users,dc=am2,dc=cloudra,dc=local" \
         "(&(objectClass=person)(sAMAccountName=adocpuser1))"`

The above query will replicate what the identity provider does when a user named adocpuser1 attempts to log in. If this user exists in your LDAP directory, the search will return a single directory entry, similar to the following:

filter: (&(objectClass=person)(sAMAccountName=adocpuser1))
requesting: All userApplication attributes
# extended LDIF
#
# LDAPv3
# base <cn=Users,dc=am2,dc=cloudra,dc=local> with scope subtree
# filter: (&(objectClass=person)(sAMAccountName=adocpuser1))
# requesting: ALL
#

# adocp user1, Users, am2.cloudra.local
dn: CN=adocp user1,CN=Users,DC=am2,DC=cloudra,DC=local
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: adocp user1
sn: user1
givenName: adocp
distinguishedName: CN=adocp user1,CN=Users,DC=am2,DC=cloudra,DC=local
instanceType: 4
whenCreated: 20191111160653.0Z
whenChanged: 20191112100318.0Z
displayName: adocp user1
uSNCreated: 188305
memberOf: CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local
uSNChanged: 188439
name: adocp user1
objectGUID:: FomFmxLnoEi0pnyxTbjHFg==
userAccountControl: 66048
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 0
lastLogoff: 0
lastLogon: 0
pwdLastSet: 132179620134013170
primaryGroupID: 513
objectSid:: AQUAAAAAAAUVAAAA1r8HwaYUleyBG+FYcgQAAA==
accountExpires: 9223372036854775807
logonCount: 0
sAMAccountName: adocpuser1
sAMAccountType: 805306368
userPrincipalName: adocpuser1@am2.cloudra.local
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=am2,DC=cloudra,DC=local
dSCorePropagationData: 20191111160653.0Z
dSCorePropagationData: 16010101000000.0Z
lastLogonTimestamp: 132180265987003551

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Once your testing with insecure access is successful, configure ldapsearch with the appropriate CA certificate for your LDAP server and then switch to using ldaps to test secure access as performed by the playbook.

# Overview of the playbook

The playbook requires a username and password to access the LDAP directory. You must also supply the PEM file and then configure the custom resource file (detailed in Appendix B) to match your particular environment.

When you run the playbook, it creates an OpenShift Container Platform Secret named ldap-secret that contains the bindPassword, used to access the identity provider. It also creates a ConfigMap named ca-config-map in the openshift-config namespace to contain the certificate authority bundle, To support secure access to the identity provider.

# Running the playbook

When you have confirmed that you can securely access to the LDAP directory and have configured all the required variables and files to match your LDAP environment, you can run the integration playbook as follows:

$ cd ~/OpenShift-on-SimpliVity

$ ansible-playbook -i hosts playbooks/ldap.yml --vault-password-file .vault_pass

You may have to wait a few seconds after the playbook completes, before the authentication cluster operator is available.

# Verification

You can now attempt to log in using the LDAP identity provider, using either the command line or the web console.

$ oc login -u adocpuser1
Authentication required for https://api.ocp.hpecloud.org:6443 (openshift)
Username: adocpuser1
Password:
Login successful.

You don't have any projects. You can try to create a new project, by running

    oc new-project <projectname>


$ oc whoami
adocpuser1

# Synchronizing groups

As an OpenShift administrator, you can use groups to manage users, change their permissions, and enhance collaboration. Your organization may have already created user groups and stored them in an LDAP server. OpenShift can sync those LDAP records with internal OpenShift records, enabling you to manage your groups in one place. OpenShift currently supports group sync with LDAP servers using three common schemas for defining group membership: RFC 2307, Active Directory, and augmented Active Directory. More information on LDAP synchronization is available in the OCP documentation at https://docs.openshift.com/container-platform/4.2/authentication/ldap-syncing.html.

This solution does not provide any playbooks to support synchronization. However, the following example shows how it can be performed manually. It assumes that you have created a group of ordinary users in your LDAP directory, named adocpusers and containing users adocpuser1 and adocpuser2. It also assumes a group of administrators, adocpadmins, containing users adocpadmin1 and adocpadmin2.

# Sync example

In order to sync OpenShift group records with those from an external provider, determine which groups you wish to sync and where their records live. For instance, all or some groups may be selected from those stored on an LDAP server. The path to a sync configuration file is required in order to describe how data is requested from the external record store and migrated to OpenShift records. Default behavior is to do a dry-run without changing OpenShift records. Passing '--confirm' will sync all groups from the LDAP server returned by the LDAP query templates.

Create a sync file ad-config.yaml similar to the following, adapting the parameters and values for your own environment.

kind: LDAPSyncConfig
apiVersion: v1
url: ldaps://mars-adds.am2.cloudra.local
ca: playbooks/roles/ldap/files/ca.pem
insecure: false
bindDN: cn=adreader,cn=Users,dc=am2,dc=cloudra,dc=local
bindPassword: *******
activeDirectory:
  usersQuery:
    baseDN: cn=Users,dc=am2,dc=cloudra,dc=local
    scope: sub
    derefAliases: never
    filter: (objectClass=person)
    pageSize: 0
  userNameAttributes: [ sAMAccountName ]
  groupMembershipAttributes: [ memberOf ]

Run the sync command, without the --confirm flag, to identify the groups in your LDAP directory:

$ oc adm groups sync --sync-config=ad-config.yaml

This command will produce output similar to the following:

...
- metadata:
    annotations:
      openshift.io/ldap.sync-time: 2019-11-12T06:41:23-0500
      openshift.io/ldap.uid: CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local
      openshift.io/ldap.url: mars-adds.am2.cloudra.local:636
    creationTimestamp: "2019-11-12T11:37:15Z"
    labels:
      openshift.io/ldap.host: mars-adds.am2.cloudra.local
    name: adocpadmins
    resourceVersion: "1924931"
    selfLink: /apis/user.openshift.io/v1/groups/adocpadmins
    uid: c6c1ba9d-0540-11ea-bcd8-0a580a80021c
  users:
  - adocpadmin1
  - adocpadmin2
...
- metadata:
    annotations:
      openshift.io/ldap.sync-time: 2019-11-12T06:41:23-0500
      openshift.io/ldap.uid: CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local
      openshift.io/ldap.url: mars-adds.am2.cloudra.local:636
    creationTimestamp: "2019-11-12T11:38:02Z"
    labels:
      openshift.io/ldap.host: mars-adds.am2.cloudra.local
    name: adocpusers
    resourceVersion: "1925158"
    selfLink: /apis/user.openshift.io/v1/groups/adocpusers
    uid: e240adc2-0540-11ea-8e34-0a580a810017
  users:
  - adocpuser1
  - adocpuser2

Note how the ldap.uid fields use capital letters: CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local.

Before syncing the data, add groupUIDNameMapping to your sync configuration file to map the LDAP groups to the group names you want to use in OCP, in this instance adocpusers and adocpadmins:

groupUIDNameMapping:
  "CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local": adocpusers
  "CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local": adocpadmins

So your complete sync file should now look like:

kind: LDAPSyncConfig
apiVersion: v1
url: ldaps://mars-adds.am2.cloudra.local
ca: playbooks/roles/ldap/files/ca.pem
insecure: false
bindDN: cn=myuser,cn=Users,dc=am2,dc=cloudra,dc=local
bindPassword: ********
groupUIDNameMapping:
  "CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local": adocpusers
  "CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local": adocpadmins
activeDirectory:
  usersQuery:
    baseDN: cn=Users,dc=am2,dc=cloudra,dc=local
    scope: sub
    derefAliases: never
    filter: (objectClass=person)
    pageSize: 0
  userNameAttributes: [ sAMAccountName ]
  groupMembershipAttributes: [ memberOf ]

By default, the sync command will synchronize based on all the data found in your LDAP directory. You can limit the sync to specific groups by supplying a whitelist to the sync command. Run the command without the --confirm flag, and provide a whitelist of the groups you want to sync, using the ldap.uid values returned earlier:

$ oc adm groups sync \
   --sync-config=ad-config.yaml \
   "CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local" \
   "CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local"

The command will perform a dry-run and return the values it would have synchronized, showing the two groups and the corresponding users:

apiVersion: v1
items:
- metadata:
    annotations:
      openshift.io/ldap.sync-time: 2019-11-14T05:54:50-0500
      openshift.io/ldap.uid: CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local
      openshift.io/ldap.url: mars-adds.am2.cloudra.local:636
    creationTimestamp: "2019-11-12T11:38:02Z"
    labels:
      openshift.io/ldap.host: mars-adds.am2.cloudra.local
    name: adocpusers
    resourceVersion: "1925158"
    selfLink: /apis/user.openshift.io/v1/groups/adocpusers
    uid: e240adc2-0540-11ea-8e34-0a580a810017
  users:
  - adocpuser1
  - adocpuser2
- metadata:
    annotations:
      openshift.io/ldap.sync-time: 2019-11-14T05:54:50-0500
      openshift.io/ldap.uid: CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local
      openshift.io/ldap.url: mars-adds.am2.cloudra.local:636
    creationTimestamp: "2019-11-12T11:37:15Z"
    labels:
      openshift.io/ldap.host: mars-adds.am2.cloudra.local
    name: adocpadmins
    resourceVersion: "1924931"
    selfLink: /apis/user.openshift.io/v1/groups/adocpadmins
    uid: c6c1ba9d-0540-11ea-bcd8-0a580a80021c
  users:
  - adocpadmin1
  - adocpadmin2
kind: List
metadata: {}

Now run the sync command with the --confirm flag to synchronize the groups to your OCP cluster:

$ oc adm groups sync \
   --sync-config=ad-config.yaml \
   "CN=adocpusers,CN=Users,DC=am2,DC=cloudra,DC=local" \
   "CN=adocpadmins,CN=Users,DC=am2,DC=cloudra,DC=local" \
   --confirm

group/adocpusers
group/adocpadmins

You can confirm that the groups have been added to the cluster:

$ oc get groups

NAME          USERS
adocpadmins   adocpadmin1, adocpadmin2
adocpusers    adocpuser1, adocpuser2

# Adding cluster admin role to group

Once you have added the adocpadmins group to your cluster, you can give administration privileges to members of the group.

Log in with the initial kubeadmin user account, as outlined in the section Logging into the OCP cluster for the first time.

Assign the cluster-admin role to the adocpadmins group:

$ oc adm policy add-cluster-role-to-group cluster-admin adocpadmins

Now, when a member of the ocadmingroup logs in to the cluster, the user will have cluster adminsitration privileges and access to all the projects/namespaces:

$ oc login -u adocpadmin1
Authentication required for https://api.ocp.hpecloud.org:6443 (openshift)
Username: adocpadmin1
Password:
Login successful.

You have access to the following projects and can switch between them with 'oc project <projectname>':

  * default
    kube-public
    kube-system
    openshift
    openshift-apiserver
    openshift-apiserver-operator
    openshift-authentication
    openshift-authentication-operator
    openshift-cloud-credential-operator
    openshift-cluster-machine-approver
    openshift-cluster-node-tuning-operator
    openshift-cluster-samples-operator
...

It is recommended that you delete the initial kubeadmin user, once you have successfully created and tested new admin user accounts:

$ oc delete secret kubeadmin -n kube-system