Repository navigation
feat: Phase A, the API service, membership and WireGuard key exchange - #1
Draft
marcos-mendez wants to merge 3 commits into
Draft
marcos-mendez wants to merge 3 commits into
marcos-mendez wants to merge 3 commits into
Conversation
added 3 commits
October 2, 2026 14:01
The service (keel_cloud.api): an aiohttp API, TLS only, over SQLite that stores accounts, the SHA-256 of each API key, sets, and per node its public record and its entry secret proof. Two key scopes, an account key and a narrower enrollment key. Nodes register with a proof the service relays but cannot make; new nodes stay pending until the operator confirms them with the account key; a set's revision drives a long poll. A registered key keeps its overlay addresses and two nodes of a set never share one. Failed keys are limited per client. The node agent (keel_cloud.node, keel-cloud-node, also `keel cloud`): reads the spec's cloud section and its 0600 secret files, registers this node, checks every record of its set against the entry secret, admits only confirmed nodes whose proof verifies, pins them per set, and writes them into network.overlay.wireguard.peers after keel spec validate accepts the new spec. It never writes WireGuard's configuration; keel spec apply converges the spec under 0018's window. 166 tests, 99.7 percent of lines and branches, the agent against the real service over TLS on [::1].
Three binary packages from one source (0039): python3-keel-cloud,
keel-cloud-api (systemd unit as the keel-cloud system user, [::]:8443,
a self-signed development certificate made at installation) and
keel-overlay-cloud (the agent, its unit installed disabled). Lintian
clean with warnings fatal.
CI runs only through Keel-Linux/.github's lxc-trixie.yml: the suite
with its 95 percent gate ("coverage / trixie"), and the build, lintian
and an install of the service checked as it runs ("build / trixie").
The README lists the recommended answers of 0046 used as defaults
until the maintainer decides them, the API, the trust model, TLS with
ACME for later, and the real test of 2026-10-02 on three containers.
The operator's confirmation was a status the service asserted, so a compromised service could admit any node holding a valid record proof. It is now a proof: keel cloud confirm, run on a node of the set, checks the new node's record proof and makes an HMAC with the entry secret over its set, key and overlay addresses, sent with the account key. Automatic admission is turned on the same way. Every node checks both, so the service can neither confirm a node nor turn admission on by itself; the instance's command line can only turn it off. On the node: a peer's addresses must lie inside this node's overlay prefixes; records dated ahead are refused; peers declared by hand are left alone; keel cloud forget removes a peer for good; one bad record or a listing of the wrong shape no longer stops the agent; the spec is read again after the long poll and keeps its owner; secret files are opened without following links; the pin state is flushed before it replaces the old one. The client follows no redirect and bounds what it reads. Patterns match whole values, so a trailing newline is no longer a second spelling of a key; scope ids and non-ASCII digits are refused. Entry secrets are at least 43 characters. Packaging: tighter sandboxing of both units, the CLI's umask, and a purge that removes only the development certificate it made. The package smoke test now installs from an absolute path, which was the CI failure.
Author
|
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. |
marcos-mendez
marked this pull request as draft
October 2, 2026 14:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 userkeel-cloud, SQLite storing only key hashes, public node records and the proofs it relays. Admin CLIkeel-cloud.keel-overlay-cloud:keel-cloud-node, alsokeel cloud(needs feat: the spec's cloud section and keel cloud (0046) keel#68 for the spec'scloudsection). 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 intonetwork.overlay.wireguard.peers;keel spec apply --systemconverges 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 forgetis 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 --systempluskeel network confirmbrought 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
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.