Skip to content
Pages in Guides

Sudo mode and four-eyes approvals

Sudo mode before a destructive action, and a second person's approval for the root-equivalent ones.

Guide Updated View as Markdown

Part of Security.

Sudo mode

Destructive and credential-changing actions ask a console session to prove it is still its operator. The session already proved the account's password and second factor at sign-in, so confirming asks for one factor: POST /api/auth/elevate with a current TOTP or backup code, or a passkey (/api/auth/passkeys/elevate); for the master token its second factor, or the token itself when it has none. An account with no second factor cannot enter sudo mode. The console shows a "Confirm it's you" dialog and retries the action.

Sudo mode is a window that stays open while it is used, as GitHub's does. It opens for auth.sudo.idle_minutes (15) and every destructive action performed in it pushes the end out by that much again, but never past auth.sudo.max_minutes (120) from the moment it was confirmed, nor past the session's own end. Only an action that needed sudo mode extends it: reading the console, or leaving a tab polling, does not. The end is written at most once a minute. A session that was confirmed before this behaviour existed ends at the deadline it was given.

auth.sudo.require_password: true asks for the account's password together with the code, as 3.3 did; the passkey is still a confirmation on its own. A wrong code is counted by the account's lockout and the address's, exactly as at sign-in, and a TOTP step is accepted once for sudo mode. Each confirmation is audited as auth.elevate, with what it was made with (never the code), and a refusal as auth.elevate failure.

Under the ens-medium profile the window is 10 minutes idle and 30 in all, the password is always asked with the code, and an operator may tighten these values but not loosen them (see ENS.md).

A central vouches for its operator's sudo mode to a node with X-Noust-Elevated, and reads the window through the same elevation_satisfied the server uses for its own routes; a call the node's schema marks as elevated passes through ensure_elevated on the central first, so working on a node through a central keeps the central's window open. A bulk fleet job queued while elevated keeps its elevated steps for 30 minutes whether or not the operator is still working.

Which routes need it is declared on each route and published in the OpenAPI schema as x-noust-requires-elevation, so the console, a script and a central all read the same list. It includes:

  • deleting anything: an application, a database, a database user, a service, a site, a certificate, a backup, a backup schedule or destination, a cron job, a domain, an account;
  • uninstalling a database engine; restoring an application or database backup; revoking a certificate;
  • revealing a secret (an .env in clear, an application's database URL, a database password, a webhook secret, a destination's key) and writing an .env;
  • moving an application to releases; changing its limits, health check, retention, branch, blue/green or previews;
  • editing a unit or a site configuration by hand; creating a service; creating or rewriting a cron job or a backup schedule; building an application as root;
  • editing, inserting or deleting a database row;
  • writing Noust's configuration, whole or by section;
  • server changes: updates, reboot and shutdown, storage clean-up, swap, time, host name, SSH, the firewall, fail2ban, accepted risks;
  • managing accounts, invitations and separation-of-duties exceptions; issuing an API token; disabling 2FA; regenerating backup codes; deciding an approval;
  • deploying or inspecting a local path (see Local paths);
  • POST /api/jobs/delete, which queues the same deletion DELETE /api/apps/{domain} does.

Sudo mode applies to sessions, whichever channel carries the session token: the cookie, or Authorization: Bearer for a client that signed in with bearer: true. The master token and API tokens issued by 3.0 are not asked to elevate. A token issued from 3.1 on is refused on these routes unless it was issued with allow_elevated. The rule follows what the credential is, never the header it arrived in.

Four-eyes approvals

With approval.enabled: true, and always under the ens-medium profile, the changes that are root on the host by another name need a second person: raw units, cron commands, backup hooks, raw site configuration, deploy hooks, moving an application to its own account and building as root (root_equivalent); SQL that writes, row edits and EXPLAIN ANALYZE included; deploying from a directory on the server; adding and removing fleet servers; opening a root shell on the server in the browser (see Terminal and one-off commands); and role grants (a role change, a new account, a recovery invitation). The OpenAPI schema marks those routes with x-noust-requires-approval.

  • The first call does not run. It answers 202 with approval_required and becomes a request that snapshots the exact call (method, path, query and body, by their SHA-256), with a reason.
  • By default only a security account decides. With approval.approvers: [security, admin], an admin may approve infrastructure changes, never a role grant. Never the requester, never another account of the same person, never a token, the master token or a central. Root at the terminal decides too (noust approval approve), on record with its operating-system identity.
  • A request waits approval.request_hours (24) for a decision. An approval lets its requester make that exact call once, within approval.execute_minutes (30), with X-Noust-Approval: <id>.
  • Every step is an audit event naming both people.

Something on this page is wrong or unclear? Report it on GitHub.

Help, contributing and the project’s policies are under Project.

Search the documentation

Type to search every page.

↑ ↓ to move, Enter to open, Esc to close