fix(ldap): do not disable every user when a sync returns no identifiers (#744)

disable_missing tested absence against the raw sync result without
checking that the search returned anything usable, so a search that
succeeds and yields nothing looked like "every user has been removed".

Connection and search errors were already safe, since synchronizeLdapUsers
returns before the disable phase. The gap is the successful-but-empty
case: a base_dn or sync_filter that stops matching, an unpopulated
replica, a field_map user_identifier naming an attribute the server does
not return, or a bind account that lost read access to the user subtree.
LDAP gives nothing to tell those apart from a directory that is genuinely
empty; they all answer success with zero entries.

Acting on it is not a database flag. TopicUserDisabled removes each
user's peers from the WireGuard device, the successful search means no
error is logged, and every message on the path was Debug while log_level
defaults to info, so the whole event was silent. It also repeats every
sync interval.

Refuse to disable anyone when no usable identifier came back, logging the
provider, entry count and identifier field. The guard counts identifiers,
not entries, so it covers the field_map case too. The per-user disable
line moves from Debug to Warn so a mass disable is audible even where the
guard does not fire.

The cost is that a directory intentionally emptied of users now disables
nobody. That is documented, along with the workaround: leave one account
matching sync_filter and everyone else is disabled as before.

Signed-off-by: clark-ja <37738506+clark-ja@users.noreply.github.com>
This commit is contained in:
Jacopo Clark
2026-09-01 22:38:31 +02:00
committed by GitHub
parent a0cb7d9d39
commit 64195a25c1
3 changed files with 208 additions and 11 deletions
@@ -827,6 +827,14 @@ Below are the properties for each LDAP provider entry inside `auth.ldap`:
#### `disable_missing`
- **Default:** `false`
- **Description:** If `true`, any user **not** found in LDAP (during sync) is disabled in WireGuard Portal.
- **Important**: As a safeguard, a synchronization that returns no usable user identifiers at all disables
nobody. LDAP cannot distinguish a directory that legitimately holds no matching users from a wrong `base_dn`,
a `sync_filter` whose group was renamed, a replica that is reachable but not yet populated, or a bind account
that has lost read access — all of them answer with success and zero entries. Disabling every user on that
signal would lock administrators out of a portal they may only reach over the VPN, so the sync logs an error
and skips the disable step instead.
If you intend to empty a group, keep one account matching `sync_filter`: the remaining users are then disabled
normally, and the account doubles as a canary, since its disappearance means the query itself is broken.
#### `auto_re_enable`
- **Default:** `false`