From e029944cb39f08c425d4df9207811429b3d999a4 Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 5 Jun 2026 23:38:54 +0200 Subject: [PATCH 1/2] 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" = { }; }; From 57b1a2d3eaa626dc7eb25f26e33393e0322b6799 Mon Sep 17 00:00:00 2001 From: damocles Date: Sat, 6 Jun 2026 00:21:52 +0200 Subject: [PATCH 2/2] docs: clarify certFingerprint does not govern matrix federation tls --- docs/swarm.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/swarm.md b/docs/swarm.md index 1fa69f17..4e0d6fb8 100644 --- a/docs/swarm.md +++ b/docs/swarm.md @@ -53,6 +53,13 @@ optional: for peers whose self-signed TLS cert doesn't chain to a CA your host trusts. +`certFingerprint` scopes **only** to hive-c0re's own peer HTTPS checks +(the P33RS dashboard links and agent peer discovery below). It is +**not** consulted by matrix federation — tuwunel validates a peer's +federation certificate against the system CA bundle independently (see +*Matrix federation* below), so pinning a fingerprint here does nothing +for a self-signed matrix gateway cert. + ### Fingerprint format The value is the string `sha256:` followed by exactly 64 hexadecimal @@ -105,10 +112,15 @@ environment and forwarded to agent containers. qualified names (`agent@domain`). 3. **Matrix federation** — when `matrix.enable` is on, tuwunel - federates with the peer's matrix server at - `matrix.{peer-domain}:8448`. No extra config needed; federation - works as soon as the domains are reachable and TLS validates. - See `docs/matrix.md` for federation firewall requirements. + federates with the peer's matrix server (discovered via the peer's + `.well-known/matrix/server` delegation, which the gateway serves). + Federation validates the peer's TLS certificate against the + **system CA bundle** — independently of `certFingerprint`, which it + never consults. A self-signed gateway certificate therefore won't + federate even with a fingerprint pinned above: the peers need + CA-issued certs (ACME) or a shared private CA trusted on both + gateway hosts. See `docs/matrix.md` for federation firewall + TLS + requirements. ## Bilateral setup