Troubleshooting
Find the symptom, then the cause. For any failed request, the response’s X-Correlation-ID finds every log line it produced: see Operations → Following one request.
The API does not start
The last lines of the log name the setting. The common ones:
No role mapping grants the Admin role
[ERR] Role mapping … names its group as 'Administrators' and grants Admin to nobody: mappings by name are ignored. …
[FTL] … No role mapping grants the Admin role by distinguished name, so nobody could administer this application. …
Cause. No mapping names the Admin group by DN. Either RoleMappings:GroupDns:Admin is not set, or the database holds mappings from an older build that name groups by CN (Administrators), which are ignored.
Fix. Set the Admin group’s real DN and restart:
RoleMappings__GroupDns__Admin="CN=Axia-Admins,OU=Groups,DC=corp,DC=example,DC=com"
At the next start, global mappings by name for the roles you list are converted to these DNs, once.
Get the DN right. The API checks the format, not that the group exists. A wrong DN is saved into the database, the API starts, and nobody can administer it. To find a group’s DN:
# Active Directory (PowerShell)
Get-ADGroup Axia-Admins | Select-Object DistinguishedName
# any LDAP
ldapsearch -H ldaps://dc01.corp.example.com -D 'svc-axia@corp.example.com' -W \
-b 'DC=corp,DC=example,DC=com' '(cn=Axia-Admins)' dn
Saved a wrong DN? Fix it with someone who holds roles.manage, or in the database:
sqlite3 nelsar-axia.db "SELECT Id, GroupName, RoleName FROM RoleMappings;"
sqlite3 nelsar-axia.db "UPDATE RoleMappings SET GroupName='cn=axia-admins,ou=groups,dc=example,dc=org' WHERE RoleName='Admin';"
On a development database you can also delete the file and let the API recreate it.
Note on placeholders. A placeholder such as "<DN of the admins group>" is safe: it is not a DN, so startup is refused before anything is written. A made-up but well-formed DN is not safe.
RoleMappings:GroupDns:X is '…', which is not a distinguished name
The value must be a full DN (CN=…,OU=…,DC=…), not a group name.
Jwt:Key … / Encryption:Key …
Missing, a placeholder (CHANGE_ME…), too short, or (for Encryption:Key) not base64 of 16, 24 or 32 bytes. Generate:
openssl rand -base64 48 # Jwt:Key
openssl rand -base64 32 # Encryption:Key
Ldap:Security is Ldaps … and Ldap:Port is 389
A configuration from before LDAPS was the default. Choose: port 636 (LDAPS), Security: StartTls on 389, or Security: None (plain LDAP, passwords readable on the network).
ActiveDirectory provider on Linux/macOS
ProviderType=ActiveDirectory needs Windows. Use StandardLdap, which works against AD.
appsettings.Development.json seems ignored
It is read only when ASPNETCORE_ENVIRONMENT=Development. The http launch profile sets that; the https profile runs as Production. dotnet run without a profile uses the first one (http).
Port already in use
Another API instance is running (ss -ltnp | grep 5063), or the console’s 8080/5500 is taken.
Signing in fails
| Response | Likely cause |
|---|---|
401 | Wrong password, or no account matches the login. Both look the same on purpose. Check the login with ldapsearch using the search filter. |
401 or 502 for every account | Check the bind account (Ldap:AdminUsername/AdminPassword): the login is looked up with it before the user’s own bind. The log has the directory’s error. |
429 “Too many requests” | auth-login limit: 5 per minute per address. Or auth-login-account: 5 failed sign-ins to this account in 15 minutes, from anywhere. Wait for Retry-After, or raise the limit under Rate limiting. Behind a proxy not listed in ReverseProxy, everyone shares one address bucket. |
502 “…no server configured” | Ldap:Server is empty and the request named no directory. |
502 unreachable | Network, firewall, wrong host/port, or the TLS handshake failed. |
400 about the directory | X-LDAP-Provider-Id names an unknown or inactive directory. |
Signs in, but only as User | No role mapping matches the account’s groups. Check that the mapping’s DN is the group the account is directly in (nesting is not followed), and that the group’s membership is visible: GET /api/v1/users/{id} shows groups. |
TLS errors
- Certificate not trusted: the directory’s CA is not in the host’s store. Set
Ldap:TrustedCaCertificatePathto the CA’s PEM. - Name mismatch:
Ldap:Servermust be a name in the certificate. Connecting by IP to a certificate issued fordc01.corp.example.comfails; use the name. - StartTLS fails: the server does not offer it on 389, or its certificate fails the same checks.
- Verification cannot be disabled. For a lab without TLS, use
Security: Noneon a directory on the same host only.
Listings look wrong
Exactly 1,000 entries, isComplete: false
The log shows Error enumerating LDAP … after 1000 entries. Size Limit Exceeded. Two possible causes:
- The application’s own limit. The LDAP library sends a default size limit of 1000 with every search, and the server applies it to a paged listing as a whole. Builds that include
PagedSearchConstraintsinStandardLdapProviderlift it; earlier ones stop every listing at- Update.
- The server’s limit for the bind account. Raise it (OpenLDAP
olcLimits … size.prtotal, ADMaxPageSize/MaxResultSetSize) or lowerUserSearchPageSize.
Fewer entries than expected, isComplete: false
The directory stopped the search: a time limit, a size limit, or a dropped connection. The log has the LDAP error.
A group is missing
posixGroup entries are not listed (they are still used for roles). Only group, groupOfNames and groupOfUniqueNames under the base DN are.
Dashboard tiles say “API unavailable” for groups or units
A known issue: the dashboard asks for 1,000 entries per page to count them, and the API’s maximum is 200, so it answers 400 (Page size must be between 1 and 200 in the log). The Groups and Organizational units pages themselves work.
Changes are refused
| Response | Cause |
|---|---|
403 | You lack the permission, or the target is protected: it holds more than you, carries adminCount, is in DirectoryProtection:ProtectedGroups, or is the bind account. The log line for the refusal says which. |
409 | Already exists; or deleting a unit that still has entries; or an e-mail in use by another account. |
502 “The directory rejected …” | The directory refused the write. Common: the bind account lacks the right (delegation/ACL); a schema violation (e.g. creating a computer on OpenLDAP, where person requires sn); a password the directory’s policy rejects; a Global Catalog port, which is read-only. |
502 on password change against AD | AD accepts unicodePwd only over an encrypted connection: use Ldaps. |
404 after a rename | Ids are derived from the DN; list again to get the new id. |
501 | Groups on the Windows ActiveDirectory provider; use StandardLdap. |
Bulk jobs
- Status
404: the API restarted; queued jobs live in memory and are lost. - Rows fail with “not found for update”: the
emailof an update row must find the account. - Every row fails with a permission message: the batch has create rows and you lack
users.create, or update rows and you lackusers.update.
Local test directory (deploy/dev)
permission denied … docker.sock: your user is not in thedockergroup. Usesudo, or add the user to the group (sudo usermod -aG docker $USER, then log in again).- Data changes do not appear: LDIF files load only on the first start of an empty directory:
down -v, thenup. - A disabled account binds with
ldapsearchbut cannot sign in: expected. OpenLDAP does not act onuserAccountControl; the API reads it and refuses the account. - More in Local test directory.
Tests
SigningIn_ToADirectoryWithNoServer_IsAGatewayFailure_NotAnUnexpectedError fails when your local Nelsar.Axia.Api/appsettings.json sets Ldap:Server: the test expects no default server, and the test host reads that file. CI has no such file. Run the tests with the file moved aside, or ignore that one locally.