From e029944cb39f08c425d4df9207811429b3d999a4 Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 5 Jun 2026 23:38:54 +0200 Subject: [PATCH] docs: specify certFingerprint format and generation recipe --- docs/swarm.md | 33 +++++++++++++++++++++++++++++++++ nix/modules/hive-c0re.nix | 12 ++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/swarm.md b/docs/swarm.md index e9c8587c..1fa69f17 100644 --- a/docs/swarm.md +++ b/docs/swarm.md @@ -53,6 +53,39 @@ optional: for peers whose self-signed TLS cert doesn't chain to a CA your host trusts. +### Fingerprint format + +The value is the string `sha256:` followed by exactly 64 hexadecimal +digits — the SHA-256 digest of the peer's DER-encoded TLS leaf +certificate. The hex is case-insensitive (upper or lower both parse), +carries no colon separators between bytes, and any value not matching +this shape is ignored with a warning rather than weakening trust. + +``` +sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12 +``` + +Generate it from the peer's certificate with openssl. The +`-fingerprint -sha256` output is uppercase and colon-separated, so +strip the colons, lowercase, and prepend the `sha256:` prefix: + +```sh +# from a PEM/CRT file +openssl x509 -in peer.crt -noout -fingerprint -sha256 \ + | sed 's/^.*=//; s/://g' | tr 'A-Z' 'a-z' | sed 's/^/sha256:/' + +# straight from the live endpoint (port 443) +echo | openssl s_client -connect peer.example.com:443 -servername peer.example.com 2>/dev/null \ + | openssl x509 -noout -fingerprint -sha256 \ + | sed 's/^.*=//; s/://g' | tr 'A-Z' 'a-z' | sed 's/^/sha256:/' +``` + +Pin the leaf certificate, not an intermediate or the CA — the +digest must match the exact cert the peer serves on its HTTPS +endpoint. When the peer rotates its cert, update the pin to the new +fingerprint (or switch the peer to a CA-trusted cert and drop the +field). + The nix module serialises the attrset to a `HYPERHIVE_PEERS` JSON array (`[{ domain, cert_fingerprint }]`) injected into the c0re environment and forwarded to agent containers. diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 9ae2899e..d66bc99b 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -120,11 +120,19 @@ in certFingerprint = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; - example = "sha256:abc123..."; + example = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12"; description = '' Expected TLS certificate fingerprint for this peer's HTTPS endpoint. Null = trust the system CA bundle (for Let's Encrypt peers). Set to pin a self-signed cert. + + Format: the literal `sha256:` followed by exactly 64 + hex digits (case-insensitive, no colon separators) — the + SHA-256 digest of the peer's DER-encoded leaf certificate. + Generate with `openssl x509 -noout -fingerprint -sha256`, + then strip the colons and prepend `sha256:`. A malformed + value is ignored with a warning rather than weakening + trust. See docs/swarm.md for the full recipe. ''; }; @@ -173,7 +181,7 @@ in default = { }; example = { "lab.example.com" = { - certFingerprint = "sha256:abc123"; + certFingerprint = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12"; }; "edge.corp" = { }; };