Introduction

In a complex environment such as OpenShift, seamless integration is key. In this post, we will go through the steps to sync Active Directory groups to OpenShift automatically, ensuring our Kubernetes-based platform is in harmony with our directory services. This procedure is used for customers in Octopus. We know the value of identity management and I am happy to share insights on how to achieve this easily. This is a follow up to Configuring Active Directory as OpenShift Identity Provider which is a requirement for this post.

Sync Active Directory Manually

Active Directory Group

If you don’t have a group that exists in your AD create it using the following:

Name the group ocp-admin and click OK

Add a user to it by right click the group -> properties -> members -> Add

Enter the user you’d like to join the ocp-admin group, press Check Names

Once recognized by an underline press OK.

Go to the Attribute Editor of the group to extract the distinguishingName attribute:

Press OK in the group editing menu to close it.

We now have a user inside the ocp-admin group.

Manual Sync

First, let’s make sure that our Active Directory (AD) ldap connection is working. We will do so using the ldapsearch command.

$ yum install openldap-clients
$ ldapsearch -x -H ldap://example.com:389 -b 'dc=example,dc=com' -D 'cn=admin,cd=users,dc=example,dc=com' -w 'password' "(objectClass=group)"

NOTE: If the above doesn’t provide you with an answer, then fix that before continuing.

Create the following files for a manual run:

auth-sync.yaml

kind: LDAPSyncConfig
apiVersion: v1
url: ldaps://<YOUR_LDAP_SERVER>/
insecure: false
bindDN: "OCP ldapUser"
bindPassword:
  file: "/etc/secrets/bindPassword"
ca: /etc/ldap-ca/ca.crt
groupUIDNameMapping:
  "CN=ocp-admin,CN=Users,DC=octopus,DC=local": ocp-admins
augmentedActiveDirectory:
    groupsQuery: 
        derefAliases: never
        pageSize: 0
    groupUIDAttribute: dn 
    groupNameAttributes: [ cn ] 
    usersQuery:
        baseDN: "CN=Users,DC=octopus,DC=local"
        scope: sub
        derefAliases: never
        filter: (objectclass=Person)
        pageSize: 0
    userNameAttributes: [ sAMAccountName ] 
    groupMembershipAttributes: [ "memberOf:1.2.840.113556.1.4.1941:" ]

whitelist.txt

"CN=ocp-admin,CN=Users,DC=octopus,DC=local"

Run a manual sync:

$ oc adm groups sync --sync-config=auth-sync.yaml  --whitelist=whitelist.txt --confirm

Output should look like this:

# oc adm groups sync --sync-config=auth-sync.yaml  --whitelist=whitelist.txt --confirm
group/ocp-admins

Let’s check the group:

# oc get groups
NAME         USERS
ocp-admins   dpointk

Permissions Wanted

Syncing a group from AD is not enough. For example, you can add permissions to the AD group. Here’s an example of adding cluster-admin permissions to a specific group:

$ oc adm policy add-cluster-role-to-group cluster-admin ocp-admins

We can now connect with the dpointk user from Active Directory to OpenShift!

Sync Active Directory Automatically

Now that we synced, let’s configure an automatic way to sync groups, this will keep our groups synced automatically.

Create the service account

We will use the service account to run the command to sync the groups.

01_sa.yaml:

kind: ServiceAccount
apiVersion: v1
metadata:
  name: ldap-group-syncer
  namespace: openshift-authentication
  labels:
    app: cronjob-ldap-group-sync

Create the service account:

$ oc create -f 01_sa.yaml

ClusterRole

Create the specific ClusterRole to allow access to the groups in OpenShift.

02_cluster_role.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ldap-group-syncer
  labels:
    app: cronjob-ldap-group-sync
rules:
  - apiGroups:
      - ''
      - user.openshift.io
    resources:
      - groups
    verbs:
      - get
      - list
      - create
      - update

Create the cluster role:

$ oc create -f 02_cluster_role.yaml

ClusterRoleBinding

Create the ClusterRoleBinding to bind the service account and the role we created earlier.

03_cluster_rolebinding.yaml:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ldap-group-syncer
  labels:
    app: cronjob-ldap-group-sync
subjects:
  - kind: ServiceAccount
    name: ldap-group-syncer
    namespace: openshift-authentication
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ldap-group-syncer

Now, create the cluster role-binding

$ oc create -f 03_cluster_rolebinding.yaml

Configmap

Let’s create the configmap that will hold our ldap-group-sync.yaml file

04_configmap.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: ldap-group-syncer
  namespace: openshift-authentication
  labels:
    app: cronjob-ldap-group-sync
