From 219bfeac42cc501ca0588a0d64c339a123baa819 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 5 Nov 2026 10:03:22 -0500 Subject: [PATCH] docs(6868): the proxy count is set in Settings, with the env var as the starting value Configuration, the README env table and the features page say Settings -> Config -> Reverse proxy is where the count is checked and set, that a saved count wins over TRUSTED_PROXY_HOPS, that Cloudflare in front of your own proxy is 2, and what a too-low count does. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- docs/configuration.md | 12 ++++++++---- docs/features.md | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b90b4979..33de81b8 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Everything is optional. Set these in the environment or in a `.env` file beside | Variable | When | What it does | |----------|------|--------------| | `BASE_URL` | Behind a proxy | The public URL, used in email links and SSO redirects | -| `TRUSTED_PROXY_HOPS` | Behind a proxy | How many reverse proxies stand in front of Grimoire (default `1`; the compose file above sets `0` because it publishes the port directly). The client address and HTTPS are read from the proxy's headers by it | +| `TRUSTED_PROXY_HOPS` | Rarely | The starting count of reverse proxies in front of Grimoire (default `1`; the compose file above sets `0` because it publishes the port directly). Check and change it in **Settings → Config → Reverse proxy**, which shows what each count reads for your own request; behind Cloudflare it is usually `2` | | `POSTGRES_PASSWORD` | Before the first start | The database password (defaults to `grimoire`; the database is reachable only inside the compose network). Postgres takes it once, when the volume is created | | `SECRET_KEY` | Rarely | A key you manage yourself, instead of the one Grimoire keeps. It wins over the kept key; remove it to go back | diff --git a/docs/configuration.md b/docs/configuration.md index ab527a60..caf5acfb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -14,7 +14,7 @@ Any variable marked *secret* can also be read from a file: set `_FILE` to | `SECRET_KEY` *(secret)* | kept in `DATA_DIR` | Signs sessions and seals stored credentials. Unset, the instance makes its own on first start and keeps it in `DATA_DIR/secret_key`. Set it only to manage the key yourself; it wins over the kept one (see below) | | `DATA_DIR` | `/data` | Where the instance keeps its own `SECRET_KEY`. Mount a volume here | | `BASE_URL` | `http://localhost:5000` | Public URL, used in email links and OIDC redirects | -| `TRUSTED_PROXY_HOPS` | `1` | How many reverse proxies stand in front of Grimoire. The client address and the scheme are read that many entries in from the right of `X-Forwarded-For` and `X-Forwarded-Proto`; `0` ignores both headers. See [Behind a reverse proxy](#behind-a-reverse-proxy) | +| `TRUSTED_PROXY_HOPS` | `1` | The starting count of reverse proxies in front of Grimoire. **Settings → Config → Reverse proxy** is where the count is checked and set, and a count saved there wins over this. The client address and the scheme are read that many entries in from the right of `X-Forwarded-For` and `X-Forwarded-Proto`; `0` ignores both headers. See [Behind a reverse proxy](#behind-a-reverse-proxy) | | `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING` or `ERROR` | | `LOG_RETENTION_DAYS` | `90` | Days of app logs to keep | | `FASTEMBED_CACHE_DIR` | `/data/fastembed-cache` | Where the embedding model is stored after its first download | @@ -95,7 +95,11 @@ Back up the database with **Settings → Data → Scheduled backups** ([Backups] ## Behind a reverse proxy -Grimoire doesn't terminate TLS. Put it behind Nginx, Traefik or Caddy, forward to port 5000, and keep port 5000 off the internet. Set `BASE_URL`, and `TRUSTED_PROXY_HOPS` to the number of proxies a request passes through (one proxy is `1`, the default; Cloudflare in front of Traefik is `2`). +Grimoire doesn't terminate TLS. Put it behind Nginx, Traefik or Caddy, forward to port 5000, and keep port 5000 off the internet. Set `BASE_URL`, then set the proxy count to the number of proxies a request passes through: one proxy is `1`, the default; Cloudflare's proxied DNS in front of your own proxy is `2`. + +**Settings → Config → Reverse proxy** sets the count and checks it against your own request. It shows the address Grimoire reads you as, and what each count from 0 to 3 would read. Pick the count that shows your own address. When a request comes through Cloudflare, the section names the count that reads the address Cloudflare reports, with a button that sets it. `TRUSTED_PROXY_HOPS` in the environment is only the starting value: a count saved in Settings wins, and **Use the environment's** goes back to it. + +A count that is too low makes every visitor read as the proxy, so they share one sign-in rate limit and a few bad sign-ins block everyone. The "too many attempts" message says so and points here. The block on sign-ins lifts after a minute; an admin who is already signed in can fix the count at once. ```nginx location / { @@ -110,7 +114,7 @@ location / { } ``` -Each proxy appends its entry to `X-Forwarded-For` and `X-Forwarded-Proto`, so Grimoire reads the entry `TRUSTED_PROXY_HOPS` in from the right: the one your own proxy wrote. Whatever a client puts to the left of it is ignored. A header with fewer entries than that (a LAN road with one proxy beside a Cloudflare road with two) is read at its leftmost entry, the one the first proxy wrote. +Each proxy appends its entry to `X-Forwarded-For` and `X-Forwarded-Proto`, so Grimoire reads the entry that many in from the right: the one your own proxy wrote. Whatever a client puts to the left of it is ignored. A header with fewer entries than that (a LAN road with one proxy beside a Cloudflare road with two) is read at its leftmost entry, the one the first proxy wrote. When that entry says `https`, the session cookie is marked `Secure` and the response carries `Strict-Transport-Security: max-age=31536000`. Over plain HTTP neither is sent, and Grimoire never redirects to HTTPS, so a LAN install keeps working. @@ -119,7 +123,7 @@ A hop count higher than the real number of proxies lets a client choose its own ## Security checklist - **Keep the key.** Forge tokens and the backup destination's S3 secret are sealed under a key derived from `SECRET_KEY`, so a copy of the database alone can't read them. Unset, the instance makes a random key and keeps it in `/data/secret_key`, readable by its owner only; keep that volume. If the key changes (say, a restore onto an instance with a different key), Settings marks those credentials and asks for them again; nothing else breaks. The app warns at startup when `SECRET_KEY` is a public placeholder or `/data` is not a mounted volume, and refuses to start with a placeholder key (`dev-secret-change-me`, `change-me-in-production`). -- **`TRUSTED_PROXY_HOPS`** matches the number of proxies in front of Grimoire, so rate limits and the audit log see the real client and the cookie is `Secure` over HTTPS. +- **The proxy count** (Settings → Config → Reverse proxy) matches the number of proxies in front of Grimoire, so rate limits and the audit log see the real client and the cookie is `Secure` over HTTPS. The section shows whether it does. - **Registration** is open only for the first account. Invite people, or open it briefly from Settings. - **Signing everyone else out**: changing a password ends that user's other sessions; Settings → Account has a button for it too. - **API keys** are per user, shown once, and can be read-only. Revoke any you no longer use under Settings → MCP Access. diff --git a/docs/features.md b/docs/features.md index a48b016e..6ee6d9cd 100644 --- a/docs/features.md +++ b/docs/features.md @@ -103,7 +103,7 @@ Email and password, plus optional OIDC single sign-on (Authentik, Keycloak, ...) | Integrations | SearXNG, git forge connections | | Data | Export, backup and restore, scheduled backups (admin) | | MCP Access | API keys and the command to connect Claude | -| Config (admin) | What's running, application URL, plugin marketplace, database maintenance, SMTP, forge webhook | +| Config (admin) | What's running, reverse proxy count (checked against your own request), application URL, plugin marketplace, database maintenance, SMTP, forge webhook | | Shared areas (admin) | The canonical areas systems map to | | Platforms (admin) | The platform catalogue family canon is grouped by | | Users (admin) | Registration, invites, users |