docs(apiserver): document security defaults changed by bug-1945..1952

deployment.md: auth.yaml now holds bcrypt hashes of the (still public)
dev credentials with startup warnings; JWT secret from XCE_JWT_SECRET or a
random per-process secret; logout revocation is per process;
security.trusted_proxies for reverse proxies.
security.md steering: JWT parsing, revocation, secret resolution,
trusted proxies, middleware abort rule, bcrypt and role-change rule.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-10 08:43:31 +00:00
parent a80768f31d
commit 27ef506058
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
2 changed files with 13 additions and 4 deletions

View file

@ -285,7 +285,10 @@ xceadmin config push cluster.bin --controller localhost:9991
Be aware of these before treating anything above as a production runbook — they are honest gaps in this repo today, not documented-but-hidden features:
1. **No process supervision is shipped.** No systemd unit, no Dockerfile, no docker-compose, no supervisord config exists anywhere in this repo for any of the four daemons. A systemd-unit *sketch* exists in `src/docs/config-replication-design.md` but has a wrong binary name (`xcecontroller` vs the real `xce_controller`) and specifies `Type=notify`, which the controller does not support (it never calls `sd_notify`) — using it as written will hang at startup and time out. If you adapt it, switch to `Type=simple` and fix the binary name.
2. **`src/frontend/apiserver/config/auth.yaml` ships plaintext dev credentials checked into git**: four accounts total — `admin`/`admin` and `op7x2k9`/`CHANGE_ME_dev_only` (both role `cluster_admin`), plus `user1`/`user1` and `user2`/`user2` (role `basic_user`). Treat all of them as exposed by default — don't ship the checked-in file as-is; copy `auth.yaml.example` and set real credentials/an external auth provider instead.
2. **`src/frontend/apiserver/config/auth.yaml` ships known dev credentials checked into git** (stored as bcrypt hashes since bug-1947, but the passwords are public): four accounts total — `admin`/`admin` and `op7x2k9`/`CHANGE_ME_dev_only` (both role `cluster_admin`), plus `user1`/`user1` and `user2`/`user2` (role `basic_user`). The apiserver logs a warning at startup for each default credential still active and for each plaintext `password:` entry. Don't ship the checked-in file as-is; set real credentials (`go run ./tools/encrypt -action=hash` in `src/frontend/apiserver` prints a bcrypt hash) or an external auth provider instead.
- **JWT secret**: set `XCE_JWT_SECRET` (or `authentication.token.secretKey` in `app.yaml`, which ships empty). Without one, the apiserver signs tokens with a random per-process secret and logs a warning: tokens do not survive a restart, and several apiservers behind one endpoint (the nginx sample's upstream) must share the same `XCE_JWT_SECRET`. The old public default `xce-api-key` is treated as unset (bug-1945).
- **Logout** revokes the token in memory until its expiry (bug-1946); the revocation is per apiserver process, not persisted and not shared between apiservers.
- **Reverse proxy**: `X-Forwarded-For`/`X-Real-IP` are honoured only from addresses in `app.yaml` `security.trusted_proxies` (default none, bug-1948). Behind nginx/HAProxy, list the proxy's address there, or every client is seen with the proxy's IP (IP whitelist, JWT IP binding).
3. **All controller-bound traffic is encrypted but not authenticated.** Controller↔controller HA replication and every `libxce` client (worker→controller, scheduler→controller, apiserver→controller, xceadmin→controller) run over TLS, but `SSL_VERIFY_NONE` is used throughout — no client certificates, no hostname/identity verification. This buys confidentiality and tamper-resistance in transit, not authentication of the peer; anyone who can reach the port and complete a TLS handshake is treated as trusted the moment the transport-level join succeeds.
4. **apiserver's own TLS uses a checked-in self-signed cert with no CA trust story**; `xcecli` hardcodes `InsecureSkipVerify: true` against it. A reverse-proxy-terminated-TLS model is sketched (nginx sample config) but not automated. (This is apiserver's REST-facing HTTPS, separate from its TLS-encrypted `libxce` connection to the controller in item 3.)
5. **`xceadmin config push` against a real cluster** — the decode-crash bug is fixed (2026-07-30, `decode_reply()` unwraps the `Request` envelope), but `--dry-run` still has no real controller-side handler (see Known Limitations item 2).