Active Directory and LDAP problems
Directory sign-in that fails, a bind that will not complete, and sync scopes that import the wrong people or nobody at all.
- Version: 0.4
- Role: admin_user
- Type: troubleshooting
Directory integration fails in two places that need to be told apart before anything else: the bind (can the appliance talk to the directory at all?) and the sync scope (does it import the right people into the right group?). A sign-in refusal can come from either, or from neither.
Note
While the directory is unreachable, local accounts keep working, and content policy and enforcement are unaffected — they never consult the directory. What stops is directory sign-in and group-membership updates, which freeze at their last synced state.
Find your symptom
| What you see | Runbook |
|---|---|
| A user cannot sign in, and you do not yet know whether the account is local or directory | A user cannot sign in |
| The connection test on the AD/LDAP page fails | The directory cannot be reached or bound |
| Sign-in says the directory is temporarily unavailable | The directory cannot be reached or bound |
| A sync scope will not run, or names a group that no longer exists | A sync scope will not run |
| The sync imported some people and removed nobody | A sync scope will not run |
| Deleting a tenant group is refused because a scope targets it | A sync scope will not run |
| Everyone is signed out at every gateway restart | The token signing secret is a placeholder |
Sign-in
A user cannot sign in
Four refusals are deliberately distinguishable — wrong credentials, an inactive account, an account managed by the directory, and a directory that could not be reached — because each needs a different person to act.
A user cannot sign in to the dashboard
You might see: Invalid credentials · Account is inactive · Account not found · User not found
Before you start
Checks
- 1
Establish whether this account is local or comes from the directory
expected · You can say which of the two it is.
- 2
For a directory account, check that the directory answered
expected · The directory test succeeds.
- 3
Read whether the account is active
expected · The account is active.
- 4
For a user who signs in and is immediately signed out again, check the token signing secret
diagnostics · posture.jwtSecretPlaceholder = false
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | wrong-authority (info) | Local accounts and directory accounts fail in different places. A directory account's password lives in Active Directory; the gateway never stores it and cannot change it, so a password reset attempted here is refused rather than silently ignored. Fix: Reset a directory account's password in the directory. Reset a local account's password from Tenants, which produces a reset link delivered by the configured mechanism. Verify: expected · Sign-in succeeds. |
| check 2 fails | directory-unreachable (critical) | When the directory cannot be reached the gateway says so explicitly rather than reporting a wrong password, because the two need opposite responses. Local accounts keep working throughout — content policy and enforcement never consult the directory. Fix: Follow The directory cannot be reached or bound. Verify: expected · It succeeds, and the user signs in. |
| check 3 fails | account-inactive (warning) | An inactive account is refused at sign-in with a distinct message. Deactivation is how an account is retired without deleting the detections and sessions attributed to it. Fix: Re-activate the account under Tenants, or point the user at the account they should be using. Verify: expected · Sign-in succeeds. |
| check 4 fails | jwt-secret-unstable (critical) | Tokens are signed with JWT_SECRET. Unset, the gateway derives an ephemeral per-boot secret, so every restart invalidates every session; a known placeholder value is worse still, because anyone with the source can mint an administrator token.Fix: Follow The token signing secret is a placeholder. Verify: diagnostics · posture.jwtSecretPlaceholder = false If that does not apply: Attach the support bundle. Include whether the user is local or directory-managed and the exact message shown — the four refusals above are deliberately distinguishable. |
The bind
The directory cannot be reached or bound
The gateway refuses to attempt a bind unless URL, bind DN and base DN are all present, so an
empty field fails instantly and looks exactly like a wrong password. Two other refusals are
security properties rather than faults: the bind password is resolved from the encrypted store,
then LDAP_BIND_PASSWORD_FILE, then LDAP_BIND_PASSWORD; and the stored password is only ever
sent to the host it was saved against, so testing a different URL requires retyping it.
Note
The stored bind password is encrypted with a key derived from JWT_SECRET. Changing that
secret invalidates the stored password, and it must be re-entered — this is easy to miss when
rotating the secret for an unrelated reason.
The directory cannot be reached or bound
You might see: LDAP url, bind DN and base DN must be configured · LDAP bind password file unreadable · Directory authentication is unavailable · The directory server could not be reached, so directory sign-in is temporarily unavailable.
Before you start
Checks
- 1
Read the three fields the connection cannot be attempted without
expected · All three are non-empty.
- 2
Check where the bind password is coming from
expected · One of the three resolves to a password the directory accepts.
- 3
If you are testing a URL you just typed, check whether you also re-entered the password
expected · Either the URL is unchanged, or you typed the bind password into the test form.
- 4
Check that the directory host answers from the appliance
expected · The port answers and, for LDAPS, the certificate chain validates.
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | incomplete-config (warning) | The gateway will not guess a base DN or bind as anonymous. A saved configuration missing any of URL, bind DN or base DN is refused at validation, which is why the failure appears instantly and identically for a wrong password and an empty field. Fix: Fill all three on Settings → AD / LDAP and save, then run the connection test. See AD / LDAP. Verify: expected · It succeeds. |
| check 2 fails | bind-password-unreadable (critical) | Where the password comes from a mounted secret file, the gateway reads it at bind time. A file the container cannot read produces an explicit "unreadable" refusal rather than a wrong-password error — the two need different fixes. Fix: Correct the mount and the file's permissions so the gateway container can read it, or store the password through the dashboard instead. The stored password is encrypted with a key derived from JWT_SECRET, so changing that secret invalidates it and it must be re-entered.Verify: expected · It succeeds. |
| check 3 fails | password-not-sent-to-new-url (info) | The saved bind password is write-only and is only sent to the host it was saved against. Testing a different URL without retyping it would hand the stored secret to a host you just named, so the gateway refuses instead. Fix: Type the bind password into the test form alongside the new URL. Verify: expected · It succeeds. |
| check 4 fails | directory-not-reachable (critical) | While the directory is unreachable, directory sign-in fails and group membership stays frozen at its last synced state. Local accounts keep working, and content policy and enforcement are unaffected — they never consult the directory. Fix: Open the path from the appliance to the domain controller, or point the URL at a controller that is reachable from this subnet. For LDAPS, install the issuing CA on the appliance so the handshake completes. Verify: expected · Both succeed. If that does not apply: Attach the support bundle and the exact test message. Include whether the URL is ldap:// or ldaps:// and which controller it names. |
Sync scopes
A sync scope will not run
A scope maps a directory group into a CID tenant group and assigns a role. Three of its refusals are protective: a target group that has been deleted, a role that may not be granted from a directory scope at all, and a directory that answered only part of the query.
On a partial answer the sync imports and updates what it saw and removes nobody — removing on incomplete data would deprovision people whose entries simply were not returned.
A directory sync scope will not run, or imports the wrong people
You might see: Sync scope not found · This sync scope is disabled. Re-enable it before syncing. · Sync scope is mapped to a CID tenant group that no longer exists · Sync scope would assign a role which cannot be granted from a directory scope
Before you start
Checks
- 1
Read the scope's state before anything else
expected · The scope is enabled.
- 2
Check the CID tenant group the scope maps into
expected · The target group exists.
- 3
Read the role the scope assigns
expected · The role is one of those the message lists as allowed.
- 4
Read the result counts of the last run
expected · The run completed rather than reporting a partial answer.
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | scope-disabled (info) | A disabled scope is skipped rather than run and failed, so membership simply stops changing. Nothing in the product degrades; it just stops tracking the directory. Fix: Re-enable the scope, then run it once by hand and read the counts. Verify: expected · It completes and reports counts. |
| check 2 fails | target-group-deleted (warning) | The scope names a CID tenant group by id. Deleting that group leaves the scope pointing at nothing, and the sync refuses rather than creating a replacement group and quietly changing who can see what. Deleting a group that is a sync target is refused for the same reason. Fix: Re-point the scope at an existing group, or clear the mapping, then sync again. See Tenant groups. Verify: expected · It completes without naming a missing group. |
| check 3 fails | role-not-grantable (warning) | A directory group is an authorisation input, not an authorisation decision. Roles that administer the appliance itself cannot be handed out by editing a group in AD, so the scope is refused at save time rather than silently downgrading the role. Fix: Set the scope's role to one of those the refusal lists, and grant the others in CID directly. Verify: expected · It saves. |
| check 4 fails | partial-directory-answer (warning) | When the directory answers only part of a query, the sync imports and updates what it saw and removes nobody. Removing on a partial answer would deprovision people whose entries simply were not returned, which is worse than leaving membership stale. Fix: Fix the directory-side limit — usually a server-side size or time limit on the search, or a base DN broader than the controller will page through — then run the scope again. Verify: expected · The run completes without the partial-answer warning. If that does not apply: Attach the support bundle and the scope's base DN, filter and reported counts. |
What the docs cannot tell you
- Whether the directory itself is refusing the bind or the network is dropping it. The gateway sees a failed connection in both cases. Confirm from the domain controller's own logs.
- Whether a size or time limit is truncating the search. The partial-answer path is detected, but the limit that caused it lives on the directory server.
Related
- AD / LDAP settings — configuring the connection and the scopes.
- Tenant groups — what a sync scope writes into.
- Access control — which roles a directory scope may grant.
Last updated on