OpenShift LDAP/Active Directory login and safe kubeadmin removal

Configure OpenShift OAuth with LDAPS and Active Directory, test the bind first, grant cluster-admin to a group, then remove kubeadmin without locking yourself out.

By Rami Chiha7 min read
Quick engineering answer

What this guide covers

What this solves
Configure OpenShift OAuth with LDAPS and Active Directory, test the bind first, grant cluster-admin to a group, then remove kubeadmin without locking yourself out.
Applies to
OpenShift · OpenShift · LDAP · Active Directory · OAuth
Prerequisites
Review the guide's prerequisites, architecture, and decision sections before execution.
Risk
Follow every warning and validate environment-specific commands before a change window.
Expected result
A fresh OpenShift cluster gives you a temporary `kubeadmin` account. It is meant for the first hours of the cluster's life. The sensible path is to connect your enterprise directory, prove that a real administrator can sign in with cluster-admin rights, and only then remove `kubeadmin`.
How to verify
Use the guide's validation, test, acceptance, or handover steps.
Last verified
2026-10-04

A fresh OpenShift cluster gives you a temporary kubeadmin account. It is meant for the first hours of the cluster's life. The sensible path is to connect your enterprise directory, prove that a real administrator can sign in with cluster-admin rights, and only then remove kubeadmin.

This guide uses Active Directory over LDAPS. The same OAuth structure works with other LDAP servers, with different attribute names. Names below are placeholders.

This procedure was validated for the OpenShift 4 OAuth LDAP identity-provider family shown here. OAuth API fields, operator behavior and directory TLS policy can change across version families. Revalidate the schema, rollout behavior, CA chain, hostname verification and rollback procedure against the selected OpenShift release before production use or upgrade.

What to collect first#

ItemExampleNotes
LDAPS endpointdc01.corp.example.com:636Use a name that matches the certificate
Base DNDC=corp,DC=example,DC=comSearch base for users
Bind DNCN=svc-openshift,OU=Service Accounts,DC=corp,DC=example,DC=comRead-only service account
Bind passwordheld in a vaultGoes into a Secret, never into YAML or documents
CA certificatead-ca.crtNeeded when AD uses a private CA
Login attributesAMAccountNameBecomes the OpenShift username
Admin groupOCP-AdminsWho receives cluster-admin

Open TCP 636 from the nodes (the authentication pods run on them) to the domain controllers.

1. Test outside OpenShift#

Prove the certificate chain, the bind and the search before touching the cluster.

bash
# Certificate chain and hostname; any verification error is a stop condition.
ldap_host=dc01.corp.example.com
ldap_port=636
ca_file=/secure/path/ad-ca.crt
test -r "$ca_file"
openssl s_client -connect "$ldap_host:$ldap_port" \
  -servername "$ldap_host" -CAfile "$ca_file" \
  -verify_hostname "$ldap_host" -verify_return_error </dev/null

# Bind account and user lookup
LDAPTLS_CACERT="$ca_file" ldapsearch -x -H "ldaps://$ldap_host:$ldap_port" \
  -D 'CN=svc-openshift,OU=Service Accounts,DC=corp,DC=example,DC=com' -W \
  -b 'DC=corp,DC=example,DC=com' \
  '(sAMAccountName=reviewed-test-user)' dn sAMAccountName memberOf

Success requires Verify return code: 0 (ok), a certificate SAN matching ldap_host, a complete trusted chain and the expected single user result. Do not use -verify 0, LDAPTLS_REQCERT=never, IP-address URLs or an untrusted CA as workarounds. Repeat against every directory endpoint that DNS or a load balancer can select.

A common failure is LDAP error 49 with data 52e. In Active Directory this points to invalid credentials for the bind. Recheck the bind DN format, the password, whether the account is locked, and whether the account may bind over LDAPS.

2. Create the Secret and CA ConfigMap#

Both live in the openshift-config namespace.

Retrieve the bind password from the approved vault into a root-only temporary file or provide it through an equivalent secret-management integration. Do not put it in a command argument, environment variable, terminal prompt recording, YAML file, or shell history.

bash
umask 077
bind_password_file=/secure/encrypted-tmp/ldap-bind-password
ca_file=/secure/path/ad-ca.crt
test -f "$bind_password_file" && test "$(stat -c '%a' "$bind_password_file")" = 600
test -r "$ca_file"
oc create secret generic ldap-bind-password \
  -n openshift-config \
  --from-file="bindPassword=$bind_password_file" \
  --dry-run=client -o yaml | oc apply -f -

oc create configmap ldap-ca \
  -n openshift-config \
  --from-file="ca.crt=$ca_file" \
  --dry-run=client -o yaml | oc apply -f -
oc get secret ldap-bind-password -n openshift-config -o name
oc get configmap ldap-ca -n openshift-config -o name

Verify the Secret exists without printing its data, then remove the temporary file using the organisation's secure temporary-data procedure. File deletion alone may not guarantee erasure on every filesystem; prefer direct vault/secret-operator integration where available. Restrict who can read Secrets in openshift-config and rotate the bind credential under a tested procedure.

