Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44164d1a89 | ||
|
|
155df39ee4 |
2 changed files with 71 additions and 0 deletions
|
|
@ -37,6 +37,27 @@ Moving the swarm CA onto its own host is then a matter of moving
|
||||||
`services.hyperhive.swarm.ca.stateDir` and leaving `autoConfigure` off —
|
`services.hyperhive.swarm.ca.stateDir` and leaving `autoConfigure` off —
|
||||||
there is no second code path to switch to.
|
there is no second code path to switch to.
|
||||||
|
|
||||||
|
### The warning about uncovered service names
|
||||||
|
|
||||||
|
The swarm's service names (`swarm.forge.domain`,
|
||||||
|
`swarm.matrix.gatewayHost`, `swarm.authelia.domain`) default to
|
||||||
|
siblings of the hive domain — `forge.<swarm.domain>`, not
|
||||||
|
`forge.<hive domain>`. The hive CA's leaf is a **single-label** wildcard
|
||||||
|
over the hive's own domain, so it cannot cover them; only the
|
||||||
|
swarm-services leaf can, and this host signs that one only under
|
||||||
|
`autoConfigure`. A hive with neither serves the hive leaf on those names
|
||||||
|
and every client sees a name mismatch.
|
||||||
|
|
||||||
|
So `hive-tls` emits an eval-time **warning** naming the uncovered names.
|
||||||
|
It is deliberately not an assertion: this module can see what *it* is
|
||||||
|
able to issue, but not an operator-installed sub-CA in
|
||||||
|
`swarm.ca.stateDir`, an external ACME setup, or a certificate delivered
|
||||||
|
by any other means. If you have arranged one, the warning is expected
|
||||||
|
and can be ignored. Otherwise either install the sub-CA, or pin the
|
||||||
|
names back under the hive domain — a supported migration, since the
|
||||||
|
sub-CA is constrained to the *configured* names and the swarm root
|
||||||
|
carries no name constraints at all.
|
||||||
|
|
||||||
## Constraints on the material
|
## Constraints on the material
|
||||||
|
|
||||||
The root's private key never reaches the nix store: the store is
|
The root's private key never reaches the nix store: the store is
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,21 @@ let
|
||||||
# constrained to cannot disagree.
|
# constrained to cannot disagree.
|
||||||
swarmServiceDomains = hyperhiveCfg.swarm.serviceDomains;
|
swarmServiceDomains = hyperhiveCfg.swarm.serviceDomains;
|
||||||
|
|
||||||
|
# True for the names `signHiveLeaf` below actually covers: the hive
|
||||||
|
# domain itself, or ONE label under it. `*.<domain>` is a single-label
|
||||||
|
# wildcard — `a.b.<domain>` does not match it — so the depth check is
|
||||||
|
# the whole point rather than a nicety.
|
||||||
|
coveredByHiveLeaf =
|
||||||
|
n:
|
||||||
|
n == domain
|
||||||
|
|| (lib.hasSuffix ".${domain}" n && !lib.hasInfix "." (lib.removeSuffix ".${domain}" n));
|
||||||
|
|
||||||
|
# Service names this host can serve a *matching* certificate for, and
|
||||||
|
# the ones it can't. A name is issuable here when the hive leaf covers
|
||||||
|
# it, or when this host issues the swarm-services leaf — which needs
|
||||||
|
# the swarm root's private key, i.e. `swarm.ca.autoConfigure`.
|
||||||
|
uncoveredServiceDomains = lib.filter (n: !coveredByHiveLeaf n) swarmServiceDomains;
|
||||||
|
|
||||||
# The host-managed hive CA is the trust anchor for self-signed mode.
|
# The host-managed hive CA is the trust anchor for self-signed mode.
|
||||||
# It is only stood up when the gateway actually serves a self-signed
|
# It is only stood up when the gateway actually serves a self-signed
|
||||||
# cert: the gateway must be in self-signed mode. `domain` is required
|
# cert: the gateway must be in self-signed mode. `domain` is required
|
||||||
|
|
@ -244,6 +259,41 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf active {
|
config = lib.mkIf active {
|
||||||
|
# A swarm service name that is a sibling of the hive domain rather
|
||||||
|
# than a child needs the swarm-services leaf, and this host only
|
||||||
|
# signs that one when it holds the swarm root key. Without it nginx
|
||||||
|
# falls back to the hive leaf on those names and every client sees a
|
||||||
|
# name mismatch — on a config that evaluates and deploys cleanly.
|
||||||
|
#
|
||||||
|
# ⚠️ A WARNING, NOT AN ASSERTION, and the distinction is the point:
|
||||||
|
# this module can see what *it* can issue; it cannot see an
|
||||||
|
# operator-installed services sub-CA, an external ACME setup, or a
|
||||||
|
# cert delivered by any other means. A rebuild must not be blocked
|
||||||
|
# by a conclusion this host is not in a position to reach. Report
|
||||||
|
# the observation and let the operator judge it.
|
||||||
|
#
|
||||||
|
# (`certDir` / ACME modes don't reach here at all: `active` is
|
||||||
|
# self-signed-only, and there the operator's cert decides.)
|
||||||
|
warnings = lib.optional (uncoveredServiceDomains != [ ] && !swarmCaCfg.autoConfigure) ''
|
||||||
|
services.hyperhive: these swarm service names are outside this
|
||||||
|
hive's domain (${domain}), so the hive CA's leaf does not cover
|
||||||
|
them:
|
||||||
|
|
||||||
|
${lib.concatMapStringsSep "\n" (n: " ${n}") uncoveredServiceDomains}
|
||||||
|
|
||||||
|
services.hyperhive.swarm.ca.autoConfigure is false, so this host
|
||||||
|
does not sign the swarm-services leaf either, and the gateway will
|
||||||
|
serve the hive leaf on those names — a certificate-name mismatch
|
||||||
|
for browsers and for agents' git-over-https.
|
||||||
|
|
||||||
|
If you have already arranged certificates for them — an
|
||||||
|
operator-installed services sub-CA in ${swarmCaCfg.stateDir}, or
|
||||||
|
services.hyperhive.gateway.tls.{certDir,acme} — this is expected
|
||||||
|
and you can ignore it. Otherwise pin the names back under
|
||||||
|
${domain} (services.hyperhive.swarm.{forge.domain,
|
||||||
|
matrix.gatewayHost, authelia.domain}) or install the sub-CA.
|
||||||
|
'';
|
||||||
|
|
||||||
# Generate (and rotate) the hive CA + gateway leaf before the gateway
|
# Generate (and rotate) the hive CA + gateway leaf before the gateway
|
||||||
# container starts. Idempotent: the CA is created once and reused; the
|
# container starts. Idempotent: the CA is created once and reused; the
|
||||||
# leaf is re-signed on expiry under the same CA so the anchor is stable.
|
# leaf is re-signed on expiry under the same CA so the anchor is stable.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue