How to check in one minute
Ask the indexer who a user is, with that user's own credentials:
curl -sk -u someuser https://<indexer>:9200/_plugins/_security/authinfo
HTTP 200 with "backend_roles":[] means the directory accepted the user and no group matched; the user then has only what is mapped to every user, own_index by default. The dashboard logs users in through the indexer's security plugin, so this is the check that decides it.
Why it happens
The security plugin uses LDAP twice. The authc domain only checks that usersearch finds the user under userbase and that the password binds. Groups are read later, by the authz backend, and only decide roles. In the default roles_mapping.yml of the 4.14.7 image, own_index is mapped to users: "*", so an authenticated user always has a role.
Our test directory: alice in group wazuh-admins (mapped to all_access), bob only in ou=people. The authc part as most guides write it:
ldap:
http_enabled: true
transport_enabled: false
order: 1
http_authenticator:
type: basic
challenge: false
authentication_backend:
type: ldap
config:
hosts:
- "ldap.example.org:389"
bind_dn: "cn=admin,dc=example,dc=org"
password: "<bind password>"
userbase: "ou=people,dc=example,dc=org"
usersearch: "(uid={0})"
username_attribute: "uid"
| usersearch | alice (in group) | bob (not in group) |
|---|---|---|
(uid={0}) | 200, backend_roles ["wazuh-admins"], roles own_index, all_access | 200, backend_roles [], roles own_index |
with the memberOf filter below | 200, same as before | 401 Authentication finally failed |
Fix
Put the group into the authentication search, in config.yml of the security plugin:
usersearch: "(&(uid={0})(memberOf=cn=wazuh-admins,ou=groups,dc=example,dc=org))"
Load it with securityadmin.sh. The paths below are those of a package install; we ran the same command inside the Docker image, where config and certificates live under /usr/share/wazuh-indexer/:
/usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh \
-f /etc/wazuh-indexer/opensearch-security/config.yml -t config -icl -nhnv \
-cacert /etc/wazuh-indexer/certs/root-ca.pem \
-cert /etc/wazuh-indexer/certs/admin.pem \
-key /etc/wazuh-indexer/certs/admin-key.pem -h localhost
- OpenLDAP fills
memberOfonly with thememberofoverlay; check withldapsearch ... "(uid=bob)" memberOf. - Active Directory has
memberOfon users. The same idea there is written as(&(sAMAccountName={0})(memberOf=CN=Wazuh Admins,OU=Groups,DC=corp,DC=example)); that is the syntax, we did not run it against AD. - Several allowed groups:
(&(uid={0})(|(memberOf=...)(memberOf=...))).
Removed from the group, still in
After we removed bob from wazuh-admins, authinfo still returned 200 with all_access 3 seconds and 25 seconds later. Flushing the security cache made the next login a 401 at once:
curl -sk --cert admin.pem --key admin-key.pem -X DELETE \
https://<indexer>:9200/_plugins/_security/api/cache
When you revoke access in the directory, flush the cache if it has to take effect now.
Limits of what we measured
Single-node wazuh-indexer 4.14.7 in Docker with OpenLDAP (memberof overlay), measured through the indexer's _plugins/_security/authinfo; we did not drive the Wazuh dashboard login screen. Not tested: Active Directory, nested groups, LDAPS, other versions. We did not measure how long the security cache keeps a removed user; we only know it was longer than 25 seconds.