3. Back up and merge the OAuth identity provider#

Treat OAuth as a shared cluster object. Capture the current object and identity-provider list in a root-only directory, then create a proposed file by adding corp-ad to the existing spec.identityProviders array. Do not apply the standalone fragment below over an object that already has providers.

bash
umask 077
oauth_change_dir="$HOME/oauth-change-$(date +%Y%m%dT%H%M%S%z)"
install -d -m 0700 "$oauth_change_dir"
oc get oauth cluster -o yaml >"$oauth_change_dir/oauth.before.yaml"
oc get oauth cluster -o json | jq '{spec: .spec}' >"$oauth_change_dir/oauth-spec.before.json"
oc get oauth cluster -o jsonpath='{range .spec.identityProviders[*]}{.name}{"\n"}{end}' \
  >"$oauth_change_dir/providers.before.txt"
cp "$oauth_change_dir/oauth.before.yaml" "$oauth_change_dir/oauth.proposed.yaml"
chmod 0600 "$oauth_change_dir"/*
# Edit oauth.proposed.yaml: preserve metadata.name and every existing provider,
# remove server-managed metadata/status fields, and append the reviewed entry below.

The identity-provider entry to merge is:

yaml
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
  name: cluster
spec:
  identityProviders:
  - name: corp-ad
    mappingMethod: claim
    type: LDAP
    ldap:
      attributes:
        id:
        - dn
        email:
        - mail
        name:
        - cn
        preferredUsername:
        - sAMAccountName
      bindDN: 'CN=svc-openshift,OU=Service Accounts,DC=corp,DC=example,DC=com'
      bindPassword:
        name: ldap-bind-password
      ca:
        name: ldap-ca
      insecure: false
      url: 'ldaps://dc01.corp.example.com:636/DC=corp,DC=example,DC=com?sAMAccountName'

Key points:

  • insecure: false keeps certificate validation on. Do not turn it off to "make it work"; fix the CA instead.
  • If you already have identity providers, add this one to the list rather than replacing the object.
  • mappingMethod: claim is the safest default: a username can only map to one identity.

Review the proposed object and server-side validation before applying it:

bash
oc diff -f "$oauth_change_dir/oauth.proposed.yaml"
oc apply --dry-run=server -f "$oauth_change_dir/oauth.proposed.yaml"
# Apply only after the diff proves existing providers remain present.
oc apply -f "$oauth_change_dir/oauth.proposed.yaml"
oc get pods -n openshift-authentication
oc rollout status deploy/oauth-openshift -n openshift-authentication
oc logs -n openshift-authentication deploy/oauth-openshift --tail=100

If the operator degrades, the rollout fails, existing login stops working or strict-TLS LDAP login fails, review the captured spec and restore it with oc patch oauth cluster --type=merge --patch-file="$oauth_change_dir/oauth-spec.before.json", watch the rollout, and retest the pre-existing provider. This requires reviewed jq output containing only the spec wrapper; validate it before the change. Keep the current break-glass session open throughout. Do not delete the bind Secret or CA ConfigMap until rollback is complete and the old provider is verified; if they replaced existing same-named objects, separately back those objects up before changing them.

4. Grant cluster-admin to a group#

You can create the group manually or synchronise it from the directory.

bash
oc adm groups new OCP-Admins
ad_admin_user=reviewed-ad-admin
oc adm groups add-users OCP-Admins "$ad_admin_user"
oc adm policy add-cluster-role-to-group cluster-admin OCP-Admins

For more than a handful of people, plan LDAP group sync (oc adm groups sync) so membership is driven from the directory instead of hand-edited.

5. Prove it with a real user#

bash
ad_admin_user=reviewed-ad-admin
oc login https://api.ocp-prod.example.com:6443 -u "$ad_admin_user"
oc whoami
oc auth can-i '*' '*' --all-namespaces

You want yes. Also sign in to the web console with the same account.

6. Remove kubeadmin#

bash
oc whoami
oc auth can-i '*' '*' --all-namespaces
oc delete secret kubeadmin -n kube-system
oc get secret kubeadmin -n kube-system     # expect NotFound

Before you delete, store the installation-time kubeconfig in your vault. It remains your certificate-based break-glass access, and access to it should be controlled and audited.

Removal is intentionally one-way for the generated password: rollback requires restoring the original kubeadmin Secret from an approved encrypted backup if the selected release supports that recovery, or using the protected installation kubeconfig to repair OAuth/RBAC. Test that break-glass kubeconfig before deletion, retain two independent authenticated administrator sessions, and record the recovery decision. Never recreate a look-alike Secret with a newly invented password.

Evidence to keep for handover#

  • The OAuth YAML without passwords.
  • The test user, the group and proof of oc auth can-i.
  • A note of where the bind password and break-glass kubeconfig are stored.
  • The pre-change OAuth backup, reviewed diff, rollout evidence, TLS verification and rollback result.

Examples use documentation-only names. Verify API fields against the Red Hat documentation for your OpenShift version.

Need identity, RBAC and access governance designed properly? See Kubernetes and OpenShift or start a project.