Troubleshooting¶
Start with Configuration › Diagnostics: it names most problems directly and never changes anything. Then read the logs:
docker logs steward-<name>-steward-1 (console), steward-<name>-steward-pwd-1 (password service), steward-<name>-acme-1
(ACME certificate), steward-<name>-cloudflared-1 (Cloudflare Tunnel).
Installation and exposure¶
| Symptom | Cause and fix |
|---|---|
| The guided install does not start; the script prints "Guided install: run deploy/install.sh in a terminal" | The wizard starts only in an interactive terminal and without options. Run sudo deploy/install.sh in a real terminal (not through a pipe or a non-interactive SSH command), or force it with --interactive. |
| "input ended: aborted, nothing was changed" | The wizard's input closed before the last question (Ctrl+D, a pipe, a dropped SSH session). Nothing was written: run it again. |
| You want to check options before installing | Add --dry-run: the options are checked and the resolved plan is printed (for secrets, only where they come from), and nothing is pulled or written. The wizard prints the equivalent command at the end, ready to re-use. |
| An option is refused (exit code 2) | The message names the option and what it expects; deploy/install.sh --help lists them all. Typical: a public CA cannot issue for names such as *.local or *.corp, a secret file is missing on the first install, --wildcard without DNS-01. |
| ACME issuance failed | The script prints the last acme.sh lines and the likely cause (DNS API refused, DNS propagation, rate limit, CAA, HTTP-01 unreachable): see HTTPS certificate. Steward was not started. |
| "--exposure cloudflare-tunnel publishes no port: HTTP-01 cannot work" | Behind Cloudflare Tunnel nothing listens on the host, so the CA cannot reach port 80. Use --tls acme-dns (or certificate files). |
| Cloudflare Tunnel: the hostname shows a Cloudflare error page | docker logs steward-<name>-cloudflared-1. Token refused: the token is wrong or the tunnel was deleted; re-run install.sh with a new --tunnel-token-file. Tunnel connected but the origin fails: the tunnel's public hostname must point to service HTTPS steward:8443 with Origin Server Name set to the console's name (or No TLS Verify for a self-signed lab certificate). The host must be allowed to connect out to Cloudflare. |
| Cloudflare Tunnel: anyone reaching the hostname gets the Steward sign-in page | No Cloudflare Access application covers the hostname. Create one (Zero Trust › Access › Applications › Self-hosted, domain = the console's name, a policy limited to your people) before using the console. See Exposure. |
| Behind a reverse proxy or a tunnel, sign-ins are throttled for everyone at once | Steward never trusts forwarded headers (X-Forwarded-For): every request seems to come from the proxy, so the per-IP sign-in limit (Throttle:PerIpAttempts, 20 attempts per Throttle:PerIpWindow, 5 minutes) is shared by all users behind it. Per-account limits still apply to each account. Put identity checks in front (Cloudflare Access, Teleport) so only authorized people reach the sign-in page; raise Throttle:PerIpAttempts only with care. Requests carrying a valid Teleport identity are rate-limited per Teleport user instead. |
| The browser shows a certificate error | --tls selfsigned (labs only), or a certificate that does not cover the name typed: Configuration › Diagnostics lists the names it covers. See HTTPS certificate. |
| A DC name does not resolve inside the containers | The containers use the host's DNS. Fix the DNS, or map the name with install.sh --add-host <fqdn>:<ip> (kept across re-runs; --no-add-host removes the mappings). |
LDAPS¶
A DC answers LDAP error 81 ("server unavailable") whatever the cause, so the console runs its own TLS handshake to tell the causes apart:
| Diagnostic | Meaning | Fix |
|---|---|---|
| unreachable | No TCP connection to port 636 | Firewall or routing; is LDAPS enabled on the DC? |
| authority not configured | The certificate chains to a CA the console does not trust | Configure the issuing CA's PEM (Configuration › Directory › Certificate authority) |
| name does not match | The DC certificate has no SAN matching its FQDN | Re-issue the certificate with the DC's FQDN; never connect by IP address |
| expired / not yet valid | Certificate dates | Renew it; check the clock of the DC and of the host |
Sign-in¶
| Symptom | Cause |
|---|---|
| "Incorrect login or password" for everyone | The read account's password changed: diagnostics show its sign-in as refused. Rotate it in Configuration › Directory › Technical accounts. |
| "Privileged accounts (Domain Admins, Tier 0/1) cannot sign in to the console" | The account is privileged (Domain Admins, adminCount, SPN, Tier OU...). Intended: use a standard account. |
| "Active Directory cannot be reached right now" | No DC reachable, or the lockout policy cannot be read: the console fails closed. |
| Repeated failures stop being tried | Per-IP and per-account throttling: the console stops sending binds well below the domain lockout threshold. |
| Teleport: "The Teleport identity could not be verified" | Check Issuer, Audience (it must equal the app uri) and JwksUrl (Teleport sign-in). The container log gives the reason (Teleport JWT rejected (...)). |
Operators and rights¶
| Symptom | Where to look |
|---|---|
| An operator sees no users | No role with users.read on the right OU: check with the simulator (Delegations). |
| A user is missing from search results | Privileged or unclassifiable accounts are never listed, by design (diagnostics show whether the privileged groups are resolved). |
| "Active Directory refused the change" | The console allows it but the write account lacks the AD right: diagnostics show the missing rights. Export and apply the AD delegation again. |
| A member cannot be added to a group ("Active Directory refused") | The write account lacks Write property on member for groups of that OU: export and apply the AD delegation. |
| A group is missing from the Groups page | Protected groups (sensitive, VIP, nested in one, adminCount, built-in) are never listed; otherwise check the operator's groups.read scope with the simulator. |
| A member cannot be added or removed | The member must be a standard user the operator may read (users.read); VIP members need vip.manage; nobody changes their own memberships. |
| "Changed in the meantime" | Someone changed the value since the page was opened: nothing was written; reload and redo. |
| "This manager does not exist or is outside your scope" | The manager must be a standard user the operator may read. |
| No photo, only initials | No users.attributes.read on thumbnailPhoto, no photo, or not a JPEG of at most 100 KB. |
| "Last sign-in" looks old | lastLogonTimestamp replicates only every 9 to 14 days: approximate by design. |
| The export stops at 1000 users | Exports are bounded: refine the search or pick an OU. |
| No Disable / Enable button on a profile | The helpdesk (password service) is off, no delegation holds users.disable / users.enable on that OU, it is the operator's own account, or a VIP without vip.manage: check with the simulator. |
| Disable / enable: "Active Directory refused" | The reset account lacks Write property on userAccountControl for users of that OU: export and apply the AD delegation. |
Enable refused by AD (account_policy) |
AD refuses to enable an account whose password does not meet the policy (e.g. created without one): reset its password first. |
| The expiry date looks one day off | The console stores the end of the chosen day in Directory:AccountExpiryTimeZone (UTC by default), like ADUC: set your organisation's zone (e.g. Europe/Paris). |
| A deleted operator is back | AD operators come back without any role when they sign in again; Teleport operators come back with the roles their Teleport mappings grant. Disable them instead. |
| "Confirm your identity" keeps coming back | A step-up lasts 5 minutes (Session:StepUpWindow). |
| The step-up dialog only offers "Open Security" | The AD operator has no authenticator app yet: set one up on the Security page (AD password once), then retry. |
totp_invalid with the right code |
The code was already used (each code is accepted once: wait for the next one) or the phone's clock is off by more than 30 seconds. |
totp_throttled |
5 wrong codes in 5 minutes: wait, or have an administrator reset the authenticator. |
Instance¶
| Symptom | Fix |
|---|---|
| Container unhealthy after an upgrade | docker logs; with Database:AutoMigrate=false, run migrate; restore the backup if needed (Operations). |
| Setup wizard locked, or no administrator left | docker exec <container> dotnet Steward.Api.dll reset-setup --confirm (Recovery). |
| The Audit page says the chain is broken | Something modified the database outside the console. Keep the instance as it is, export the forwarded audit stream, investigate. |
Directory settings and failover¶
| Symptom | Cause and fix |
|---|---|
| "Not saved: the check failed on at least one controller" | The result per DC says why: unreachable (name or firewall), certificate not signed by this authority (wrong CA), name not in the certificate (SAN), password rejected (read account). Nothing was saved. |
| CA replacement refused | At least one DC does not present a chain to the new CA. During a migration, paste a PEM with both the old and the new CA. |
| Password rotation refused | One DC still has the old password (replication) or it was mistyped. Nothing was saved; the old password is still used. |
| A DC shows "Failing" | Its last connection failed; it is tried last for 60 seconds. Test each controller re-tests at once. Remove decommissioned DCs from the list. |
| Every operation is a few seconds slower once a minute | A configured DC is dead: each cooldown ends with one retry. Remove or fix it. |
| Discover via DNS finds nothing | The console host does not use the AD DNS. Set Directory:DiscoveryDnsServer to a DC address, or add the DCs by hand. |
| Removing a managed OU asks for a confirmation | Delegations or onboarding templates still use it (the list is shown). After confirming, they reach nothing until the OU is managed again. |