data:
  ldap-group-sync.yaml: |
    kind: LDAPSyncConfig
    apiVersion: v1
    url: ldaps://<YOUR_LDAP_SERVER>/
    insecure: false
    bindDN: "OCP ldapUser"
    bindPassword:
      file: "/etc/secrets/bindPassword"
    ca: /etc/ldap-ca/ca.crt
    groupUIDNameMapping:
      "CN=ocp-admin,CN=Users,DC=octopus,DC=local": ocp-admins
    augmentedActiveDirectory:
        groupsQuery: 
            derefAliases: never
            pageSize: 0
        groupUIDAttribute: dn 
        groupNameAttributes: [ cn ] 
        usersQuery:
            baseDN: "CN=Users,DC=octopus,DC=local"
            scope: sub
            derefAliases: never
            filter: (objectclass=Person)
            pageSize: 0
        userNameAttributes: [ sAMAccountName ] 
        groupMembershipAttributes: [ "memberOf:1.2.840.113556.1.4.1941:" ]

NOTE: Make sure to replace the ldap address and OU as needed.

Create the configmap:

$ oc create -f 04_configmap.yaml

Conrjob

Finally, create the cronjob that will run every 10 minutes and re-sync the OpenShift groups and the Active Directory.

Remember! This is a one way street, we rely on the Active Directory to be our source truth.

05_cronjob.yaml

kind: CronJob
apiVersion: batch/v1
metadata:
  name: ldap-group-syncer
  namespace: openshift-authentication
  labels:
    app: cronjob-ldap-group-sync
spec:
  schedule: "*/10 * * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 5
  failedJobsHistoryLimit: 5
  jobTemplate:
    metadata:
      labels:
        app: cronjob-ldap-group-sync
    spec:
      backoffLimit: 0
      template:
        metadata:
          labels:
            app: cronjob-ldap-group-sync
        spec:
          containers:
            - name: ldap-group-sync
              image: "openshift/origin-cli:latest"
              command:
                - "/bin/bash"
                - "-c"
                - oc adm groups sync --sync-config=/etc/config/ldap-group-sync.yaml --confirm
              volumeMounts:
                - mountPath: "/etc/config"
                  name: "ldap-sync-volume"
                - mountPath: "/etc/secrets"
                  name: "ldap-bind-password"
                - mountPath: "/etc/ldap-ca"
                  name: "ldap-ca"
          volumes:
            - name: "ldap-sync-volume"
              configMap:
                name: "ldap-group-syncer"
            - name: "ldap-bind-password"
              secret:
                secretName: "v4-0-config-user-idp-0-bind-password"
            - name: "ldap-ca"
              configMap:
                name: "ca-config-map"
          restartPolicy: "Never"
          terminationGracePeriodSeconds: 30
          activeDeadlineSeconds: 500
          dnsPolicy: "ClusterFirst"
          serviceAccountName: "ldap-group-syncer"
          serviceAccount: "ldap-group-syncer"

Create the cronjob:

$ oc create -f 05_cronjob.yaml

NOTE: v4-0-config-user-idp-0-bind-password and ca-config-map were previously configured due to LDAP configuration done in the previous post Configuring Active Directory as OpenShift Identity Provider

Let’s make sure it was created:

# oc get cronjob -n openshift-authentication
NAME                SCHEDULE       SUSPEND   ACTIVE   LAST SCHEDULE   AGE
ldap-group-syncer   */10 * * * *   False     0        <none>          23s

Let’s test the cronjob:

$ oc project openshift-authentication
$ oc create job sync --from=cronjob/ldap-group-syncer

Check all was successful, if so, then you should have a Completed pod and a Completed job:

# oc get pods
NAME                               READY   STATUS      RESTARTS   AGE
oauth-openshift-54f4d8f77d-jw8jn   1/1     Running     0          47m
sync-gl6db                         0/1     Completed   0          2m1s

# oc get job
NAME   COMPLETIONS   DURATION   AGE
sync   1/1           16s        2m17s

This will now run every 10 minutes according to the cronjob we’ve created.

Troubleshooting

If you get the following error:

error: from must be an existing cronjob: no kind "CronJob" is registered for version "batch/v1" in scheme "k8s.io/kubectl/pkg/scheme/scheme.go:28"

See this Red Hat KB https://access.redhat.com/solutions/6956109 and replace your oc client.

Should your pods are stuck on ContainerCreating state check the pod status with:

$ oc describe pod <podname>

Summary

In this post we’ve explored how to sync Active Directory groups to OpenShift automatically, an essential process for unified user management and access controls. It ensures that your OpenShift environment stays in sync with organizational changes. With Octopus at your side, benefit from expert guidance and support in implementing such procedures and manage your OpenShift, ensuring optimal security in your deployments.