Compare commits

...
2 changed files with 59 additions and 6 deletions

View file

@ -53,6 +53,46 @@ 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
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.
@ -72,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

View file

@ -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" = { };
};