The thing I had been calling blocked for weeks, built in an afternoon once it was pointed out that it was already decided. 08-connectivity says of the overlay keys: each node generates its own keypair, the private half never leaves the machine, the public half is published -- and says outright this IS ADR 0004's "a node holds its own identity". Nobody had applied it to node identity itself. identity now holds the public half of each node's key. Only the public half, which is the property worth having: a copy of this database is a list of who to believe, not a set of credentials, so compromise of a node really is compromise of only that node. Exactly one key is live per node, and re-enrolment revokes the one it replaced in the same transaction -- two live identities is the stolen-laptop case with the replaced machine still believed. Fault injection was worth the time here. Three findings. The unique index was defended by no test at all: sequential enrolment is already safe because the code revokes before inserting, so removing the constraint changed nothing. The constraint only matters when two enrolments race, and there is now a test that runs six at once and fails without it. My injection harness also lied to me. One injection matched nothing, changed no file, and reported NO BITE identically to a real one -- so a test that defends nothing and an injection that does nothing look the same. The harness now checksums the files and says NO-OP when they did not change. And one honest NO BITE left standing: making the key lookup return a zero key for an unknown node does not fail the test, because the signature check refuses it a line later. Two independent mechanisms, not a placebo. 61 tests, none skipped.
39 lines
1.9 KiB
SQL
39 lines
1.9 KiB
SQL
-- What a node presents to prove it is that node.
|
|
--
|
|
-- novox/hq ADR 0004: the node generates a keypair, the private half never leaves the machine, and
|
|
-- the mesh records the public half. The same rule 08-connectivity already applies to the overlay
|
|
-- keys, applied to the thing 0004 is about.
|
|
--
|
|
-- Only the public half is here, and that is the property worth having: a copy of this database
|
|
-- grants nothing. It is a list of who to believe, not a set of credentials -- which is what makes
|
|
-- "compromise of a node is compromise of that node" literally true.
|
|
|
|
create table node_key (
|
|
id uuid primary key default gen_random_uuid(),
|
|
|
|
-- The node record this key speaks for. Held as an id rather than a foreign key: the node
|
|
-- records live in `inventory`, which is a different context and a different database
|
|
-- (novox/hq ADR 0008). There is deliberately no join to be had -- the process holding both
|
|
-- grants asks each for its part.
|
|
node uuid not null,
|
|
|
|
public bytea not null check (octet_length(public) = 32),
|
|
|
|
issued timestamptz not null default now(),
|
|
|
|
-- Issuing a re-enrolment token revokes the previous identity for that node, and that is not
|
|
-- housekeeping: two live identities for one node record is the stolen-laptop case with the
|
|
-- thief's credentials still valid.
|
|
revoked timestamptz
|
|
);
|
|
|
|
-- One live key per node. The constraint is what makes revocation mean something -- without it a
|
|
-- second enrolment would add a key rather than replace one, and the old machine would go on being
|
|
-- believed.
|
|
create unique index node_key_one_live on node_key (node) where revoked is null;
|
|
|
|
-- Redemption looks a node up by the key it presented, so that is the other direction. Not unique:
|
|
-- a revoked key stays, and a machine re-enrolling after a rebuild may legitimately present the
|
|
-- same one it had before.
|
|
create index node_key_public on node_key (public);
|