108 lines
4 KiB
Nix
108 lines
4 KiB
Nix
# The password this hive's squid self-check logs in to squid with, kept where
|
|
# only root can read it.
|
|
#
|
|
# swarm-controller mints it at `swarm/hives/<hive>/squid-selfcheck`
|
|
# (`swarm_secret_client::proxy::selfcheck_path`). hive-c0re reads the same path
|
|
# to put its hash in squid's users file; this root oneshot reads it under the
|
|
# hive's own store identity and lands it as the systemd credential
|
|
# `squid-selfcheck` imports (`/run/credstore/<name>`, root, `0400`, tmpfs).
|
|
#
|
|
# A hive with no minted credential yet leaves the file alone and the self-check
|
|
# reports `credential=absent`.
|
|
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
hyperhiveCfg = config.services.hyperhive;
|
|
hiveController = hyperhiveCfg.deploy.hive-controller;
|
|
tls = hiveController.tls;
|
|
baoCfg = hyperhiveCfg.swarm.bao;
|
|
baoDeploy = hyperhiveCfg.deploy.bao;
|
|
|
|
enabled =
|
|
hiveController.enable
|
|
&& hiveController.hiveName != null
|
|
&& tls.baoHiveIdentityCertFile != null
|
|
&& tls.baoHiveIdentityKeyFile != null;
|
|
|
|
credentialFile = "/run/credstore/${hyperhiveCfg.deploy.hive-controller.squid.selfcheck.credentialName}";
|
|
|
|
# Where the store runs on this host, the first login waits for its container.
|
|
# No `wants`: a pull would start the container mid-switch on the old closure.
|
|
storeContainer = lib.optional baoDeploy.enable "container@${baoCfg.machine}.service";
|
|
|
|
storeRetry = import ./lib/store-retry.nix { };
|
|
storeRefreshTimer = import ./lib/store-refresh-timer.nix { };
|
|
in
|
|
{
|
|
config = lib.mkIf enabled {
|
|
systemd.services.squid-selfcheck-credential = {
|
|
description = "Read the squid self-check's password from the swarm secret store";
|
|
wantedBy = [ "multi-user.target" ];
|
|
before = [ "squid-selfcheck.service" ];
|
|
wants = [ "network-online.target" ];
|
|
after = [
|
|
"network-online.target"
|
|
"systemd-tmpfiles-setup.service"
|
|
]
|
|
++ storeContainer;
|
|
path = [
|
|
baoDeploy.package
|
|
pkgs.coreutils
|
|
pkgs.gnugrep
|
|
];
|
|
environment = {
|
|
BAO_ADDR = "https://${baoCfg.domain}:${toString baoCfg.port}";
|
|
BAO_CLIENT_CERT = tls.baoHiveIdentityCertFile;
|
|
BAO_CLIENT_KEY = tls.baoHiveIdentityKeyFile;
|
|
}
|
|
// lib.optionalAttrs (baoDeploy.serverCaFile != null) {
|
|
BAO_CACERT = baoDeploy.serverCaFile;
|
|
};
|
|
# ./lib/store-retry.nix: the login fails for as long as the store is
|
|
# sealed or down.
|
|
inherit (storeRetry) startLimitBurst startLimitIntervalSec;
|
|
serviceConfig = storeRetry.serviceConfig // {
|
|
Type = "oneshot";
|
|
UMask = "0077";
|
|
SyslogIdentifier = "squid-selfcheck-credential";
|
|
TimeoutStartSec = "5min";
|
|
};
|
|
script = ''
|
|
set -euo pipefail
|
|
${import ./lib/store-seeded-secret.nix { }}
|
|
${import ./lib/atomic-write-secret.nix { }}
|
|
${import ./lib/refresh-consumer.nix { }}
|
|
|
|
install -d -m 0700 /run/credstore
|
|
store_init
|
|
store_login
|
|
if [ "$STORE_UP" != 1 ]; then
|
|
echo "could not read the squid self-check credential; ${credentialFile} stays as it is." >&2
|
|
exit 1
|
|
fi
|
|
if ! value="$(store_bao kv get -field=value ${lib.escapeShellArg "secret/swarm/hives/${hiveController.hiveName}/squid-selfcheck"} 2>"$STORE_ERR")"; then
|
|
if grep -qi 'no value found' "$STORE_ERR"; then
|
|
echo "the swarm has not minted this hive's squid self-check credential yet; ${credentialFile} stays as it is."
|
|
exit 0
|
|
fi
|
|
cat "$STORE_ERR" >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! "$value" =~ ^[A-Za-z0-9_-]+$ ]]; then
|
|
echo "the stored squid self-check credential is not one base64url word; ${credentialFile} stays as it is." >&2
|
|
exit 1
|
|
fi
|
|
# Written only when the value changed, so the file keeps its mtime.
|
|
if secret_differs ${credentialFile} "$value"; then
|
|
atomic_write_secret 0400 root:root ${credentialFile} "$value"
|
|
fi
|
|
'';
|
|
};
|
|
|
|
systemd.timers.squid-selfcheck-credential = storeRefreshTimer "re-read the squid self-check's password";
|
|
};
|
|
}
|