swarm-controller: provision + collect its own forge account token
This commit is contained in:
parent
b03ae55786
commit
c1e46b378d
2 changed files with 213 additions and 12 deletions
|
|
@ -34,6 +34,7 @@ let
|
|||
};
|
||||
|
||||
natsCfg = config.services.hyperhive.swarm.nats;
|
||||
forgeCfg = config.services.hyperhive.swarm.forge;
|
||||
|
||||
# The controller's own OAuth2 client. It is NOT a hive: the per-hive
|
||||
# clients the roster issues belong to hives, and the responder's client
|
||||
|
|
@ -66,6 +67,22 @@ let
|
|||
SWARM_CONTROLLER_OIDC_CLIENT_SECRET_FILE = "%d/queue-client.secret";
|
||||
};
|
||||
|
||||
# Independent of `queueEnv`/`queueLocal` on purpose — the queue
|
||||
# coordinates' own co-location guard is narrower than it looks (it
|
||||
# silently drops the controller's identity on a host that splits
|
||||
# authelia/NATS from swarm-controller; a known gap tracked separately).
|
||||
# Forge access has nothing to do with whether authelia+NATS happen to be
|
||||
# local, only with whether `cfg.forgeTokenFile` resolves to a real file —
|
||||
# which `forgeTokenFile`'s own default already handles (forge-local vs.
|
||||
# operator-copied). Gating a second time here would just repeat that
|
||||
# option's own logic under a different name.
|
||||
forgeEnv = lib.optionalAttrs (cfg.forgeTokenFile != null) {
|
||||
SWARM_CONTROLLER_FORGE_URL = "https://${forgeCfg.domain}";
|
||||
# Same `%d` shape as the queue secret above — root reads the plaintext
|
||||
# at unit start, the daemon's own user sees a 0400 copy.
|
||||
SWARM_CONTROLLER_FORGE_TOKEN_FILE = "%d/forge-token";
|
||||
};
|
||||
|
||||
# Wrapped rather than documented: every one of these values is derived
|
||||
# from an option this deployment already set, so making the operator
|
||||
# re-supply them on the command line would be asking them to repeat the
|
||||
|
|
@ -211,6 +228,33 @@ in
|
|||
module's own activation condition.
|
||||
'';
|
||||
};
|
||||
|
||||
forgeTokenFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = if forgeCfg.enable then forgeCfg.hostSwarmControllerTokenFile else null;
|
||||
defaultText = lib.literalExpression ''
|
||||
forge's own `hostSwarmControllerTokenFile` when this host runs
|
||||
forge, else null
|
||||
'';
|
||||
example = "/var/lib/secrets/swarm-controller-forge.token";
|
||||
description = ''
|
||||
Path to this host's copy of the swarm-controller's forge access
|
||||
token (see `hive-forge/default.nix`'s
|
||||
`forgejo-swarm-controller-account` + `hive-forge-swarm-controller-token`
|
||||
units, which mint and collect it onto forge's own host).
|
||||
|
||||
Defaults to forge's own delivery path only when forge runs on
|
||||
this same host. On any other host the token has to get here
|
||||
somehow — copy it out of forge's
|
||||
{option}`services.hyperhive.swarm.forge.hostSwarmControllerTokenFile`
|
||||
with whatever secret management this deployment already uses,
|
||||
the same shape `swarm.nix`'s `clientSecretFile` documents for
|
||||
the analogous cross-host case. `null` (the default when forge
|
||||
isn't local and nothing else was set) means no forge access —
|
||||
the daemon logs that and continues without it, the same
|
||||
graceful-absence shape the queue coordinates already use.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.hyperhive.enable && cfg.enable) {
|
||||
|
|
@ -251,13 +295,14 @@ in
|
|||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/swarm-controller";
|
||||
|
||||
# Only when the queue is actually reachable from here. An absent
|
||||
# credential is not a failure: the daemon logs that no queue is
|
||||
# configured and serves its HTTP surface, which is the correct
|
||||
# behaviour on the hosts that do not run one.
|
||||
LoadCredential = lib.mkIf queueLocal [
|
||||
"queue-client.secret:${autheliaCfg.hostClientSecretDir}/${queueClientId}.secret"
|
||||
];
|
||||
# Only the credentials that actually resolve on this host. An
|
||||
# absent credential is not a failure for either: the daemon logs
|
||||
# that no queue / no forge is configured and serves its HTTP
|
||||
# surface regardless, which is the correct behaviour on a host
|
||||
# that doesn't run one.
|
||||
LoadCredential =
|
||||
lib.optional queueLocal "queue-client.secret:${autheliaCfg.hostClientSecretDir}/${queueClientId}.secret"
|
||||
++ lib.optional (cfg.forgeTokenFile != null) "forge-token:${cfg.forgeTokenFile}";
|
||||
User = "swarm-controller";
|
||||
Group = "swarm-controller";
|
||||
Restart = "on-failure";
|
||||
|
|
@ -295,10 +340,11 @@ in
|
|||
];
|
||||
};
|
||||
|
||||
# Queue coordinates (`queueEnv`) merge in last and are present only
|
||||
# where the queue and its IdP both run. The daemon refuses a PARTIAL
|
||||
# set rather than treating it as absent, which is why they are built
|
||||
# as one attrset and never assigned individually.
|
||||
# Queue coordinates (`queueEnv`) and forge coordinates (`forgeEnv`)
|
||||
# merge in last, each present only when its own resolution actually
|
||||
# succeeded on this host. Both refuse a PARTIAL set rather than
|
||||
# treating it as absent, which is why each is built as one attrset
|
||||
# and never assigned individually.
|
||||
environment = {
|
||||
SWARM_CONTROLLER_SOCKET = cfg.socketPath;
|
||||
# The swarm's hive directory, JSON-encoded — the full directory
|
||||
|
|
@ -323,7 +369,8 @@ in
|
|||
# off (the daemon just has nothing to apply it to).
|
||||
SWARM_CONTROLLER_STALE_AFTER_SECS = toString cfg.staleAfterSeconds;
|
||||
}
|
||||
// queueEnv;
|
||||
// queueEnv
|
||||
// forgeEnv;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue