Documentation Index
Fetch the complete documentation index at: https://conductorone-ian-account-to-user-pipeline.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Availability
The Active Directory connector is self-hosted and supports Windows and Linux.Capabilities
| Resource | Sync | Provision |
|---|---|---|
| Accounts | ||
| Groups | ||
| Group Managed Service Accounts (gMSAs) |
- Primary group memberships sync automatically via
primaryGroupID; AD’smemberOfdoesn’t include them. - Multi-domain and cross-forest sync in a single run via
additional-domainsconfig — see Multi-domain and cross-forest sync. - gMSA sync is opt-in via
enable-gmsa-sync; provisioning modifies themsDS-GroupMSAMembershipsecurity descriptor ACL. - Two connection modes: LDAP (default on Linux) and WinLDAP (default on Windows; uses
wldap32.dllfor Kerberos/GSSAPI). - Account provisioning (create/delete) requires LDAPS (
ldaps: true).
Connector actions
Connector actions are custom capabilities that extend C1 automations with app-specific operations. You can use connector actions in the Perform connector action automation step. Global actions (connector-level):| Action name | Additional fields | Description |
|---|---|---|
enable_user | resource_id (resource ID, required) | Enable a disabled AD account (clears ACCOUNTDISABLE flag) |
disable_user | resource_id (resource ID, required) | Disable an active AD account (sets ACCOUNTDISABLE flag) |
lock_account | resource_id (resource ID, required) | Lock an AD account — alias for disable_user, sets the ACCOUNTDISABLE UAC flag. AD has no separate lock state. |
unlock_account | resource_id (resource ID, required) | Unlock an AD account — alias for enable_user, clears the ACCOUNTDISABLE UAC flag. |
update_user_attrs | resource_type (string, required), resource_id (string, required), attrs (map, required), attrs_update_mask (string list, required) | Update user attributes. Known names (for example, first_name) are mapped to AD attributes; unknown names are passed through as raw AD attribute names. Empty values clear the attribute. |
lookup_user | At least one of: upn (string), sam_account_name (string), employee_id (string) | Look up a user by UPN, SAM Account Name, or Employee ID and return their DN, SAM Account Name, UPN, display name, employee ID, and objectGUID |
set_manager | resource_id (resource ID, required), plus exactly one of: manager_resource_id (resource ID) or clear_manager (bool) | Set or clear the manager attribute on a user. The handler returns the resulting manager_dn as an output for observability; manager_dn is not an input. |
| Action name | Additional fields | Description |
|---|---|---|
update_profile | user_id (resource ID, required), plus optional string fields (see Push attributes under User profile attributes), and custom_attributes (map of raw AD attribute names to values) | Update a user’s profile attributes. Empty values clear the attribute in AD. |
move_ou | user_id (resource ID, required), target_ou (string, required) | Move a user to a different Organizational Unit. Automatically handles CN collisions by appending a numeric suffix. |
| Action name | Additional fields | Description |
|---|---|---|
create | name (string, required), organizationalUnit (string, required), plus optional: sAMAccountName, description, groupScope (global/domain_local/universal), groupType (security/distribution), managedByUser (resource ID), managedByGroup (resource ID), mailEnabled (bool), primaryEmailAddress, emailAliases (string list), hideFromGAL (bool), gidNumber (int), userMembers (resource ID list), groupMembers (resource ID list) | Create a new AD group with optional initial members, mail settings, and POSIX attributes |
Resources
- Official download center: For stable binaries (Windows/Linux) and container images.
Gather Active Directory credentials
Create a service account
Create a dedicated AD service account for the connector (for example,
svc-baton). A standard domain user account with read access is sufficient for sync-only operation.Entitlement provisioning permissions
For entitlement provisioning support, the service account needs delegated rights to manage group membership.Right-click on your forest root (or a specific OU if you only want to provision into groups in that OU) and select Delegate Control.
For each protected group: right-click the group, click the Security tab, click Advanced, click Add, select the service account as the principal, and grant Write Members permission.
Account provisioning permissions
Your service account is now ready. Continue to the connector configuration.
Configure the Active Directory connector
The Active Directory connector is self-hosted — it runs on a Windows or Linux server in your environment with direct network access to your domain controllers. Once installed and configured, Baton runs as a Windows service (or Linux daemon). The service maintains contact with C1, syncs and uploads data at regular intervals, and passes that data to the C1 UI for access reviews and access requests.Requirements
Host server (Windows Server or Linux):- 2-4 vCPU
- 4-8 GB RAM
- Minimum OS disk space for the binary (~60 MB) plus room for logs and the local sync database
- The connector runs as a Windows service or Linux daemon — a dedicated host is not required
- Outbound TCP/443 to your C1 tenant
- Outbound LDAP (TCP/UDP 389), LDAPS (TCP 636), and Kerberos (TCP/UDP 88) to each domain controller the connector talks to. Global Catalog adds TCP 3268 / 3269. For the full multi-domain port list, see Multi-domain and cross-forest sync.
- An Active Directory service account with appropriate permissions (see Gather Active Directory credentials above)
- The Connector Administrator or Super Administrator role in C1
File locations
Windows (the default install path;%PROGRAMDATA% is typically C:\ProgramData):
- Binary:
C:\Program Files\ConductorOne\baton-active-directory.exe - Config:
%PROGRAMDATA%\ConductorOne\baton-active-directory\config.yaml— auto-discovered at startup ifBATON_CONFIG_PATHis not set - Log:
%PROGRAMDATA%\ConductorOne\baton-active-directory\baton.log
%PROGRAMDATA%\ConductorOne so the connector can write to the log file. If the connector can’t write to this directory, the service will fail to start.
Linux: there is no default config location — set BATON_CONFIG_PATH (or pass --config-file) to wherever you deploy config.yaml. A common pattern is /etc/baton-active-directory/config.yaml with the log redirected via systemd journal or --file.
Step 1: Set up a new Active Directory connector in C1
Choose how to set up the new Active Directory connector:
- Add the connector to a currently unmanaged app (select from the list of apps that were discovered in your identity, SSO, or federation provider that aren’t yet managed with C1)
- Add the connector to a managed app (select from the list of existing managed apps)
- Create a new managed app
Set the owner for this connector. You can manage the connector yourself, or choose someone else from the list of C1 users. Setting multiple owners is allowed.If you choose someone else, C1 will notify the new connector owner by email that their help is needed to complete the setup process.
Click Rotate to generate a new Client ID and Secret.Carefully copy and save these credentials. You will need them when configuring the connector.
Step 2: Install and configure baton-active-directory
Option 1: MSI installer (recommended)
Download the latest Windows MSI from the C1 download center.
Run the MSI on the host designated to run the connector. The installer prompts for your AD domain, C1 credentials, and connection settings, then writes the YAML config (with DPAPI-encrypted secrets by default) and registers the Windows service with auto-restart recovery.
After install, a Configuration Editor shortcut is available from the Start Menu if you need to change settings later. See GUI Configuration Editor below.
Option 2: CLI setup command
From an elevated Command Prompt (The setup command checks for a config file, offers to copy the binary to Program Files, optionally configures a service account, and registers the Windows service with auto-start and recovery.First-run behavior: if no config exists,
cmd.exe), run:setup launches the GUI Configuration Editor in a separate window. Fill in your domain, base DN, and C1 credentials, click Save, and close the editor — setup resumes in cmd.exe and finishes registering the service.For non-interactive setup (no prompts, LocalSystem account, no editor), use: baton-active-directory.exe setup -yGrant the service account Modify folder permissions to
C:\ProgramData\ConductorOne so it can write to the log file.baton-active-directory setup and the Windows installer now apply this grant automatically when you specify a non-LocalSystem service account. This step is only required if you change the service account later via services.msc or sc config.Launch the Services console, locate the service named baton-active-directory, and configure it:
- Double-click to open properties
- Change the Startup type to Automatic
- Navigate to the Log On tab and click This account
- Click Browse, enter your service account name, and click Check Names
- Enter the service account password and confirm it
- Click Apply
- Navigate back to the General tab and click Start
Step 3: Manage the Windows service
Use the following commands to manage the service (all require administrator privileges):
- To start the service:
baton-active-directory.exe start - To stop the service:
baton-active-directory.exe stop - To check the status:
baton-active-directory.exe status - To remove the service:
baton-active-directory.exe remove(config and binary are preserved)
The connector syncs current data, uploads it to C1, and prints a Task complete! message when finished.
Reference
The sections below cover advanced configuration, troubleshooting, and customization. Skim them only when you need to tune the connector beyond defaults.Configuration reference
All fields below can be set in the YAML config file. Unless noted, each field also maps to an equivalent CLI flag (--field-name) and environment variable (BATON_FIELD_NAME).
Example minimal config:
| Field | Type | Default | Description |
|---|---|---|---|
domain | string | (required) | Fully-qualified Windows domain. Example: baton.example.com |
base-dn | string | (required) | Base DN for LDAP searches. Example: DC=baton,DC=example,DC=com |
sitename | string | (any reachable DC) | AD site name. When set, scopes DC discovery to an available DC in that site. Example: US-DC-01 |
mode | string | winldap (Windows) / ldap (Linux) | Connection mode: ldap (Go library) or winldap (Windows wldap32.dll system calls) |
ldaps | bool | false | Enable LDAPS (TLS). Required for account provisioning. |
ldaps-port | int | 636 | LDAPS port. Use 3269 for Global Catalog over LDAPS. |
ldaps-skip-verify | bool | true | Skip LDAPS certificate validation (go-ldap mode only; ignored in WinLDAP). |
sync-scope | string | Standalone | Standalone or GlobalCatalog. GlobalCatalog is sync-only — provisioning is not supported and some profile fields are unavailable. |
| Field | Type | Default | Description |
|---|---|---|---|
bind-type | string | external | external (Kerberos/GSSAPI, uses the service account running the Windows service — no bind-user or bind-password needed) or simple (username/password) |
bind-user | string | — | Full DN of the bind user (required for simple) |
bind-password | string (secret) | — | Password for the bind user (required for simple). On Windows, can be stored as dpapi:… for DPAPI encryption. |
| Field | Type | Default | Description |
|---|---|---|---|
user-search-dn | string | base-dn | DN to search for users |
user-search-filter | string | (&(objectCategory=person)(objectClass=user)) | LDAP filter for users |
group-search-dn | string | base-dn | DN to search for groups |
group-search-filter | string | (objectCategory=group) | LDAP filter for groups |
skip-ous | list of DNs | — | OUs to exclude. Mutually exclusive with only-ous. |
only-ous | list of DNs | — | OUs to include. Mutually exclusive with skip-ous. |
custom-user-attributes | list of strings | — | Extra LDAP attributes to include in the user profile (for example, githubUserName) |
sync-extension-attributes | bool | false | Sync extensionAttribute1–extensionAttribute15 on every user profile. Equivalent to listing all 15 names in custom-user-attributes. |
use-display-name | bool | false | Use the LDAP displayName attribute as the C1 resource label instead of cn. Falls back to cn when displayName is empty. Applies to user and gMSA resources. |
| Field | Type | Default | Description |
|---|---|---|---|
enable-gmsa-sync | bool | false | Enable syncing of Group Managed Service Accounts |
gmsa-search-dn | string | base-dn | DN to search for gMSAs. Example: CN=Managed Service Accounts,DC=example,DC=com |
gmsa-search-filter | string | (objectClass=msDS-GroupManagedServiceAccount) | LDAP filter for gMSAs |
| Field | Type | Default | Description |
|---|---|---|---|
provisioning | bool | false | Enable Grant/Revoke and account provisioning. Required for any write operation. |
powershell-actions | map | — | Custom PowerShell-backed actions. See Custom PowerShell actions. |
| Field | Type | Default | Description |
|---|---|---|---|
client-id | string | (required) | C1 client ID. On Windows, can be stored as dpapi:…. |
client-secret | string (secret) | (required) | C1 client secret. On Windows, can be stored as dpapi:…. |
| Field | Type | Default | Description |
|---|---|---|---|
additional-domains | list of maps | — | Additional trusted domains to sync. YAML-only — not exposed as a CLI flag or environment variable. See Multi-domain and cross-forest sync. |
baton-active-directory --help to see every flag with its current default.
Multi-domain and cross-forest sync
The connector can sync across multiple AD domains and forests in a single run. Add additional trusted domains to your config using native YAML syntax:The
additional-domains field is YAML-only — it cannot be set via CLI flags or environment variables.Supported trust types
- 1-way outbound trust (primary trusts remote, or remote trusts primary)
- 2-way trust
- Cross-forest trust
- External (non-transitive) trust
Prerequisites
1. Active Directory trust. A trust relationship must exist between the domains. For Kerberos (bind-type: external), the connector’s domain must be trusted by the remote domain. For simple bind, any network connectivity is sufficient.
Verify with:
| Port | Protocol | Purpose |
|---|---|---|
| 389 | TCP/UDP | LDAP (plaintext with signing) |
| 636 | TCP | LDAPS (TLS) |
| 88 | TCP/UDP | Kerberos KDC (external/Kerberos auth) |
| 3268 | TCP | Global Catalog (optional) |
| 3269 | TCP | Global Catalog over SSL (optional) |
- Option A — Automatic cross-forest trust: With a 2-way trust and Enterprise CAs in both forests, the remote root CA is typically published to AD automatically.
-
Option B — Manual import: Export the remote CA and import it into the connector machine’s Trusted Root CAs (machine store):
-
Option C — Skip validation (go-ldap mode only, lab/testing): Set
ldaps-skip-verify: true. This has no effect in WinLDAP mode, which always validates against the Windows certificate store. -
Option D — Plain LDAP with signing: Omit
ldaps: truefor the additional domain and connect over TCP/389 with LDAP signing.
bind-type: external). On Windows domain-joined hosts, cross-realm Kerberos is typically automatic when a forest or external trust exists. The connector targets the remote DC’s hostname (for example, DC01.remote-forest.example.com) rather than the domain FQDN, because the SPN (ldap/DC01…) is registered under the DC’s machine account.
On Linux, add the remote realm to /etc/krb5.conf:
Authenticated Users has read access to most AD objects by default, which is usually sufficient. For gMSA provisioning in an additional domain, set read-only: false on the domain entry and grant the bind account write access to msDS-GroupMSAMembership.
Additional domain fields
Each entry underadditional-domains supports the following fields:
| Field | Type | Default | Description |
|---|---|---|---|
domain | string | (required) | FQDN of the remote domain |
base-dn | string | auto-discovered via rootDSE | Base DN for LDAP searches |
bind-type | string | external | external (Kerberos) or simple |
bind-user | string | — | Bind DN (required for simple) |
bind-password | string | — | Bind password (required for simple) |
ldaps | bool | inherits primary | Omit to inherit from the primary domain; set true or false to override |
ldaps-port | int | 636 | LDAPS port |
read-only | bool | see below | Prevent write operations (Grant/Revoke) to this domain |
user-search-dn | string | base-dn | Override user search base |
user-search-filter | string | primary filter | Override user search filter |
group-search-dn | string | base-dn | Override group search base |
group-search-filter | string | primary filter | Override group search filter |
skip-ous | list | — | OUs to exclude |
only-ous | list | — | OUs to include (mutually exclusive with skip-ous) |
gmsa-search-dn | string | base-dn | Override gMSA search base |
gmsa-search-filter | string | primary filter | Override gMSA search filter |
read-only default: When omitted, read-only defaults to true unless bind-type is explicitly set (in which case it defaults to false). Providing domain-specific bind credentials signals that writes are intended. Set read-only explicitly to override.
How it works
- Resource enumeration: the connector paginates across all configured domains — the primary completes first, then each additional domain. All resources land in a single unified sync.
- Cross-domain DN lookups (group members, managers, Foreign Security Principals): the DN’s domain suffix is matched against your configured domains to route the lookup.
- Cross-domain SID lookups (gMSA ACLs, primary group SIDs): the SID is resolved against each configured domain in order until a match is found.
- NetBIOS names (needed for down-level logon display): each forest’s
configurationNamingContextis queried forcrossRefentries. On Windows, the primary domain also uses the nativeDsCrackNamesWAPI; additional domains always fall back to LDAPcrossRef.
Diagnosing issues
Run the built-in test harness to check DC resolution, TLS handshake, certificate validation, and LDAP bind for every configured domain:- LDAP error 81 (“Server Down”) on the remote domain — TLS handshake failure, almost always a missing CA trust. Import the remote root CA (Option B above) or use plain LDAP with signing.
- LDAP result code 32 (“No Such Object”) during the grants phase — the connector hit a DN or SID on a remote domain that isn’t configured. Add the missing domain to
additional-domains. - “baseDN auto-discovery failed” — can’t reach the remote rootDSE. Check DNS and network, or set
base-dnexplicitly. - “SASL bind failed” / “Cannot obtain Kerberos ticket” — cross-realm Kerberos isn’t working. Verify the trust, DNS SRV records, and that you’re targeting the DC hostname rather than the domain FQDN. As a fallback, switch that domain to
bind-type: simple. - “netbios domain is empty” warnings — the NetBIOS cache couldn’t load crossRef entries. Confirm read access to the remote
configurationNamingContext.
Performance
Each additional domain adds to the sync cycle. The connector completes one domain fully before moving on; LDAP pagination (1000 entries per page) is used throughout; total sync time scales linearly with the number of domains and objects.gMSA sync
To sync Group Managed Service Accounts, enable the flag in your config:password_retrieval entitlement representing which principals are authorized to retrieve the managed password. Grant and Revoke operations modify the gMSA’s msDS-GroupMSAMembership security descriptor ACL.
OU filtering
You can restrict which Organizational Units the connector syncs by usingskip-ous or only-ous (mutually exclusive):
Custom user attributes
By default, the connector syncs a standard set of AD user attributes. Usecustom-user-attributes to include additional AD attributes in the user profile for account correlation:
Casing: LDAP attribute names are case-insensitive (RFC 4512 §2.5), so configuring
mail, Mail, or MAIL all reference the same AD attribute. The connector matches the operator’s configured casing case-insensitively against the entry’s returned attribute names. The profile-map key preserves the operator’s configured casing — so a config of Mail produces profile["Mail"], not profile["mail"] — predictable for downstream consumers reading the C1 profile JSON.sync-extension-attributes: true instead of listing all 15 names manually:
Resource label (cn vs displayName)
By default, the connector uses the LDAP cn attribute as the C1 resource label. AD often stores cn in Last, First order while displayName carries the operator-curated First Last form. To use displayName as the C1 Identity Name, set:
cn when displayName is empty or whitespace. Applies to both user and gMSA resources. This setting is off by default to preserve existing Identity Names on upgrade — flipping it changes resource labels across access reviews and audit history.
Custom PowerShell actions
You can define additional connector actions backed by PowerShell scripts:string, int, or bool.
User profile attributes
Synced user profiles carry the standard AD attributes (display name, email, manager, department, etc.) plus three groups of extra fields the connector populates from AD: read-only state booleans, FILETIME timestamps, and any custom attributes you list incustom-user-attributes.
Read-only state attributes
Four booleans are surfaced on every user profile:| Field | Source | Meaning |
|---|---|---|
password_never_expires | stored userAccountControl | Static admin config (DONT_EXPIRE_PASSWD bit) |
password_not_required | stored userAccountControl | Static admin config (PASSWD_NOTREQD bit) |
account_locked | computed msDS-User-Account-Control-Computed | Live state — matches what ADUC and Get-ADUser show, accounting for lockoutDuration |
password_expired | computed msDS-User-Account-Control-Computed | Live state — accounts for max-password-age policy |
STATUS_DISABLED in C1 (backwards-compatible). To keep locked accounts as STATUS_ENABLED and report lockout state solely via the account_locked field above, set:
Timestamp attributes
The connector parses every well-known AD time attribute to an RFC3339 (UTC) timestamp and surfaces it under a canonical*At profile key. The canonical naming convention is raw AD attribute name + At.
| Canonical profile key | Source AD attribute(s) | Source format |
|---|---|---|
pwdLastSetAt | pwdLastSet | FILETIME |
accountExpiresAt | accountExpires | FILETIME |
lockoutTimeAt | lockoutTime | FILETIME |
badPasswordTimeAt | badPasswordTime | FILETIME |
lastLoginAt | lastLogon or lastLogonTimestamp | FILETIME |
whenChangedAt | whenChanged | generalizedTime |
whenCreatedAt | whenCreated | generalizedTime |
0) or “never expires” (0x7FFFFFFFFFFFFFFF for FILETIME) cause the parsed *At key to be omitted — no year-1601 or year-30828 dates leak into a profile view.
Raw values for operator-configured attributes: if you include one of the source AD attribute names above in custom-user-attributes, the raw value is also surfaced under the operator-requested key (preserving the YAML contract for downstream tooling that reads by attribute name) — except when the value is a sentinel, in which case the raw is also suppressed (the meaningless 19-digit integer would only confuse reviewers).
| Operator config | Raw value type | Profile contains |
|---|---|---|
custom-user-attributes: [pwdLastSet] | valid FILETIME | pwdLastSet (raw) + pwdLastSetAt (parsed) + passwordLastSet (legacy alias) |
custom-user-attributes: [accountExpires] | sentinel 9223372036854775807 | neither key (both suppressed) |
custom-user-attributes: [lastLogon] | sentinel 0 | neither key (both suppressed) |
custom-user-attributes: [whenChanged] | valid generalizedTime | whenChanged (raw) + whenChangedAt (parsed) |
(default — not listed in custom-user-attributes) | any non-sentinel | parsed *At only |
pwdLastSet and lockoutTime are not replicated to the Global Catalog, so they may be missing under sync-scope: GlobalCatalog. badPasswordTime is local to each domain controller and not replicated at all — its value reflects the DC the connector talked to during the sync. lastLogon is also per-DC and not replicated; the connector prefers lastLogonTimestamp (replicated) when both are present.Backwards-compat aliases (v0.5.5 transition): for one release, the connector also emits each renamed FILETIME canonical key under its pre-v0.5.5 name —
passwordLastSet (alongside pwdLastSetAt), lockedOutAt (alongside lockoutTimeAt), and lastBadPasswordAt (alongside badPasswordTimeAt) — so downstream automations reading the older keys continue to work. The new generalizedTime keys (whenChangedAt, whenCreatedAt, lastLoginAt) have no legacy aliases since they’re introduced in this release. Legacy aliases will be dropped in a future release; new consumers should target the canonical *At form.Push attributes
Theupdate_profile resource action accepts 23 named string fields, each named after the canonical AD attribute it targets. The platform’s push-rule UI shows the AD attribute name in its “App attribute” column, so the value you map to in C1 corresponds directly to the attribute AD will receive. Attributes outside this list (including extensionAttribute1 through extensionAttribute15) must be set via the custom_attributes map field on update_profile, or via the update_user_attrs global action.
| AD attribute | Category | Notes |
|---|---|---|
givenName | Identity | First name |
middleName | Identity | Middle name |
sn | Identity | Last name / surname |
displayName | Identity | Operator-curated full name |
sAMAccountName | Identity | Pre-Windows-2000 login |
userPrincipalName | Identity | UPN login, e.g. user@domain.com |
mail | Contact | Email address |
telephoneNumber | Contact | Primary phone |
mobile | Contact | Mobile phone |
title | Organization | Job title |
department | Organization | Department |
division | Organization | Division |
company | Organization | Company |
description | Organization | Free-form description |
employeeID | Organization | Employee ID (string) |
employeeNumber | Organization | Employee number |
employeeType | Organization | Employment type |
l | Address | City / locality |
st | Address | State or province |
streetAddress | Address | Street address |
postalCode | Address | Postal / ZIP code |
c | Address | Country code, ISO 3166 alpha-2 |
physicalDeliveryOfficeName | Address | Office location |
Pre-v0.5.5 push rules used snake_case field names (
first_name, display_name, email, etc.). Those rules continue to work — the connector resolves both the canonical AD names listed above and the legacy snake_case aliases to the same target attribute. New push rules should prefer the AD-canonical names so the C1 UI’s “App attribute” column matches what AD actually expects.update_user_attrs global action recognizes the 23 fields above (plus their legacy snake_case aliases for backwards compatibility), plus 15 extension attribute names (extensionAttribute1 through extensionAttribute15 — also accepted as extension_attribute_1–extension_attribute_15). Any attribute name not in the combined list is passed through as a raw AD attribute name, allowing direct access to any writable AD attribute.
Troubleshooting “search returned 0 entries”
When the connector logswinldap_ldap_search: search returned 0 entries at INFO level, it means a configured search base (typically user-search-dn or group-search-dn) produced no entries. The log line includes base, filter, and scope so you can correlate which search returned empty. There are three common causes:
-
The search filter is too narrow — verify with an
ldapsearchorGet-ADUser -LDAPFilterfrom the same DC the connector is bound to. If they also return zero, the filter or the DN is wrong. -
The DN doesn’t exist as written — the connector logs the exact
basestring sent to AD. Copy it verbatim intodsquery * "<base>" -limit 1on the DC to confirm. -
The bound DC isn’t authoritative for the search base — when you narrow
user-search-dnorgroup-search-dninto a delegated OU, a child-domain OU, or a Read-Only DC subtree, the bound DC may answer withLDAP_REFERRAL(result code 10) orLDAP_PARTIAL_RESULTS(9) instead of entries. The connector surfaces this case explicitly: alongside the zero-entries INFO, look for a WARN log of the formldap_ext_search: non-success result code_name=LDAP_REFERRAL r1=10. The same condition surfaces in the C1 sync-failure panel asLDAP error: LDAP_REFERRAL, so it’s identifiable from the platform side too without needing log access. The connector does not chase referrals. WinLDAP’s referral-chasing option (LDAP_OPT_REFERRALS) is explicitly disabled — it has to be, to work around an AD pagination bug where chased subordinate referrals confuse the paging control and corrupt the page cookie. Re-enabling chasing would also introduce an authentication-security risk (chased referrals can authenticate against arbitrary servers). The remediation is to adjust the search configuration so the bound DC is authoritative for the subtree:- Widen the search base (e.g. move from
OU=Sub,OU=Parent,DC=corp,DC=example,DC=comup toOU=Parent,...or to the partition rootDC=corp,DC=example,DC=com). - Configure a different bind DC that is authoritative for the OU — pass
--sitenameto scope discovery to a site that includes an authoritative DC, or set upadditional-domainsif the subtree belongs to a different partition. - For multi-domain forests, use
additional-domainsso the connector binds separately against each partition’s authoritative DC instead of chasing referrals from one DC.
- Widen the search base (e.g. move from
LDAPS troubleshooting
LDAPS settings (ldaps, ldaps-port, ldaps-skip-verify) live in the Configuration reference. This section covers the parts that don’t fit in a flag table: how the two connection modes treat certificate trust differently, and how to diagnose handshake failures.
Certificate trust depends on connection mode
Theldaps-skip-verify flag behaves differently depending on mode:
-
WinLDAP mode (default on Windows): TLS validation is delegated to the Windows certificate store.
ldaps-skip-verifyhas no effect — the OS always validates againstCert:\LocalMachine\Root. To trust a CA, import its root certificate into the Windows machine store: -
LDAP mode (Go library, default on Linux): TLS validation uses Go’s TLS stack.
ldaps-skip-verify: true(the default) disables hostname and chain validation, which lets self-signed AD certs work in lab environments. For production, setldaps-skip-verify: falseand either trust the CA at the OS level or import it into the connector machine.
mode: ldap doesn’t connect on a Windows host (typical when the AD server requires channel binding or LDAP signing), switch to mode: winldap.
Diagnose with test-ldaps
The connector ships a diagnostic command that exercises every configured domain end-to-end:
x509: certificate signed by unknown authority(go-ldap mode) — the connector machine doesn’t trust the CA that issued the DC certificate. Import the CA root or setldaps-skip-verify: truefor testing only.tls: failed to verify certificate: x509: certificate is valid for X, not Y— certificate Subject/SAN doesn’t include the hostname the connector is connecting to. Verify you’re targeting a DC hostname that matches the cert (DCs typically have multiple SAN entries).LDAP Result Code 81 "Server is unavailable"in WinLDAP mode — usually a TLS handshake failure where Schannel rejected the cert. Check the Windows event log underLDAP-ClientandSchannelsources for the rejection reason.- Connection times out on port 636 — port not open between the connector host and the DC, or LDAPS isn’t enabled on the DC. Confirm the DC has a Server Authentication EKU certificate in its Personal store.
test-ldaps checks every entry in additional-domains plus the primary, so a single run covers your whole topology.
DPAPI secrets encryption
On Windows, the connector can encrypt sensitive config values (client-id, client-secret, and bind-password) using Windows DPAPI machine-scoped encryption. Encrypted values are prefixed dpapi: in the YAML config and are decrypted transparently at startup. The installer encrypts by default; you can also encrypt an existing config manually:
GUI Configuration Editor
The connector includes a native Windows GUI for managing configuration without editing YAML files manually. It’s the same window thatsetup launches automatically on first run when no config exists (Option 2, Step 3) — you can also open it standalone at any time:
Lockout Status Behavior (locked-account-is-disabled), Sync Extension Attributes (sync-extension-attributes), and Use displayName as Resource Label (use-display-name). Secret fields (bind passwords, client secret) are write-only — entering a new value encrypts it via DPAPI on save; leaving a field blank preserves the existing encrypted value.