swarm: refuse hive names that CONTAIN a word identifiers are composed from
Hive-scoped identifiers are built by joining a hive name with fixed words — `hive-<name>`, `hive-<name>-agent` — so a hive called `foo-agent` produces exactly the id hive `foo`'s agent containers produce. The queue's responder resolves it as the agents, and that hive silently receives an agent grant instead of its own; a NATS denial reaches a client as a timeout, so nothing names the cause. Equality cannot see this: the two NAMES differ, only the composed strings collide. Deliberately a second file rather than three more entries in `reserved-names.nix`. That list is matched by equality against a whole name and every entry is a value some component emits; applying containment to it would refuse `forgeworks` and `operator-hq` for failures that do not exist. The two lists differ in subject (hive-only vs both), matcher (substring vs equality) and admission rule, and each file's header says so. No Rust change: `HIVE_RESERVED_NAMES` answers "may an AGENT be called this", which stays an equality question — an agent name is never composed into these identifiers. Measured against the predicate rather than assumed: pr1ma=ok alpha=ok forgeworks=ok operator-hq=ok foo-agent=REFUSED beehive=REFUSED swarm=REFUSED myswarmx=REFUSED The four `ok` cases are the control: `forgeworks` and `operator-hq` are exactly what a merged list would have refused.
This commit is contained in:
parent
dbda6fbde1
commit
cdaf5824ed
3 changed files with 94 additions and 0 deletions
|
|
@ -72,6 +72,14 @@ let
|
|||
# would be an eval error rather than a silently missing guard.
|
||||
reservedOwners = import ../reserved-names.nix;
|
||||
|
||||
# The second, narrower blacklist: words no hive name may CONTAIN, because
|
||||
# they are fixed segments of identifiers built FROM a hive name
|
||||
# (`hive-<name>`, `hive-<name>-agent`). Substring rather than equality is
|
||||
# the whole point — a hive called `foo-agent` collides in the composed
|
||||
# string while colliding with nothing in the list above. That file explains
|
||||
# why it is not merged into this one.
|
||||
reservedFragments = import ../reserved-hive-fragments.nix;
|
||||
|
||||
# A published target is declared as ONE url, because that url is also the
|
||||
# audience its token is minted for — but prometheus wants the same fact in
|
||||
# three fields. Split it here rather than asking a service to state it
|
||||
|
|
@ -685,6 +693,44 @@ in
|
|||
Rename the hive. Reserved: ${lib.concatMapStringsSep ", " (n: "'${n}'") reservedOwners}.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# The substring half, and a genuinely different failure from the one
|
||||
# above: these words are fixed SEGMENTS of identifiers composed from a
|
||||
# hive name, so the collision is between two composed strings while
|
||||
# the names themselves differ. `hive-foo-agent` is hive `foo-agent`'s
|
||||
# own client id and hive `foo`'s AGENT client id at the same time; the
|
||||
# queue's responder resolves it as the agents, so `foo-agent` silently
|
||||
# receives an agent grant instead of its own — and a NATS denial
|
||||
# reaches a client as a timeout, so nothing names the cause.
|
||||
#
|
||||
# Lives here rather than in `swarm-authelia.nix` (which composes those
|
||||
# ids) because this module already owns the hive-roster check and a
|
||||
# second site for "is this hive name legal" is how the two drift.
|
||||
assertion =
|
||||
!lib.any (h: lib.any (frag: lib.hasInfix frag h) reservedFragments) (
|
||||
lib.attrNames hyperhiveCfg.swarm.hives
|
||||
);
|
||||
message = ''
|
||||
services.hyperhive.swarm.hives contains ${
|
||||
lib.concatMapStringsSep ", " (h: "'${h}'") (
|
||||
lib.filter (h: lib.any (frag: lib.hasInfix frag h) reservedFragments) (
|
||||
lib.attrNames hyperhiveCfg.swarm.hives
|
||||
)
|
||||
)
|
||||
}, which CONTAIN a word the swarm composes identifiers from.
|
||||
|
||||
Hive-scoped identifiers are built by joining a hive name with these
|
||||
words — `hive-<name>` for a hive, `hive-<name>-agent` for its agent
|
||||
containers — so a name containing one produces an identifier that is
|
||||
also somebody else's. The failure is a wrong grant, not an error:
|
||||
the client authenticates and receives permissions meant for another
|
||||
principal.
|
||||
|
||||
Rename the hive. Forbidden as substrings: ${
|
||||
lib.concatMapStringsSep ", " (n: "'${n}'") reservedFragments
|
||||
}.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# A published target that is not an `https://host/path` url. Without
|
||||
# this the split returns null and the failure surfaces as
|
||||
|
|
|
|||
Loading…
Reference in a new issue