feat(secrets/bitwarden): EU Cloud + self-hosted server URL support (#31378)

Closes #31370.

bws defaults to the US identity endpoint, so EU Cloud and self-hosted
machine-account tokens fail with [400 Bad Request] {"error":"invalid_client"}
during 'hermes secrets bitwarden setup'. The token is valid — it's just
being checked against the wrong region.

Add a Bitwarden region step to the wizard between the access-token and
project-list steps:

  Step 1  Install bws
  Step 2  Provide access token
  Step 3  Pick region   <-- new (US / EU / self-hosted-custom-URL)
  Step 4  Pick project  (now talks to the right endpoint)
  Step 5  Test fetch

Region is stored in config.yaml as secrets.bitwarden.server_url and
plumbed into every bws subprocess as BWS_SERVER_URL (project list,
secret list, test fetch, and the env_loader startup pull).

Also:
- Non-interactive: 'hermes secrets bitwarden setup --server-url ...'
- Pre-existing BWS_SERVER_URL in the shell is detected and reused
- Cache key includes server_url so EU/US fetches don't collide
- 'hermes secrets bitwarden status' shows the configured region
- 'invalid_client' / '400 Bad Request' from bws now triggers a hint
  pointing at the region setting instead of looking like a bad token
This commit is contained in:
Teknium 2026-05-24 02:19:57 -07:00 committed by GitHub
parent c9b3eeabdc
commit bc3f1f4f34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 271 additions and 14 deletions

View file

@ -21,7 +21,7 @@ You set up the machine account *in the web app*, where your normal 2FA applies.
### 1. Create a machine account and access token
In the [Bitwarden web app](https://vault.bitwarden.com):
In the [Bitwarden web app](https://vault.bitwarden.com) (or [vault.bitwarden.eu](https://vault.bitwarden.eu) for EU accounts):
1. Switch to **Secrets Manager** from the product switcher.
2. Create or pick a **Project** (e.g. "Hermes keys").
@ -41,9 +41,19 @@ It will:
1. Download and verify `bws v2.0.0` into `~/.hermes/bin/bws`.
2. Prompt you for the access token (input is hidden). Stored in `~/.hermes/.env` as `BWS_ACCESS_TOKEN`.
3. List the projects the machine account can see; pick one. Stored in `config.yaml` as `secrets.bitwarden.project_id`.
4. Test-fetch the project's secrets and show you which env vars will resolve.
5. Flip `secrets.bitwarden.enabled: true`.
3. Ask which Bitwarden region your machine account belongs to — **US Cloud**, **EU Cloud**, or **self-hosted / custom URL**. Stored in `config.yaml` as `secrets.bitwarden.server_url` and passed to `bws` as `BWS_SERVER_URL`.
4. List the projects the machine account can see; pick one. Stored in `config.yaml` as `secrets.bitwarden.project_id`.
5. Test-fetch the project's secrets and show you which env vars will resolve.
6. Flip `secrets.bitwarden.enabled: true`.
Non-interactive setup is also supported via flags:
```bash
hermes secrets bitwarden setup \
--access-token "$BWS_ACCESS_TOKEN" \
--server-url https://vault.bitwarden.eu \
--project-id <project-uuid>
```
### 3. Confirm
@ -74,6 +84,7 @@ secrets:
enabled: false
access_token_env: BWS_ACCESS_TOKEN
project_id: ""
server_url: ""
cache_ttl_seconds: 300
override_existing: true
auto_install: true
@ -84,6 +95,7 @@ secrets:
| `enabled` | `false` | Master switch. When false, Bitwarden is never contacted. |
| `access_token_env` | `BWS_ACCESS_TOKEN` | Env var name that holds the bootstrap token. Change this if you already use `BWS_ACCESS_TOKEN` for something else. |
| `project_id` | `""` | UUID of the project to sync from. |
| `server_url` | `""` | Bitwarden region or self-hosted endpoint. Empty = `bws` default (US Cloud, `https://vault.bitwarden.com`). Set to `https://vault.bitwarden.eu` for EU Cloud, or your own URL for self-hosted. Plumbed into the `bws` subprocess as `BWS_SERVER_URL`. |
| `cache_ttl_seconds` | `300` | How long an in-process fetch result is reused. Set to `0` to disable caching. Cache is per-process; new `hermes` invocations start fresh. |
| `override_existing` | `true` | When true, Bitwarden values overwrite anything already in env (so rotation in the web app actually takes effect). Flip to `false` if you want `.env` / shell exports to win locally. |
| `auto_install` | `true` | When true, `bws` is auto-downloaded into `~/.hermes/bin/` on first use. |
@ -96,7 +108,8 @@ Bitwarden never blocks Hermes startup. If anything goes wrong, you'll see a one-
|---|---|---|
| `BWS_ACCESS_TOKEN is not set` | Enabled in config but token cleared from `.env` | Re-run `hermes secrets bitwarden setup` |
| `bws exited 1: invalid access token` | Token revoked or wrong | Generate a new token, re-run setup |
| `bws timed out` | Network blocked or Bitwarden API slow | Check connectivity to `api.bitwarden.com` |
| `[400 Bad Request] {"error":"invalid_client"}` | Token is for a Bitwarden region other than the one `bws` is calling (e.g. EU token hitting the US identity endpoint) | Re-run setup and pick the right region, or set `secrets.bitwarden.server_url` to `https://vault.bitwarden.eu` (or your self-hosted URL) |
| `bws timed out` | Network blocked or Bitwarden API slow | Check connectivity to `api.bitwarden.com` (or your `server_url`) |
| `bws binary not available` | `auto_install: false` and `bws` not on PATH | Install manually from [github.com/bitwarden/sdk-sm/releases](https://github.com/bitwarden/sdk-sm/releases) or flip `auto_install` back on |
| `Checksum mismatch` | Download corrupted or tampered | Re-run, will retry; if it persists, file an issue |