swarm CA: retire the host-held root CA, the store holds the only anchor
The secret store's pki root issues every hive's gateway leaf and the
swarm's service names, so the root swarm-ca.service generated on the
host anchors nothing and its key is a secret outside the store.
- swarm-ca.nix is gone: swarm-ca.service, root.pem and root-key.pem,
and the swarm.ca.{autoConfigure,stateDir,validityDays} options. A
definition of any of them fails evaluation with a removal message
(swarm-ca-removed.nix), and a tmpfiles rule deletes /var/lib/swarm-ca
on every host. singleHostSwarm no longer sets a swarm.* option.
- hive-tls-ca drops the $root slot: trust-bundle.pem holds the store's
pki root and a hive CA's public ca.pem/ca-previous.pem only.
- hivectl peer-config no longer prints the scp line for the swarm root;
the store's pki root is the one step.
- The gateway's cert import no longer stands a hive leaf or a throwaway
in for a missing swarm-services leaf where it Requires= the issuing
unit: it fails, naming swarm-services-cert.service. The stand-in stays
where the gateway does not wait (no swarm name, or the store's UI
only, whose passthrough must start with the store sealed).
Comments and docs that named swarm-ca.nix, swarm.ca.* or the host root
now name the store's pki root.
Refs #4588
This commit is contained in:
parent
5d6ebda3e8
commit
93f1a76ea7
22 changed files with 305 additions and 395 deletions
|
|
@ -29,6 +29,16 @@ let
|
|||
# The hive leaf exists only where a hive runs; ../hive-tls.nix issues it
|
||||
# under that same condition.
|
||||
runsHive = config.services.hyperhive.deploy.hive-controller.enable;
|
||||
# Whether the cert import `Requires=` the services leaf's issuing unit
|
||||
# (../hive-tls.nix decides). Where it does, a missing leaf fails the
|
||||
# import rather than anything standing in for it.
|
||||
svcLeafRequired = lib.elem "hive-gateway-self-signed-cert.service" (
|
||||
config.systemd.services.swarm-services-cert.requiredBy or [ ]
|
||||
);
|
||||
svcLeafMissing = ''
|
||||
echo "no swarm-services leaf at ${tlsStateDir}/swarm-services.pem, and this gateway serves swarm names only with it." >&2
|
||||
echo "swarm-services-cert.service issues it from the secret store's pki mount; its journal says why it has not." >&2
|
||||
exit 1'';
|
||||
|
||||
# Every vhost claiming `default_server`, ours and the operator's
|
||||
# alike. Computed once so the assertion below and the message it
|
||||
|
|
@ -252,10 +262,9 @@ in
|
|||
# 🚨 DO NOT "simplify" this into pointing nginx at the CA dir. It does
|
||||
# TWO jobs: it re-modes the leaf to the 0640 root:nginx copy below (a
|
||||
# 0600 key fails nginx's pre-start config test and blocks the unit),
|
||||
# and it guarantees every cert path the config names exists — which is
|
||||
# what the swarm-services fallback at the bottom of the script is for.
|
||||
# A leaf that never issues therefore takes the WHOLE gateway down, not
|
||||
# one vhost. Both mechanisms, and what it cost the time this was
|
||||
# and it puts a file at every cert path the config names or fails: one
|
||||
# nginx cannot find takes the WHOLE gateway down, not one vhost. Both
|
||||
# mechanisms, and what it cost the time this was
|
||||
# collapsed: `docs/networking/gateway.md::Self-signed TLS (default)`.
|
||||
#
|
||||
# ALWAYS runs (no ConditionPathExists) and is idempotent, because it
|
||||
|
|
@ -307,49 +316,51 @@ in
|
|||
# because the hive leaf's role allows only its own hive's
|
||||
# domain, and the service names are siblings of it.
|
||||
#
|
||||
# Absent is a normal state, not a failure: a host fronting
|
||||
# no swarm name has none, and issuance can also fail on a
|
||||
# host that wants one.
|
||||
#
|
||||
# ⚠️ When it is absent the HIVE leaf goes to this path
|
||||
# anyway, and that fallback is load-bearing rather than
|
||||
# tidy. nginx refuses to load a config naming a cert file
|
||||
# that does not exist — `cannot load certificate … no such
|
||||
# file` fails the pre-start test, so the vhost does not
|
||||
# degrade, the ENTIRE gateway dies and takes the forge, the
|
||||
# dashboard and matrix with it. Serving the hive leaf on a
|
||||
# swarm-service name is a name mismatch: browsers warn,
|
||||
# strict clients refuse, everything else keeps working, and
|
||||
# the operator gets a bad cert instead of no hive.
|
||||
#
|
||||
# Measured, not theorised: this exact path took pr1ma's
|
||||
# gateway down when the services sub-CA failed to issue.
|
||||
# Where the gateway waits for it, a missing leaf fails this
|
||||
# unit and nginx with it. Elsewhere — no swarm name here, or
|
||||
# only the store's UI, whose host must start its passthrough
|
||||
# with the store sealed — the hive leaf stands in, because
|
||||
# nginx refuses a config naming a missing cert file, until
|
||||
# `swarm-services-cert` issues one and re-runs this unit.
|
||||
if [ -s ${tlsStateDir}/swarm-services.pem ]; then
|
||||
install -m 0644 ${tlsStateDir}/swarm-services.pem ${svcCert}
|
||||
install -m 0640 -g nginx ${tlsStateDir}/swarm-services-key.pem ${svcKey}
|
||||
else
|
||||
echo "no swarm-services leaf — serving the hive leaf on those names (mismatch, not an outage)" >&2
|
||||
install -m 0644 ${tlsStateDir}/gateway.pem ${svcCert}
|
||||
install -m 0640 -g nginx ${tlsStateDir}/gateway-key.pem ${svcKey}
|
||||
${
|
||||
if svcLeafRequired then
|
||||
svcLeafMissing
|
||||
else
|
||||
''
|
||||
echo "no swarm-services leaf yet — serving the hive leaf on those names until swarm-services-cert.service issues one" >&2
|
||||
install -m 0644 ${tlsStateDir}/gateway.pem ${svcCert}
|
||||
install -m 0640 -g nginx ${tlsStateDir}/gateway-key.pem ${svcKey}''
|
||||
}
|
||||
fi''
|
||||
else
|
||||
''
|
||||
# No hive here, so no hive leaf: every vhost, the catch-all
|
||||
# included, serves the swarm-services leaf. Until it lands a
|
||||
# throwaway self-signed pair stands in, because nginx refuses
|
||||
# a config naming a missing cert file. A store-only host does
|
||||
# not wait for the leaf, so its stream passthrough comes up with
|
||||
# the store sealed. `swarm-services-cert` re-runs this unit
|
||||
# once the leaf lands.
|
||||
# included, serves the swarm-services leaf. Where the gateway
|
||||
# waits for it, a missing leaf fails this unit. A store-only
|
||||
# host does not wait, so its stream passthrough comes up with
|
||||
# the store sealed: a throwaway self-signed pair stands in,
|
||||
# because nginx refuses a config naming a missing cert file,
|
||||
# until `swarm-services-cert` issues the leaf and re-runs this
|
||||
# unit.
|
||||
if [ -s ${tlsStateDir}/swarm-services.pem ] && [ -s ${tlsStateDir}/swarm-services-key.pem ]; then
|
||||
cert=${tlsStateDir}/swarm-services.pem
|
||||
key=${tlsStateDir}/swarm-services-key.pem
|
||||
else
|
||||
echo "no swarm-services leaf yet — serving a throwaway self-signed certificate until it lands" >&2
|
||||
cert=${tlsDir}/fallback.pem
|
||||
key=${tlsDir}/fallback-key.pem
|
||||
(umask 077; openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 30 \
|
||||
-subj "/CN=hyperhive-gateway-fallback" -keyout "$key" -out "$cert")
|
||||
${
|
||||
if svcLeafRequired then
|
||||
svcLeafMissing
|
||||
else
|
||||
''
|
||||
echo "no swarm-services leaf yet — serving a throwaway self-signed certificate until it lands" >&2
|
||||
cert=${tlsDir}/fallback.pem
|
||||
key=${tlsDir}/fallback-key.pem
|
||||
(umask 077; openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 30 \
|
||||
-subj "/CN=hyperhive-gateway-fallback" -keyout "$key" -out "$cert")''
|
||||
}
|
||||
fi
|
||||
install -m 0644 "$cert" ${tlsCert}
|
||||
install -m 0640 -g nginx "$key" ${tlsKey}
|
||||
|
|
|
|||
Loading…
Reference in a new issue