Integrating LDAP Authentication in Argo CD: A Practical Deep Dive
Introduction
In distributed platform environments, authentication must be centralized to ensure consistency, security, and operational efficiency. Managing local users across multiple tools quickly becomes unsustainable.
This article documents a complete LDAP integration with Argo CD, focusing on how the system works internally, why each configuration is required, and how to avoid common failure patterns.
This is based on a real lab setup, including full commands and explicit configuration choices.
1. LDAP Preparation
Before integrating with Argo CD, the LDAP directory must already contain:
- A valid user
- A group for authorization
- Proper membership linking
User
In our LDAP, I created a group called ArgoAdminsGroup and added my user fbranco, and another group called ArgoDevsGroup, which will be used for developers. More details in 5. RBAC and Role-Based Access Control
Group
dn: cn=ArgoAdminsGroup,ou=SecurityGroups,dc=ldap,dc=devops-db,dc=info<br>objectClass: groupOfNames
member: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=infoWhy this structure matters
LDAP schemas vary, but in this setup:
- Users are identified by
uid - Groups use
groupOfNames - Membership is defined using full DN references
This directly impacts how Argo CD must be configured:
| Concept | LDAP Value | ArgoCD Mapping |
|---|---|---|
| User login | uid=fbranco | username: uid |
| User identity | uid | idAttr: uid |
| Group membership | member: DN | userAttr: DN |
2. Understanding the Authentication Flow
Argo CD uses Dex as an identity broker. The LDAP authentication flow is:
- Bind using service account
- Search for user
- Resolve user DN
- Attempt bind as user (password validation)
- Resolve groups
- Apply RBAC
Each step depends on the previous one. Misconfiguration in any step results in authentication failure.
3. Argo CD LDAP Configuration
Apply the values.yaml file with the LDAP information.
https://github.com/faustobranco/devops-db/blob/master/infrastructure/helms/argocd/values-ldap.yaml
crds:
install: true
global:
domain: argocd.devops-db.internal
configs:
params:
server.insecure: true
cm:
url: https://argocd.devops-db.internal
dex.config: |
connectors:
- type: ldap
name: LDAP
id: ldap
config:
host: ldap.devops-db.info:389
insecureNoSSL: true
bindDN: cn=readonly-bind-dn,ou=ServiceGroups,dc=ldap,dc=devops-db,dc=info
bindPW: 1234qwer
userSearch:
baseDN: dc=ldap,dc=devops-db,dc=info
filter: "(objectClass=person)"
username: uid
idAttr: uid
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: ou=SecurityGroups,dc=ldap,dc=devops-db,dc=info
filter: "(objectClass=groupOfNames)"
userMatchers:
- userAttr: DN
groupAttr: member
nameAttr: cn
rbac:
policy.csv: |
g, ArgoAdminsGroup, role:admin
g, ArgoDevsGroup, role:readonly
policy.default: role:readonly
server:
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: step-ca-issuer
tls:
- hosts:
- argocd.devops-db.internal
secretName: argocd-tls
ingressClassName: nginx
hosts:
- argocd.devops-db.internal
paths:
- /
pathType: Prefix
service:
type: ClusterIP
controller:
replicas: 1
repoServer:
replicas: 1
applicationSet:
enabled: true
redis:
enabled: true
master:
persistence:
enabled: true
size: 10GiApply:
helm upgrade --install argocd argo/argo-cd \
-n argocd \
--create-namespace \
-f values.yamlThe LDAP integration is configured via the argocd-cm ConfigMap.
Apply configuration
kubectl patch configmap argocd-cm -n argocd \
--type merge \
-p "$(cat <<'EOF'
data:
dex.config: |
connectors:
- type: ldap
name: LDAP
id: ldap
config:
host: ldap.devops-db.info:389
insecureNoSSL: true
bindDN: cn=readonly-bind-dn,ou=ServiceGroups,dc=ldap,dc=devops-db,dc=info
bindPW: 1234qwer
userSearch:
baseDN: dc=ldap,dc=devops-db,dc=info
filter: "(objectClass=person)"
username: uid
idAttr: uid
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: ou=SecurityGroups,dc=ldap,dc=devops-db,dc=info
filter: "(objectClass=groupOfNames)"
userMatchers:
- userAttr: DN
groupAttr: member
nameAttr: cn
EOF
)"Restart Argo CD components
kubectl rollout restart deployment argocd-server -n argocd
kubectl rollout restart deployment argocd-dex-server -n argocd
4. Configuration Breakdown
LDAP Connection
host: ldap.devops-db.info:389
insecureNoSSL: trueThis uses plain LDAP over port 389. In production, TLS should be enforced.
Service Account (Bind DN)
bindDN: cn=readonly-bind-dn,ou=ServiceGroups,...
bindPW: 1234qwerUsed to:
- search users
- retrieve group membership
User Search
filter: "(objectClass=person)"
username: uidDex automatically injects the username, avoiding malformed filters.
Identity Mapping
idAttr: uidKeeps identity aligned with login input.
Group Resolution
userMatchers:
- userAttr: DN
groupAttr: memberRequired because groups reference users by DN.
5. RBAC and Role-Based Access Control
LDAP handles authentication, but authorization is entirely managed by Argo CD.
Where RBAC is defined
kubectl get configmap argocd-rbac-cm -n argocd -o yamlBuilt-in Roles
Argo CD includes two primary roles by default:
role:admin
- Full access
- Create, update, delete applications
- Manage clusters and repositories
- Execute sync operations
role:readonly
- View applications
- View logs
- No write or deployment permissions
Custom Roles
Argo CD does not restrict you to predefined roles — you can define your own.
RBAC Policy Syntax
p, <role>, <resource>, <action>, <object>, <effect>
g, <group>, <role>Example: Developer Role
policy.csv: |
p, role:dev, applications, get, */*, allow
p, role:dev, applications, sync, */*, allow
p, role:dev, logs, get, */*, allow g, ArgoDevsGroup, role:devExample: QA Role
p, role:qa, applications, get, */*, allow
p, role:qa, logs, get, */*, allowg, ArgoQAGroup, role:qaMapping LDAP Groups to Roles
g, ArgoAdminsGroup, role:adminThis directly links LDAP group membership to Argo CD permissions.
Available Resources
| Resource | Description |
|---|---|
| applications | Application management |
| projects | Project definitions |
| clusters | Cluster connections |
| repositories | Git repositories |
| logs | Application logs |
| exec | Pod exec access |
| settings | Argo CD configuration |
Available Actions
| Action | Description |
|---|---|
| get | Read access |
| create | Create resources |
| update | Modify resources |
| delete | Remove resources |
| sync | Deploy applications |
| override | Bypass restrictions |
Why RBAC Matters
Without RBAC:
- All authenticated users are equal
- No separation of responsibilities
- Increased operational risk
With RBAC:
- Permissions are centralized
- Roles map cleanly to teams
- Access becomes predictable and auditable
6. Debugging and Observability
View Dex logs
kubectl logs -l app.kubernetes.io/name=argocd-dex-server -n argocd -f