hive-tls: stage bao pki root in every hive's trust bundle
A self-signed hive fronting no swarm service name never filled `$svcroot` (/var/lib/hive-tls/swarm-services-root.pem): the only writer was `swarm-services-cert`'s issuance, which exits early on `want_svc=0`. So those hives' trust-bundle.pem lacked the store's `pki` root, even though every other hive's swarm-service names chain to it. On `want_svc=0`, `swarm-services-cert` now reads the root from the store's unauthenticated `GET /v1/pki/ca/pem`, over the hive's existing BAO_ADDR, `deploy.bao.serverCaFile` and reader identity (`deploy.bao.clientCertFile`/`clientKeyFile`) at the TLS layer only; no login. It writes `$svcroot` and restarts `hive-tls-ca` when the root changed, as the issuing path already does. The daily `swarm-services-cert-renew` timer runs the same script, so the copy follows a root rotation. The gateway no longer `Requires=` `swarm-services-cert` on such a host: nginx reads nothing that unit writes there, and the fetch retries against a sealed store. `hivectl peer-config` prints the root as step 2 of the self-signed instructions: the swarm's service names are issued under it, not the swarm root. No new option. Refs #4588.
This commit is contained in:
parent
111c27e225
commit
72f5e39278
5 changed files with 171 additions and 10 deletions
|
|
@ -42,7 +42,12 @@ let
|
|||
# path down whenever it is sealed. There the gateway starts on the hive
|
||||
# leaf fallback, and the issuing script re-imports the services leaf into
|
||||
# the running nginx once it lands.
|
||||
gatewayWaitsForServicesLeaf = localServiceDomains != [ baoCfg.ui.domain ];
|
||||
#
|
||||
# Nor on a host fronting no swarm name: it asks the store for the root
|
||||
# alone, which nginx never reads, and a sealed store would otherwise hold
|
||||
# its gateway down.
|
||||
gatewayWaitsForServicesLeaf =
|
||||
localServiceDomains != [ ] && localServiceDomains != [ baoCfg.ui.domain ];
|
||||
|
||||
# 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
|
||||
|
|
@ -283,6 +288,8 @@ let
|
|||
pkgs.diffutils
|
||||
# `flock`, for the lock at the top of the script.
|
||||
pkgs.util-linux
|
||||
# The root-only fetch on a host fronting no swarm name.
|
||||
pkgs.curl
|
||||
];
|
||||
servicesCertEnvironment = {
|
||||
BAO_ADDR = "https://${baoCfg.domain}:${toString baoCfg.port}";
|
||||
|
|
@ -299,6 +306,21 @@ let
|
|||
// lib.optionalAttrs (baoDeploy.serverCaFile != null) {
|
||||
BAO_CACERT = baoDeploy.serverCaFile;
|
||||
};
|
||||
# The root-only fetch. Same store and server CA as `servicesCertEnvironment`,
|
||||
# but the hive's reader identity: a host fronting no swarm name holds no
|
||||
# services-issuer pair.
|
||||
storeRootCurlTls = lib.escapeShellArgs (
|
||||
lib.optionals (baoDeploy.serverCaFile != null) [
|
||||
"--cacert"
|
||||
baoDeploy.serverCaFile
|
||||
]
|
||||
++ lib.optionals (baoDeploy.clientCertFile != null && baoDeploy.clientKeyFile != null) [
|
||||
"--cert"
|
||||
baoDeploy.clientCertFile
|
||||
"--key"
|
||||
baoDeploy.clientKeyFile
|
||||
]
|
||||
);
|
||||
storeRetry = import ./lib/store-retry.nix { };
|
||||
in
|
||||
{
|
||||
|
|
@ -768,8 +790,42 @@ in
|
|||
|
||||
${leafCoverage}
|
||||
|
||||
# No leaf to ask for, but the root still belongs in the trust
|
||||
# bundle: every other hive's swarm-service names chain to it.
|
||||
# `ca/pem` takes no token; the TLS client certificate is the
|
||||
# store's listener's demand, not bao's, so this presents the
|
||||
# hive's reader identity and never logs in with it.
|
||||
if [ "$want_svc" = 0 ]; then
|
||||
echo "no swarm service domains configured — nothing to issue"
|
||||
echo "no swarm service domains configured — fetching only the store's pki root"
|
||||
err="$(mktemp)"
|
||||
raw="$(mktemp "$d/swarm-services-root.XXXXXX")"
|
||||
trap 'rm -f "$err" "$raw"' EXIT
|
||||
if ! curl --fail --silent --show-error \
|
||||
${storeRootCurlTls} --output "$raw" \
|
||||
"$BAO_ADDR/v1/${baoServicesPkiMount}/ca/pem" 2>"$err"; then
|
||||
echo "could not read the store's ${baoServicesPkiMount} root." >&2
|
||||
cat "$err" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Re-encoded rather than copied: it fails on a body that does not
|
||||
# parse as a certificate, and ends the file in the newline the
|
||||
# bundle's `cat` relies on.
|
||||
if ! openssl x509 -in "$raw" -out "$svcroot.new" 2>"$err"; then
|
||||
echo "the store's ${baoServicesPkiMount} root does not parse as a certificate." >&2
|
||||
cat "$err" >&2
|
||||
exit 1
|
||||
fi
|
||||
if cmp -s "$svcroot.new" "$svcroot"; then
|
||||
rm -f "$svcroot.new"
|
||||
echo "the store's pki root is unchanged"
|
||||
exit 0
|
||||
fi
|
||||
mv -f "$svcroot.new" "$svcroot"
|
||||
chmod 0644 "$svcroot"
|
||||
if systemctl is-active --quiet hive-tls-ca.service; then
|
||||
echo "the services root changed — rebuilding the trust bundle"
|
||||
systemctl restart --no-block hive-tls-ca.service
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue