Document TLS between the store's machines, and close section A of the backlog

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilker ManapandClaude Opus 5.5 committed 2026-10-04 12:12:35 +02:00
1 parent 43d3c12302
commit eb10aee93b
3 files changed
+29 -14

No files matched your search

+7 -14
View File
@@ -19,20 +19,13 @@ Conventions this codebase keeps, which every item here must keep too:
## A. Security — do first
**Status (2026-10-04, branch `security-a`):** done — A1, A2, A3, A4, A5
(token on loopback rather than a unix socket), A6 except the item below, A7,
A8, A9. **Left:**
- **A6b** — etcd client/peer TLS with `--client-cert-auth`, and Patroni's
REST API over TLS, for new clusters and as a rolling change for running
ones. Until then the firewall (A9) keeps 2379/2380/8008 to the cluster's
networks and Patroni's API asks for a password.
- From A8: thread ids are bounded and documented as project-wide, but not
scoped per user; Python engines are resolved with an explicit PyTorch
index and a PEP 751 lock, but locks are not shipped per engine version.
- A9 is live on the PoC (140, 149, 150, 148): llmgw's ports answer
10.0.0.0/24, are dropped from outside it (tested over IPv6), and ssh,
:8080 and other services are untouched. The other nodes get it on their
next `node add`.
**Status (2026-10-04): done and merged.** A1–A9, A6b included — etcd and
Patroni's API on TLS, the PoC moved in maintenance mode without a failover.
Deliberately narrower than written: the admin API is a token on loopback
rather than a unix socket (A5); thread ids are bounded and documented as
project-wide rather than scoped per user (A8); Python engines resolve with an
explicit PyTorch index and a PEP 751 lock rather than locks shipped per engine
version (A8). All of A is live on the PoC.
### A1. Toolchain (S, hours)
`govulncheck` finds 11 standard-library vulnerabilities reachable from this
+13
View File
@@ -211,6 +211,19 @@ APIs change between minors without a deprecation period.
already has are left as they were. Extra networks go in
`firewall_networks`; `--no-firewall` skips it; `llmgw doctor` says when a
node lacks it.
- **etcd and Patroni's API speak TLS.** etcd's peers and clients use
certificates from the cluster's authority, and a client must present one;
Patroni reaches it with its member's certificate and serves its API over
TLS. `server init --patroni`, run again, moves an existing cluster: with
Patroni in maintenance mode, so PostgreSQL serves on and nothing fails
over, etcd is rebuilt on TLS and Patroni's keys carried into it. The
control plane asks the API over HTTPS, verified, and over HTTP only where a
member has not moved yet.
- **A cluster keeping its access rules per member got no `hostssl`.** The
TLS step looked only at the shared configuration, and on a cluster set up
before the rules moved there said the rules were `hostssl` while
plaintext was still accepted. Each member's own rules are now made
`hostssl` too.
### Fixed
+9
View File
@@ -152,6 +152,15 @@ promoted within about thirty seconds of the primary being lost, and the
control plane follows it by itself. `llmgw ctl status` says who leads; see
[deploy/ha](../deploy/ha/README.md) for what it sets up.
Everything between the machines is TLS, with certificates from the cluster's
own authority: PostgreSQL (every remote rule is `hostssl`, and the control
plane verifies the member it reaches), etcd (peers and clients, and a client
must present a certificate), and Patroni's API, which also asks for a
password before a change. A cluster set up by an earlier llmgw is moved to
this by running the same command again, on the control plane that holds the
authority: etcd is rebuilt on TLS with Patroni in maintenance mode, so
PostgreSQL serves throughout and no failover happens.
Every step looks before it acts. Running it again finishes an install that
stopped halfway and changes nothing on one that did not; it never overwrites
`server.yaml`, the authority, or an existing account's password.