Watch
0
0
Fork
You've already forked hyperhive
0

always include a hive's own allowedDomains in the rendered swarm-wide list

This commit is contained in:
atlas 2026-10-29 01:47:25 +01:00 • committed by mara
commit 7e2f4ce7c8
2 changed files with 141 additions and 75 deletions

View file

@ -5,8 +5,8 @@
# hive-c0re renders. A credential's agent either has the swarm's
# `unrestricted_internet` flag, and may then reach any destination the deny
# rules leave, or reaches only the listed names: the base list, the allow-list
# the swarm keeps (rendered by hive-c0re beside the users file, with this
# host's `allowedDomains` as the list until the swarm has one) and the names
# the swarm keeps plus this host's `allowedDomains` (rendered by hive-c0re
# beside the users file) and the names
# the swarm lists for that agent alone. An agent sends its credential
# once it has fetched it (../agent-modules/egress-proxy.nix); the exporter,
# journal forwarding and the self-check below show the proxy works.
@ -32,8 +32,8 @@ let
# The names squid allows every authenticated user whatever the swarm lists.
# hive-c0re skips an allow-list entry that one of them already covers.
alwaysAllowed = base ++ [ cfg.selfcheck.canary ];
# What `allowedFile` holds until the swarm has an allow-list: this hive's
# own `allowedDomains`, one per line. The canary is in the configuration.
# What `allowedFile` starts as and always includes: this hive's own
# `allowedDomains`, one per line. The canary is in the configuration.
seedFile = pkgs.writeText "squid-allowed-seed" (
lib.concatMapStrings (d: d + "\n") (
lib.unique (lib.subtractLists [ cfg.selfcheck.canary ] cfg.allowedDomains)
@ -254,12 +254,13 @@ in
"crates.io"
];
description = ''
Hostnames squid lets a client reach, by CONNECT or plain HTTP, until the
swarm has an allow-list of its own: the list the swarm's controller
keeps (`/api/egress/allowed-domains`) replaces this one as soon as it
holds an entry or has had one, and this option is the list again only
while the controller has none, for instance because it predates the
feature. hive-c0re writes whichever list applies to `allowedFile`.
Hostnames squid always lets a client reach, by CONNECT or plain HTTP, on
this hive. The entries the swarm's controller keeps
(`/api/egress/allowed-domains`) add to this list and never replace it:
hive-c0re writes these names and the swarm-wide entries to
`allowedFile`. A hive-c0re that predates this rule replaced this list
with the controller's once the controller held an entry, so every hive
must run the new one before the controller creates its first entry.
Each entry matches that exact name, or that name and its subdomains
with a leading dot. Everything else gets a 403, except
for an unrestricted user. The swarm's own service names are not
@ -340,9 +341,9 @@ in
description = ''
The names squid lets every restricted user reach, one per line, read as
part of its allow ACL beside the self-check canary. It starts as a copy
of `allowedDomains`. hive-c0re rewrites it every pass: with the swarm's
swarm-wide entries once the controller has a list, otherwise with
`allowedDomains`. Squid is reloaded when it changes. Owned by
of `allowedDomains`. hive-c0re rewrites it every pass: with
`allowedDomains` and the swarm-wide entries once the controller has a
list, otherwise with `allowedDomains` alone. Squid is reloaded when it changes. Owned by
`hive-core` and readable by the `squid` group.
'';
};
@ -369,8 +370,8 @@ in
readOnly = true;
defaultText = lib.literalMD "`allowedDomains`, one per line, minus the self-check canary";
description = ''
The names `allowedFile` starts as and hive-c0re writes to it again while
the swarm has no allow-list. Read-only.
The names `allowedFile` starts as and hive-c0re writes into it on every
pass, with or without a swarm allow-list. Read-only.
'';
};

View file

@ -520,13 +520,13 @@ pkgs.testers.runNixOSTest {
wait_status("other.t.local", RUTH, "403")
assert connect("other.t.local", UMA) == "200", "control: uma is still listed"
with subtest("the swarm's allow-list replaces the hive's names, opens one to every restricted user, and a per-agent rule opens one to that agent alone"):
with subtest("the swarm's allow-list adds to the hive's names, opens one to every restricted user, and a per-agent rule opens one to that agent alone"):
seeded = proxy.succeed(f"cat {ALLOWED}")
assert connect("swarm.t.local", RUTH) == "403", "control: not allowed yet"
assert connect("agentonly.t.local", ATLAS) == "403", "control: not allowed yet"
assert connect("forge.t.local", RUTH) == "200", "control: the hive's own name"
write_allow(
"# source: swarm\nswarm.t.local\n",
"# source: swarm\n" + seeded + "swarm.t.local\n",
"# source: swarm\n"
"acl agent_allow_atlas proxy_auth atlas\n"
"acl agent_allow_atlas_dst dstdomain agentonly.t.local priv10.t.local\n"
@ -537,13 +537,13 @@ pkgs.testers.runNixOSTest {
assert connect("swarm.t.local", ATLAS) == "200", "swarm-wide, another user"
assert connect("agentonly.t.local", RUTH) == "403", "the per-agent name, another restricted user"
assert connect("agentonly.t.local") == "407", "no credential"
assert connect("forge.t.local", RUTH) == "403", "the hive's own name is replaced"
assert connect("forge.t.local", RUTH) == "200", "the hive's own name stays"
assert connect("priv10.t.local", ATLAS) == "403", "a per-agent name that resolves privately"
assert connect("agentonly.t.local", UMA) == "200", "control: unrestricted reaches it anyway"
write_allow(seeded, "")
wait_status("swarm.t.local", RUTH, "403")
wait_status("agentonly.t.local", ATLAS, "403")
assert connect("forge.t.local", RUTH) == "200", "the hive's own name is back"
assert connect("forge.t.local", RUTH) == "200", "the hive's own name after the swarm list is gone"
with subtest("squid starts on a missing list, treats every user as restricted, and recovers when it appears"):
proxy.succeed(f"rm {LIST}")