LDAP Connector
Kubauth Architecture
If you inspect the kubauth pod, you will find it currently consists of 3 containers:
List the containers:
kubectl -n kubauth get pod -l app.kubernetes.io/instance=kubauth -o jsonpath='{range .items[0].spec.containers[*]}{.name}{"\n"}{end}'
Kubauth is built on a modular architecture. The following diagram describes the default configuration:

All modules communicate using the same simple identity protocol with a single exchange type:
- The Request provides a login and optionally a password
- The Response provides a status (NotFound, PasswordFail, PasswordChecked, ...) and all available user-related information (Name, Emails, Claims, ...)
All communication between modules (containers) uses localhost, keeping it private and not exposed outside the pod.
Each module (container) listens on a specific port:
- The
oidcmodule handles all user interaction and initiates identity requests - The
auditmodule is a transparent pass-through for the protocol, logging all requests to provide the audit feature described previously - The
ucrdmodule accesses the User, Group, and GroupBinding Custom Resources to build the response
All these modules are built into the kubauth image. Assembly is performed by the Helm chart.

There is another built-in module: ldap, which implements the identity protocol by connecting to an external LDAP server.
Here is the new configuration:

To switch to this configuration, you must:
- Disable the
ucrdmodule - Enable the
ldapmodule - Configure the
auditmodule to request theldapmodule (listening on port 6803) instead ofucrd(which listened on port 6802) - Configure the
ldapmodule for your target LDAP server

