diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..a057d863 --- /dev/null +++ b/.env.example @@ -0,0 +1,37 @@ +# wg-easy — example environment file +# +# Copy to `.env` and adjust. All variables are OPTIONAL; wg-easy runs with +# sensible defaults and an interactive setup wizard if none are set. +# See docs: advanced/config/optional-config and advanced/config/unattended-setup. + +# ── General ──────────────────────────────────────────────────────────────── +# PORT=51821 +# HOST=0.0.0.0 +# INSECURE=false +# DISABLE_IPV6=false + +# ── Unattended first-run setup (only used on first start) ─────────────────── +# INIT_ENABLED=true +# INIT_USERNAME=admin +# INIT_PASSWORD=change-me-to-something-long +# INIT_HOST=vpn.example.com +# INIT_PORT=51820 +# INIT_DNS=1.1.1.1,8.8.8.8 + +# ── Trusted-Header SSO (reverse-proxy authentication) ─────────────────────── +# Have wg-easy trust an identity your authenticating reverse proxy (Authentik, +# Authelia, oauth2-proxy, …) has already verified, instead of showing its own +# login form. Disabled unless TRUSTED_PROXY_ENABLED=true. +# +# SECURITY: TRUSTED_PROXY_SECRET is the trust boundary. A client can forge any +# header, so the proxy must inject this secret and it must be unreachable by end +# users. The feature fails closed if enabled without a secret. +# +# TRUSTED_PROXY_ENABLED=true +# TRUSTED_PROXY_SECRET= # generate: openssl rand -hex 32 +# TRUSTED_PROXY_SECRET_HEADER=x-wg-proxy-secret +# TRUSTED_PROXY_HEADER=x-authentik-username +# TRUSTED_PROXY_EMAIL_HEADER=x-authentik-email +# TRUSTED_PROXY_NAME_HEADER=x-authentik-name +# TRUSTED_PROXY_DEFAULT_ROLE=client # or: admin +# TRUSTED_PROXY_IPS= # optional CSV source-IP allowlist (defense-in-depth) diff --git a/docker-compose.yml b/docker-compose.yml index 1dc53c2a..27bb8fd8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,17 @@ services: # - PORT=51821 # - HOST=0.0.0.0 # - INSECURE=false + # + # Trusted-Header SSO (behind an authenticating reverse proxy). + # Disabled unless TRUSTED_PROXY_ENABLED=true. See docs: advanced/config/optional-config. + # - TRUSTED_PROXY_ENABLED=true + # - TRUSTED_PROXY_SECRET=${TRUSTED_PROXY_SECRET} # required; the proxy injects this + # - TRUSTED_PROXY_SECRET_HEADER=x-wg-proxy-secret + # - TRUSTED_PROXY_HEADER=x-authentik-username + # - TRUSTED_PROXY_EMAIL_HEADER=x-authentik-email + # - TRUSTED_PROXY_NAME_HEADER=x-authentik-name + # - TRUSTED_PROXY_DEFAULT_ROLE=client # or admin + # - TRUSTED_PROXY_IPS= # optional source-IP allowlist image: ghcr.io/wg-easy/wg-easy:15 container_name: wg-easy diff --git a/docs/content/advanced/config/optional-config.md b/docs/content/advanced/config/optional-config.md index c58f5636..bf9618ac 100644 --- a/docs/content/advanced/config/optional-config.md +++ b/docs/content/advanced/config/optional-config.md @@ -20,3 +20,28 @@ You will however still see a IPv6 address in the Web UI, but it won't be used. This option can be removed in the future, as more devices support IPv6. /// + +## Trusted-Header SSO (Reverse-Proxy Authentication) + +If wg-easy runs behind an authenticating reverse proxy (e.g. Authentik, Authelia, or oauth2-proxy in forward-auth mode), you can have wg-easy trust the identity the proxy has already verified instead of showing its own login form. When enabled, the proxy asserts the username via a header and wg-easy establishes a session for that user, provisioning the account on first sight. + +This is **disabled by default**. Stock behaviour is unchanged unless you set `TRUSTED_PROXY_ENABLED=true`. + +| Env | Default | Example | Description | +| ---------------------------- | ------------------- | ---------------------- | ----------------------------------------------------------------------------------------------- | +| `TRUSTED_PROXY_ENABLED` | `false` | `true` | Enables trusted-header SSO. | +| `TRUSTED_PROXY_SECRET` | _(none)_ | `a1b2c3…` (32+ bytes) | Shared secret the proxy injects. **Required** when enabled; feature fails closed if unset. | +| `TRUSTED_PROXY_SECRET_HEADER`| `x-wg-proxy-secret` | `x-proxy-secret` | Header carrying the shared secret. | +| `TRUSTED_PROXY_HEADER` | `x-authentik-username` | `x-forwarded-user` | Header carrying the verified username. | +| `TRUSTED_PROXY_EMAIL_HEADER` | `x-authentik-email` | `x-forwarded-email` | Optional header carrying the user's email. | +| `TRUSTED_PROXY_NAME_HEADER` | `x-authentik-name` | `x-forwarded-name` | Optional header carrying the user's display name. | +| `TRUSTED_PROXY_IPS` | _(empty)_ | `10.0.0.2,10.0.0.3` | Optional allowlist of proxy source IPs (defense-in-depth). Left empty when a NAT hides the peer. | +| `TRUSTED_PROXY_DEFAULT_ROLE` | `client` | `admin` | Role for auto-provisioned users (`client` or `admin`). The first-ever user is always `admin`. | + +/// warning | Security — the secret is the trust boundary + +A raw client can forge any header. **The `TRUSTED_PROXY_SECRET` is what proves a request actually came from your proxy** — the proxy must inject it and it must never be reachable by end users. Do not enable this feature without a strong secret, and make sure the header can only be set by the proxy (not passed through from the client). The optional `TRUSTED_PROXY_IPS` allowlist is a second layer, but note that container NAT often rewrites the source IP to a gateway address, making it non-discriminating on its own — the secret is mandatory, the IP list is not. + +Provisioned SSO users have no local password and cannot log in via the password form. Set `TRUSTED_PROXY_DEFAULT_ROLE` deliberately. + +///