Skip to content

Security

Deployer can change a server: it writes nginx sites, runs containers, issues certificates and edits Authentik. Its design assumes that whoever controls Deployer controls the server, and protects that control.

Threat model

Threat Protection
Someone on the internet reaches Deployer Listens on loopback only; published by nginx over HTTPS. Login required for every endpoint except health, setup status, sign-in.
Password guessing Argon2id (t=3, m=64 MiB, p=2); rate limits: 10 failures per IP and 5 per email per 15 minutes; constant-time dummy check for unknown emails.
Stolen password TOTP two-factor (per account; mandatory with DEPLOYER_REQUIRE_MFA=1), or sign-in through your identity provider (OIDC).
Stolen session Random 256-bit token, only its SHA-256 stored; HttpOnly, Secure, SameSite=Lax; 24 h sliding expiry; changing the password signs out the other devices; an admin disabling a user or resetting their password ends all their sessions.
Cross-site requests CSRF double-submit token (SameSite=Strict cookie + header) on every non-GET request, plus an Origin check against the public URL.
A user doing too much Roles: viewer (read), editor (build and change apps), admin (delete, users, sign-in, Authentik). Every acting request is audited.
Command injection No shell anywhere. Server actions go through a fixed set of operations; names, domains, paths, compose sub-commands, service names and users are validated against allow-lists; commands run as argument vectors.
A malicious compose file Lint: build: refused, paths escaping the stack folder refused, name: enforced; privileged, Docker socket, host network/PID/IPC, devices, powerful capabilities, public ports and host folders need an explicit "accept risks".
A broken nginx site taking down every site Candidate sites are tested with an isolated nginx -t (temporary copy of the whole configuration) before installation; live nginx -t again before every reload; previous site restored on failure.
Secrets at rest App secrets in secrets/*.env (mode 400, folder 700). In the database, sensitive values (TOTP seeds, OIDC client secret of Deployer, Authentik token, shared app passwords) are sealed with AES-256-GCM using the master key. Viewers never receive secret values.
Credentials in nginx sites Sites are written with mode 600 (they may contain HTTP Basic credentials or the auto-login token).
Forged identity headers nginx always sets the user headers from Authentik when enabled and clears them (Remote-User, X-authentik-*…) otherwise, so a visitor can never send them. Apps trusting headers are told to trust only the app network gateway.
Auto-login abuse /__deployer/autologin is behind Authentik; Deployer's endpoint accepts only loopback requests with the app's 256-bit token (constant-time compare); the password never leaves the server.
Apps exposed by mistake Ports bound to 127.0.0.1 only (Docker bypasses host firewalls for published ports); Authentik SSO is the recommended default.
Supply chain A single static binary; the UI is embedded at build time; the catalog is embedded.

Authentik permissions

Deployer needs an Authentik API token with administrative rights (it creates providers, applications, bindings, scope mappings and reads users and groups). "Connect automatically" creates a dedicated service account deployer; you can also use your own admin token. Deployer only removes Authentik objects it created (their ids are recorded per app).

Things to know

  • Deployer runs as root. It has to write /etc/nginx and run docker. Treat access to Deployer like root access to the server: enable two-factor, keep the admin list short, review the audit log.
  • Shared-account methods (HTTP Basic, auto-login) make everyone you let in the same user inside the app. Prefer OIDC, SAML or headers whenever the app supports them.
  • The master key decrypts every sealed value. Keep it out of backups that leave the server, or encrypt them.
  • Authentik capacity. Authentik keeps database connections open per login; under heavy automated login bursts its PostgreSQL may reach max_connections. Size it accordingly.

Reporting a vulnerability

Please report security issues privately to the maintainer (see the repository README) rather than in a public issue. Include steps to reproduce; you will get an answer within a few days.