swarm: default every queue URL to the queue's name on every hive
A remote hive dialled nothing until an operator copied the queue's URL into it, though the URL is the same string everywhere. statusPublish.natsUrl, queue.agentNatsUrl and controller.queue.natsUrl now default to tls://<swarm.nats.domain>:<port> unconditionally. The statusPublish assertion treated a URL without a secret as a half config. With the URL a default on every hive, only the secret claims publishing: the assertion now refuses a secret without a URL or token endpoint, and hive-c0re's status environment is gated on the secret too, so a hive without one publishes nothing instead of reading a missing credential.
This commit is contained in:
parent
0081d75c86
commit
a5259146dc
8 changed files with 220 additions and 129 deletions
|
|
@ -42,6 +42,10 @@ let
|
|||
# renders the same absences `bare` does.
|
||||
centralToggleOff = hive { enable = false; };
|
||||
|
||||
# Every hive defaults the agents' queue address to the queue's name, so the
|
||||
# only hive without one is a hive told to have none.
|
||||
noAgentQueue = hive { deploy.hive-controller.queue.agentNatsUrl = null; };
|
||||
|
||||
withCi = hive { deploy.forgejo.ci.enable = true; };
|
||||
|
||||
# A priority collision is a property of the *option*, not
|
||||
|
|
@ -149,13 +153,13 @@ let
|
|||
}
|
||||
{
|
||||
# The absence arm, and what makes the two above able to fail: a hive
|
||||
# with no queue address must forward neither coordinate, because half a
|
||||
# pair reaches the harness as a partial configuration rather than as
|
||||
# none.
|
||||
name = "a hive with no swarm queue forwards no agent queue coordinates";
|
||||
# with no queue address for its agents must forward neither coordinate,
|
||||
# because half a pair reaches the harness as a partial configuration
|
||||
# rather than as none.
|
||||
name = "a hive with no agent queue address forwards no agent queue coordinates";
|
||||
ok =
|
||||
let
|
||||
e = bare.systemd.services.hive-c0re.environment;
|
||||
e = noAgentQueue.systemd.services.hive-c0re.environment;
|
||||
in
|
||||
!(e ? HIVE_AGENT_NATS_URL) && !(e ? HIVE_AGENT_OIDC_TOKEN_ENDPOINT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,40 @@ let
|
|||
deploy.nats.autoGenerateCallout = true;
|
||||
};
|
||||
|
||||
# A hive that is not the queue's host, with nothing about the queue's
|
||||
# address set by hand: what every hive but one in a multi-host swarm looks
|
||||
# like. The controller is on too, since it may run away from the queue.
|
||||
#
|
||||
# The two secrets are the ones a hive away from authelia already has to be
|
||||
# handed, and neither is an address; without them this hive would fail
|
||||
# assertions that have nothing to do with the queue.
|
||||
remoteSecrets = {
|
||||
deploy.forgejo.sso.clientSecretFile = "/var/lib/forgejo-oidc/by-hand.secret";
|
||||
deploy.swarm-controller.queue.clientSecretFile = "/var/lib/secrets/swarm-controller.secret";
|
||||
};
|
||||
remote = hive (lib.recursiveUpdate remoteSecrets { deploy.swarm-controller.enable = true; });
|
||||
|
||||
# The same hive given its status secret by hand, which is what turns
|
||||
# publishing on away from the IdP's host.
|
||||
remotePublishing = hive (
|
||||
lib.recursiveUpdate remoteSecrets {
|
||||
deploy.hive-controller.statusPublish.clientSecretFile = "/var/lib/secrets/hive-h1.secret";
|
||||
}
|
||||
);
|
||||
|
||||
# A real half-config: the secret, with the URL it would be presented at
|
||||
# taken away.
|
||||
remoteSecretNoUrl = hive (
|
||||
lib.recursiveUpdate remoteSecrets {
|
||||
deploy.hive-controller.statusPublish.clientSecretFile = "/var/lib/secrets/hive-h1.secret";
|
||||
deploy.hive-controller.statusPublish.natsUrl = null;
|
||||
}
|
||||
);
|
||||
|
||||
failedAssertions = m: lib.filter (a: !a.assertion) m.assertions;
|
||||
refusedStatusSecret =
|
||||
m: lib.any (a: lib.hasInfix "status-publishing client secret" a.message) (failedAssertions m);
|
||||
|
||||
policyScript = allLocal.systemd.services.swarm-bao-nats-tls-policy.script;
|
||||
leafUnit = allLocal.systemd.services.swarm-bao-nats-tls;
|
||||
natsContainer = allLocal.containers.swarm-nats.config;
|
||||
|
|
@ -78,6 +112,8 @@ let
|
|||
containerUnits = lib.concatLists (
|
||||
lib.mapAttrsToList (c: cc: fromUnits c cc.config.systemd.services) machine.containers
|
||||
);
|
||||
# The responder runs beside the queue only, so a hive without one has
|
||||
# none to read.
|
||||
responder =
|
||||
map
|
||||
(u: {
|
||||
|
|
@ -85,7 +121,9 @@ let
|
|||
value = u;
|
||||
})
|
||||
(
|
||||
urlsIn machine.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart
|
||||
lib.optionals (machine.containers ? swarm-nats) (
|
||||
urlsIn machine.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart
|
||||
)
|
||||
);
|
||||
in
|
||||
lib.listToAttrs (fromUnits "host" machine.systemd.services ++ containerUnits ++ responder);
|
||||
|
|
@ -212,6 +250,65 @@ let
|
|||
!(lib.elem "swarm-bao-nats-tls-policy.service" u.after)
|
||||
&& !(lib.elem "swarm-bao-nats-tls-policy.service" u.wants);
|
||||
}
|
||||
{
|
||||
# Every hive dials the queue by the same name, so a hive away from it
|
||||
# needs no URL of its own. Control and property in one: the scan must
|
||||
# reach the controller and the agents' address here, and every URL it
|
||||
# finds, like the options it reads through, is the name.
|
||||
name = "a hive that is not the queue's host dials tls://<the queue's name>:4222 with nothing set";
|
||||
ok =
|
||||
let
|
||||
s = clientUrls remote;
|
||||
d = remote.services.hyperhive.deploy;
|
||||
in
|
||||
!d.nats.enable
|
||||
&& s ? "host/hive-c0re/HIVE_AGENT_NATS_URL"
|
||||
&& s ? "host/swarm-controller/SWARM_CONTROLLER_NATS_URL"
|
||||
&& lib.all (u: u == natsUrl) (lib.attrValues s)
|
||||
&& d.hive-controller.statusPublish.natsUrl == natsUrl
|
||||
&& d.hive-controller.queue.agentNatsUrl == natsUrl
|
||||
&& remote.services.hyperhive.swarm.controller.queue.natsUrl == natsUrl;
|
||||
}
|
||||
{
|
||||
# The URL is set and the secret is not, which is every such hive until
|
||||
# an operator places one: publishing is off, not misconfigured.
|
||||
name = "that hive evaluates without an assertion failure";
|
||||
ok = failedAssertions remote == [ ];
|
||||
}
|
||||
{
|
||||
# Off means off: hive-c0re is handed no status coordinates, rather
|
||||
# than a secret path nothing fills.
|
||||
name = "without its status secret, that hive's hive-c0re is given no status coordinates";
|
||||
ok =
|
||||
let
|
||||
s = remote.systemd.services.hive-c0re;
|
||||
in
|
||||
!(s.environment ? HIVE_C0RE_NATS_URL)
|
||||
&& !(s.environment ? HIVE_C0RE_OIDC_CLIENT_SECRET_FILE)
|
||||
&& !(lib.any (lib.hasPrefix "swarm-status-client.secret:") (
|
||||
lib.toList (s.serviceConfig.LoadCredential or [ ])
|
||||
));
|
||||
}
|
||||
{
|
||||
# The secret alone turns publishing on, at the default URL.
|
||||
name = "given its status secret, that hive publishes to the queue's name";
|
||||
ok =
|
||||
let
|
||||
s = remotePublishing.systemd.services.hive-c0re;
|
||||
in
|
||||
failedAssertions remotePublishing == [ ]
|
||||
&& s.environment.HIVE_C0RE_NATS_URL == natsUrl
|
||||
&& s.environment.HIVE_C0RE_OIDC_CLIENT_SECRET_FILE == "%d/swarm-status-client.secret"
|
||||
&& lib.elem "swarm-status-client.secret:/var/lib/secrets/hive-h1.secret" (
|
||||
lib.toList s.serviceConfig.LoadCredential
|
||||
);
|
||||
}
|
||||
{
|
||||
# What the assertion was written for is still refused: a secret with
|
||||
# nowhere to present it.
|
||||
name = "a status secret without a queue URL is refused at eval";
|
||||
ok = refusedStatusSecret remoteSecretNoUrl && !(refusedStatusSecret remote);
|
||||
}
|
||||
];
|
||||
in
|
||||
runGroup "nats-tls" cases
|
||||
|
|
|
|||
Loading…
Reference in a new issue