WIP: feat: Phase A, the API service, membership and WireGuard key exchange #1

Draft
marcos-mendez wants to merge 3 commits from feat/phase-a into master
marcos-mendez commented 2026-10-02 11:02:23 -03:00 (Migrated from github.com)

Phase A of handbook decision 0046 (proposed, handbook#39): the API service, and membership and WireGuard key exchange for the nodes of a set. Nothing about DNS (Phase B) or the registry view (Phase C).

The recommended answers of 0046 are the working defaults, listed in the README as "defaults pending the maintainer's decision", each in one place: Python from Debian 13 only, an account key plus a narrower enrollment key, operator confirmation by default, one reusable entry secret per set, outbound long polling, IPv6 first and TLS only.

What is here

  • keel-cloud-api: aiohttp, TLS only on [::]:8443, as the system user keel-cloud, SQLite storing only key hashes, public node records and the proofs it relays. Admin CLI keel-cloud.
  • keel-overlay-cloud: keel-cloud-node, also keel cloud (needs Keel-Linux/keel#68 for the spec's cloud section). It admits a peer only when its record proof and its confirmation (or the set's automatic admission proof) verify with the set's entry secret, pins it per set, and writes it into network.overlay.wireguard.peers; keel spec apply --system converges it.
  • python3-keel-cloud: the shared package.

API

GET /v1/health; POST /v1/keys (account); GET /v1/sets, PATCH /v1/sets/{set} (account; automatic admission needs its proof); POST /v1/sets/{set}/peers (register, with the record proof); GET /v1/sets/{set}/peers?since=&wait= (list, long poll); POST /v1/sets/{set}/peers/confirm (account key plus a confirmation made on a node).

Trust model

The service relays three HMAC proofs made with the set's entry secret (record, confirmation, automatic admission) and can make none of them, so a compromised service can only propose; nodes check all three. Admission needs both the entry secret (nodes) and the account key (operator). Pinned peers keep their key and addresses, addresses must lie inside the overlay prefix, and keel cloud forget is permanent. An independent security review was run on the first version; its findings are fixed in the third commit, and the README lists the known limits of Phase A.

Tests

208 tests, 99.8 percent of lines and branches. CI only through lxc-trixie.yml@main: "coverage / trixie" and "build / trixie" (build, lintian with warnings fatal, and an install, run and purge of the service).

Real test on the test VM, 2026-10-02, with the final packages: kc-api, kc-a and kc-b from the Keel Core step 8 image. Both nodes enrolled and were held; the enrollment key could not confirm; a simulated compromised service marking them confirmed and turning automatic admission on admitted nothing; the operator confirmed each from the other; each spec gained the other as a peer; keel spec apply --system plus keel network confirm brought wg0 up and each pinged the other; a moved endpoint and a bogus confirmed peer injected into the database were refused with the spec unchanged; the hardened agent service picked up a peer's new endpoint through the long poll. Containers destroyed.

Test plan

  • "coverage / trixie" and "build / trixie" green
  • The maintainer answers the open questions of 0046; a change of default is one place, listed in the README
  • keel#68 merged before keel-overlay-cloud is published (it depends on keel >= 0.16.0)

Branch protection on master, copied from keel-core: required checks "coverage / trixie" and "build / trixie", strict, pull request required with 0 approvals and stale reviews dismissed, enforced for admins, no force push, no deletion.

Phase A of handbook decision 0046 (proposed, handbook#39): the API service, and membership and WireGuard key exchange for the nodes of a set. Nothing about DNS (Phase B) or the registry view (Phase C). The recommended answers of 0046 are the working defaults, listed in the README as "defaults pending the maintainer's decision", each in one place: Python from Debian 13 only, an account key plus a narrower enrollment key, operator confirmation by default, one reusable entry secret per set, outbound long polling, IPv6 first and TLS only. ## What is here - `keel-cloud-api`: aiohttp, TLS only on `[::]:8443`, as the system user `keel-cloud`, SQLite storing only key hashes, public node records and the proofs it relays. Admin CLI `keel-cloud`. - `keel-overlay-cloud`: `keel-cloud-node`, also `keel cloud` (needs Keel-Linux/keel#68 for the spec's `cloud` section). It admits a peer only when its record proof and its confirmation (or the set's automatic admission proof) verify with the set's entry secret, pins it per set, and writes it into `network.overlay.wireguard.peers`; `keel spec apply --system` converges it. - `python3-keel-cloud`: the shared package. ## API `GET /v1/health`; `POST /v1/keys` (account); `GET /v1/sets`, `PATCH /v1/sets/{set}` (account; automatic admission needs its proof); `POST /v1/sets/{set}/peers` (register, with the record proof); `GET /v1/sets/{set}/peers?since=&wait=` (list, long poll); `POST /v1/sets/{set}/peers/confirm` (account key plus a confirmation made on a node). ## Trust model The service relays three HMAC proofs made with the set's entry secret (record, confirmation, automatic admission) and can make none of them, so a compromised service can only propose; nodes check all three. Admission needs both the entry secret (nodes) and the account key (operator). Pinned peers keep their key and addresses, addresses must lie inside the overlay prefix, and `keel cloud forget` is permanent. An independent security review was run on the first version; its findings are fixed in the third commit, and the README lists the known limits of Phase A. ## Tests 208 tests, 99.8 percent of lines and branches. CI only through `lxc-trixie.yml@main`: "coverage / trixie" and "build / trixie" (build, lintian with warnings fatal, and an install, run and purge of the service). Real test on the test VM, 2026-10-02, with the final packages: kc-api, kc-a and kc-b from the Keel Core step 8 image. Both nodes enrolled and were held; the enrollment key could not confirm; a simulated compromised service marking them confirmed and turning automatic admission on admitted nothing; the operator confirmed each from the other; each spec gained the other as a peer; `keel spec apply --system` plus `keel network confirm` brought wg0 up and each pinged the other; a moved endpoint and a bogus confirmed peer injected into the database were refused with the spec unchanged; the hardened agent service picked up a peer's new endpoint through the long poll. Containers destroyed. ## Test plan - [ ] "coverage / trixie" and "build / trixie" green - [ ] The maintainer answers the open questions of 0046; a change of default is one place, listed in the README - [ ] keel#68 merged before keel-overlay-cloud is published (it depends on keel >= 0.16.0) Branch protection on master, copied from keel-core: required checks "coverage / trixie" and "build / trixie", strict, pull request required with 0 approvals and stale reviews dismissed, enforced for admins, no force push, no deletion.
marcos-mendez commented 2026-10-02 11:56:05 -03:00 (Migrated from github.com)

Paused by the maintainer (2026-10-02) until the etcd mesh (Phase 5 of tracker#46: registry, install by discovery, three nodes without Keel Cloud) is stable. Then the node-side client ships in images, small and disabled, and is enabled later through apt update and apt upgrade. Needs an independent security review before merge.

Paused by the maintainer (2026-10-02) until the etcd mesh (Phase 5 of tracker#46: registry, install by discovery, three nodes without Keel Cloud) is stable. Then the node-side client ships in images, small and disabled, and is enabled later through apt update and apt upgrade. Needs an independent security review before merge.
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/phase-a:feat/phase-a
git switch feat/phase-a
Sign in to join this conversation.
No description provided.