The values-ldap.yaml file below implements this configuration.
The LDAP configuration itself is tailored for an OpenLDAP server deployed as described in Appendix/OpenLDAP Deployment.
values-ldap.yaml
oidc:
issuer: https://kubauth.mycluster.mycompany.com
postLogoutURL: https://kubauth.mycluster.mycompany.com/index
allowPasswordGrant: true
ingress:
host: kubauth.mycluster.mycompany.com
server:
certificateIssuer: cluster-odp
audit:
idProvider:
baseURL: http://localhost:6803 # ldap provider listening port
ucrd:
enabled: false
ldap:
enabled: true
ldap:
host: openldap.openldap.svc
insecureNoSSL: true
bindDN: cn=admin,dc=mycompany,dc=com
bindPW: admin123
timeoutSec: 10
groupSearch:
baseDN: ou=Groups,dc=mycompany,dc=com
filter: (objectClass=posixgroup)
linkGroupAttr: memberUid
linkUserAttr: uid
nameAttr: cn
userSearch:
baseDN: ou=Users,dc=mycompany,dc=com
cnAttr: cn
emailAttr: mail
filter: (objectClass=inetOrgPerson)
loginAttr: uid
numericalIdAttr: uidNumber
LDAP Configuration
If you need to configure a different type of LDAP server, each property is documented in the Helm chart values file. For convenience, the relevant extract is provided below:
values-ldap-commented.yaml
.......
ldap:
enabled: true
ldap:
# The host and port of the LDAP server.
host:
# If port isn't supplied, it will be guessed based on the TLS configuration. (389 or 636).
port:
# Timeout on connection to ldap server. Default to 10
timeoutSec:
# Required if LDAP host does not use TLS.
insecureNoSSL:
# Don't verify the CA.
insecureSkipVerify:
# Connect to the insecure port then issue a StartTLS command to negotiate a
# secure connection. If unsupplied secure connections will use the LDAPS protocol.
startTLS:
# Only one of rootCaData, rootCaPath or rootCaSecret must be defined
# Path to a trusted root certificate file.
rootCaPath:
# Base64 encoded PEM data containing root CAs.
rootCaData:
# A secret hosting the CA, as base64 encoded value, at the 'rootCaSecretPath' location.
# The secret can be generated using trust-manager from cert-manager
rootCaSecret:
rootCaSecretPath: # Default to 'ca.crt'
# Path to a client cert file. If LDAP server require a certificate based client authentication
clientCert:
# Path to a client private key file. If LDAP server require a certificate based client authentication
clientKey:
# BindDN and BindPW for an application service account. The connector uses these
# credentials to search for users and groups.
bindDN:
bindPW:
groupSearch:
# BaseDN to start the search from. For example "cn=groups,dc=example,dc=com"
baseDN:
# Optional filter to apply when searching the directory. For example "(objectClass=posixGroup)"
filter:
# The filter for group/user relationship will be: (<linkGroupAttr>=<Value of LinkUserAttr for the user>)
# If there is several value for LinkUserAttr, we will loop on.
linkGroupAttr:
linkUserAttr:
# The attribute of the group that represents its name.
nameAttr:
# Defaults to "sub"
scope:
userSearch:
# BaseDN to start the search from. For example "cn=users,dc=example,dc=com"
baseDN:
# Optional filter to apply when searching the directory. For example "(objectClass=person)"
filter:
# Attribute to match against the login. This will be translated and combined with the other filter as "(<loginAttr>=<login>)".
loginAttr:
# Can either be:
# "sub" - search the whole sub tree
# "one" - only search one level
scope:
# The attribute providing the numerical user ID
numericalIdAttr:
# The attribute providing the user's email
emailAttr:
# The attribute providing the user's common name
cnAttr:
Deployment
Once your configuration is ready, proceed with deployment by running the helm upgrade ... command:
helm -n kubauth upgrade -i kubauth --values ./values-ldap.yaml oci://quay.io/kubauth/charts/kubauth --version 0.2.1 --create-namespace --wait
Verify which containers have been deployed:
kubectl -n kubauth get pod -l app.kubernetes.io/instance=kubauth -o jsonpath='{range .items[0].spec.containers[*]}{.name}{"\n"}{end}'
Test authentication:
If you enter jim/jim123, the login will fail.
However, if you enter credentials for a user defined in your LDAP server, the login should succeed. For example, using fred/fred123 (if using the sample LDAP configuration defined in the appendix):
Access token: ory_at_2vDnbRl0-cazsy2ZkIV_3bG8blZzgA5Cc0FxKXg5g6g.IvAqwgU04BnmBBoO_IPNt3UtIkiAWfJlgcj5WyMPsPk
Refresh token: ory_rt_UQQoBvf3Pkk1Yr6W3JCO_aShfX_28e8tAnxcMUQGjJE.I-P_3XJxVwPRmrUDVOXtt0ofS_G2MISSVRMoL48ZIAg
ID token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImY0Y2NkNDU0LWYzYTgtNDQ3Zi1hN2MzLTY3ZmY5MzUxMzZiMSIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoiM0RMYWZJMzdNZnNGZjVWOUN6QU1hUSIsImF1ZCI6WyJwdWJsaWMiXSwiYXV0aF90aW1lIjoxNzYxNjc0MzU2LCJhenAiOiJwdWJsaWMiLCJlbWFpbCI6ImZyZWRAbXljb21wYW55LmNvbSIsImVtYWlscyI6WyJmcmVkQG15Y29tcGFueS5jb20iXSwiZXhwIjoxNzYxNjc3OTU2LCJncm91cHMiOlsibWFuYWdlcnMiLCJzdGFmZiJdLCJpYXQiOjE3NjE2NzQzNTYsImlzcyI6Imh0dHBzOi8va3ViYXV0aC5pbmdyZXNzLmt1Ym82Lm1icCIsImp0aSI6IjE3YjQxMzk1LTU4MmUtNDM3ZC05YTA2LWNkYWY5NjM1NjdjZiIsIm5hbWUiOiJGcmVkIEFTVEVSIiwicmF0IjoxNzYxNjc0MzU2LCJzdWIiOiJmcmVkIn0.ZSnv4godrQOkrYNaEteOi85N-M9b4UKOI8zj0sorrD5Az1_xezINWiSRkrN2vLq9DtMKXZUBNjr41m4dW83xTsex8Tq_lEtisJP8woZzXzUpv1HnK2_RJiX_euGM2RSUJNaih4vpeDOPBhZQPUTkCIqnH_kus2dLuh84KGmcpLFC7vG60JW1lqRlx1Wb5ggmZmSFHC6k44Lx0bkihhpofiJg0QL1UoxmN1elixOegxOs0g_vxjdoDC3jNVOWONR6qVpvG7YRa25FgNPvfIV1XWeIP9upq0Yn_LNxLXuNFY_ysdw4lIQH2aEO58118KwB5eB8UQRnZJXmnAyKF-OU5Q
Expire in: 1h0m0s
JWT Payload:
{
"at_hash": "3DLafI37MfsFf5V9CzAMaQ",
"aud": [
"public"
],
"auth_time": 1761674356,
"auth_time_human": "2025-10-28 17:59:16 UTC",
"azp": "public",
"email": "fred@mycompany.com",
"emails": [
"fred@mycompany.com"
],
"exp": 1761677956,
"exp_human": "2025-10-28 18:59:16 UTC",
"groups": [
"managers",
"staff"
],
"iat": 1761674356,
"iat_human": "2025-10-28 17:59:16 UTC",
"iss": "https://kubauth.mycluster.mycompany.com",
"jti": "17b41395-582e-437d-9a06-cdaf963567cf",
"name": "Fred ASTER",
"rat": 1761674356,
"rat_human": "2025-10-28 17:59:16 UTC",
"sub": "fred"
}
Check the audit trail: