# Security Configuration WireBuddy applies its core security controls by default. There is no separate **Settings → Security** tab in the current UI. Account security is managed from **Users**, while deployment controls are configured through environment variables and the reverse proxy. ## Passwords Passwords are hashed with PBKDF2-HMAC-SHA256 using 600,000 iterations and a random per-password salt. The enforced password policy is: - minimum 8 characters - maximum 72 UTF-8 bytes - no control characters or known common passwords - at least three of uppercase, lowercase, number, and special character The bootstrap administrator's random temporary password is printed once to the console at first startup, never logged and never stored. It must be replaced at first login. Administrator password resets also force a change at next login and revoke existing sessions. Password policy parameters are not configurable in the current UI. ## Sessions and Cookies Authentication uses opaque random session tokens. Only SHA-256 token hashes are stored in SQLite. - Idle validity starts at 1 hour. - Cookie-authenticated activity extends the idle expiry. - Absolute session lifetime is 24 hours. - The authentication cookie is `HttpOnly` and `SameSite=Lax`. - The `Secure` flag is enabled when HTTPS is detected or `FORCE_HTTPS_COOKIES` is set. With the built-in HTTPS listener it is always set, because the request scheme is `https` directly. There is currently no session-duration setting or active-session management screen. ## Built-in HTTPS Listener WireBuddy can terminate TLS itself instead of relying on a reverse proxy. Enable **Settings → General → Serve GUI over HTTPS**. The setting is read at startup, so restart WireBuddy afterwards. This is a single, all-or-nothing switch (`gui_https_enabled`, also settable via `PATCH /api/wireguard/settings`). Enabling it does two things: the GUI port serves TLS, and logins and node enrollments reject plain-HTTP transports, so session cookies and node secrets are never sent in clear text. There is no separate policy toggle. The ACME challenge path is never affected. To enforce the same plain-HTTP authentication rejection without the built-in listener — for example when TLS terminates at a reverse proxy — set [`WIREBUDDY_REQUIRE_HTTPS_AUTH`](environment.md#wirebuddy_require_https_auth). Loopback peers stay exempt so local HTTP development keeps working. Which certificate is presented is decided automatically: 1. A **Let's Encrypt** certificate for the configured Server FQDN, if one exists under `data/certs//` and has not expired. Staging certificates are ignored. 2. Otherwise a **self-signed** certificate, generated on demand into `data/certs/_selfsigned/`. Browsers show a trust warning for it. Request a certificate in **Settings → Let's Encrypt** and restart; the ACME certificate then replaces the self-signed one automatically. Nothing needs to be copied by hand, and the two never share files. The Settings page shows which certificate would be served. ### Let's Encrypt while HTTPS is enabled ACME HTTP-01 validation always connects to **port 80 over plain HTTP**. Once the GUI port speaks TLS, WireBuddy therefore also starts a small plaintext listener that answers only `/.well-known/acme-challenge/…` and redirects everything else to HTTPS. Certificate issue and renewal keep working, and no authenticated surface is exposed in clear text. That listener defaults to port 80 and is configured with the `gui_acme_http_port` setting; set it to `0` to disable it. Make sure the port is reachable from the internet, otherwise validation fails. Certificates are read at startup only. **Restart WireBuddy after issuing or renewing a certificate** for the new one to be served. ## HTTPS and Reverse Proxies If you prefer to terminate TLS upstream instead of using the built-in listener, leave **Serve GUI over HTTPS** off and terminate in Caddy, nginx, Traefik, or another trusted reverse proxy. Do not enable both — WireBuddy would then expect a TLS connection on a port the proxy speaks plain HTTP to. For a same-host proxy, the Docker defaults trust loopback. For other proxy addresses, configure: ```bash WIREBUDDY_TRUSTED_PROXIES=192.168.1.10/32 WIREBUDDY_PUBLIC_ORIGIN=https://vpn.example.com ``` `WIREBUDDY_TRUSTED_PROXIES` controls both which peers Uvicorn trusts for `X-Forwarded-*` and application-level client-IP/HTTPS detection. Do not use broad CIDRs unless the complete range is controlled by your proxy infrastructure. HSTS is emitted for requests detected as HTTPS. Set `WIREBUDDY_FORCE_HSTS=1` only when TLS terminates upstream and scheme detection cannot be made reliable. See [Environment Variables](environment.md#reverse-proxy-and-origin-handling) and [Installation](../getting-started/installation.md#reverse-proxy). ## Host Validation Enable Starlette's Trusted Host middleware with an explicit allowlist: ```bash WIREBUDDY_ALLOWED_HOSTS=vpn.example.com,localhost ``` Leave this unset only when Host-header validation is handled by a trusted front-end proxy. With neither `WIREBUDDY_ALLOWED_HOSTS` nor `WIREBUDDY_PUBLIC_ORIGIN` set, WireBuddy logs a warning at startup unless the listener is bound to loopback only, where it logs an informational message instead. ## CSRF Protection Browser mutations use a double-submit CSRF cookie plus Origin/Referer validation. Cookie-authenticated API requests are protected; header-only Bearer requests do not rely on cookies and are exempt from CSRF token matching. For a reverse-proxy deployment, set the canonical origin: ```bash WIREBUDDY_PUBLIC_ORIGIN=https://vpn.example.com ``` Additional legitimate origins can be listed in `WIREBUDDY_CSRF_ALLOWED_ORIGINS`. CSRF protection cannot be disabled through the UI. ## Security Headers WireBuddy sends these headers by default: ```http Content-Security-Policy: default-src 'self'; ... X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin ``` The CSP permits the explicitly used map tiles, jsDelivr-hosted country flags, and local application assets. There is no custom-CSP editor in the UI. ## Rate Limiting and Login Lockouts Route rate limits (an in-memory sliding window per route and client address, IPv6 clients counted per /64, applied before authentication) protect login, sensitive writes, expensive operations, and general API traffic. Exceeded route limits return HTTP `429` with `Retry-After`. Failed logins are additionally tracked across: - source IP - username plus source IP - username across changing source IPs The progressive delays and thresholds can be tuned through the `WIREBUDDY_LOGIN_*` variables documented under [Login Lockout Tuning](environment.md#login-lockout-tuning). There is no rate-limit toggle in the UI. See [Rate Limiting](../security/rate-limiting.md). ## TOTP and Passkeys TOTP and passkeys are managed per account from the administrator-only **Users** page. - TOTP enrollment requires confirmation before activation. - Eight one-time recovery codes are delivered through a short-lived download. - Disabling TOTP requires reauthentication. - Passkeys support usernameless login and multiple credentials per account. - Administrators can initiate onboarding and reset another user's passkeys. There is no global MFA-enforcement or passkey-policy screen. See [User Management](../features/users.md) and [Passkeys](../security/passkeys.md). ## Swagger Swagger is disabled by default. Administrators can enable it under **Settings → General → API Documentation**. - UI: `/swagger` - Schema: `/swagger/openapi.json` - Access: authenticated administrators only `SWAGGER_ENABLED` is not an environment variable in the current application. ## Public Status Page The status page is disabled by default and can be enabled under **Settings → General → Public Status Page**. It is intended for WireGuard clients; authenticated administrators have an override. When a reverse proxy fronts `/status`, list non-loopback proxy CIDRs in `WIREBUDDY_TRUSTED_PROXIES`. See [Status Page](status-page.md). ## Secrets at Rest Sensitive values are wrapped with Fernet authenticated encryption. New values use the vault v2 derivation scheme: 1. PBKDF2-HMAC-SHA256 derives a deployment master key from `WIREBUDDY_SECRET_KEY`. 2. HKDF-SHA256 plus a random per-row salt derives the row key. 3. Fernet encrypts and authenticates the value. The default vault PBKDF2 work factor is 480,000. Do not change `WIREBUDDY_SECRET_KEY`, `WIREBUDDY_PBKDF2_ITERATIONS`, or `WIREBUDDY_DEPLOYMENT_ID` after encrypted data exists unless performing a supported migration. Encrypted data includes WireGuard private keys, preshared keys, and OTP secrets. Session and recovery tokens are stored as hashes. ACME account and certificate private keys are not vault-encrypted; see [Secrets at rest](../security/overview.md#secrets-at-rest). The SQLite file itself is not whole-database encrypted; use encrypted storage when that threat model requires it. ## Container Hardening The supplied Compose service: - drops all capabilities and adds back only the six that are required: `NET_ADMIN` for WireGuard and iptables, and `NET_BIND_SERVICE`, `SETUID`, `SETGID`, `CHOWN`, `DAC_OVERRIDE` for the bundled Unbound resolver - enables `no-new-privileges` - mounts `/dev/net/tun` - uses host networking as required by WireGuard - limits JSON log-file growth The container runs as root because WireGuard, iptables, Unbound, and network namespace operations require privileges. Running the current image as an arbitrary non-root UID or with a read-only root filesystem is not a supported drop-in configuration. ## Operational Logging WireBuddy emits structured security-relevant log events for authentication, password changes, OTP/passkey changes, and administrative mutations. There is no built-in audit-log browser or CSV/syslog export page. Forward container logs to your logging system when centralized retention or alerting is required. ## Not Currently Implemented The current release does not provide: - a Security settings tab - configurable password history or expiry - a built-in application IP whitelist - active-session browsing or individual remote-session revocation - global MFA enforcement - custom CSP editing - a dedicated API-token subsystem - built-in CrowdSec/fail2ban integration Implement these controls at the reverse proxy, firewall, identity, or logging layer where needed. ## Related - [Security Overview](../security/overview.md) - [Security Best Practices](../security/best-practices.md) - [Environment Variables](environment.md) - [API Authentication](../api/authentication.md)