Watch
0
0
Fork
You've already forked hyperhive
0

send agent traffic through squid once its hive announces it

This commit is contained in:
atlas 2026-10-28 14:35:08 +01:00
commit b551eb02ff

View file

@ -1,5 +1,7 @@
# `checks.vm-hive-squid`: runs ../host-modules/hive-squid.nix in a VM, which
# the module-eval suite can only read. Three nodes on two networks:
# the module-eval suite can only read, and checks that an agent with the proxy
# variables of nix/agent-modules/egress-proxy.nix reaches squid and skips it
# where it should. Three nodes on two networks:
#
# agent (vlan 1) --> proxy (vlan 1 = the stand-in bridge, vlan 2 = uplink) --> web (vlan 2)
#
@ -170,6 +172,8 @@ pkgs.testers.runNixOSTest {
value = [ p.name ];
}) privateNets
);
# The gateway: nginx answers on the bridge, which agents reach directly.
networking.firewall.interfaces.eth1.allowedTCPPorts = [ 443 ];
# Renders the users file the way hive-c0re does.
environment.systemPackages = [ pkgs.mkpasswd ];
services.hyperhive.squid.selfcheck.unrestrictedCanary = "other.t.local";
@ -211,6 +215,8 @@ pkgs.testers.runNixOSTest {
{ lib, ... }:
{
virtualisation.vlans = [ 1 ];
# The bridge listener, by the name the proxy resolves to the same address.
networking.hosts.${bridgeIp} = [ "bridge.t.local" ];
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
{
address = "192.168.1.3";
@ -357,6 +363,45 @@ pkgs.testers.runNixOSTest {
"http://${bridgeIp}:3128/squid-internal-mgr/counters) = 403"
)
# The variables an agent's `proxy.env` carries (nix/agent-modules/egress-proxy.nix),
# as a curl on the agent reads them.
def agent_env(cred, no_proxy):
url = f"http://{cred}@${bridgeIp}:3128"
pairs = {
"HTTPS_PROXY": url, "HTTP_PROXY": url, "https_proxy": url, "http_proxy": url,
"NO_PROXY": no_proxy, "no_proxy": no_proxy,
}
return " ".join(f"{k}='{v}'" for k, v in pairs.items())
def agent_get(cred, no_proxy, url):
# The status of the answer, whoever gave it; 000 when none came.
return agent.succeed(
f"env {agent_env(cred, no_proxy)} curl -sS -m 15 -o /dev/null -w '%{{http_code}}' {url} 2>/dev/null || true"
).strip()
def squid_saw(pattern):
return re.search(pattern, proxy.succeed("journalctl -u squid.service -o cat"))
with subtest("an agent with the proxy variables reaches an allowed name through squid and a swarm-internal name directly"):
NO_PROXY = "localhost,127.0.0.1,::1,bridge.t.local,${bridgeIp}"
# Plain HTTP to the listeners' port 443, so the answer carries a status.
assert agent_get(ATLAS, NO_PROXY, "http://forge.t.local:443/") not in ("000", "403", "407")
assert squid_saw(r"192\.168\.1\.3 TCP_\w+/\d+ .* GET http://forge\.t\.local:443/ atlas "), "through squid, as atlas"
assert agent_get(ATLAS, NO_PROXY, "http://bridge.t.local:443/") not in ("000", "403", "407")
assert not squid_saw(r"GET http://bridge\.t\.local:443/ "), "a swarm-internal name never reached squid"
# Control: without the exemption the same request goes to squid, which refuses the bridge.
assert agent_get(ATLAS, "localhost", "http://bridge.t.local:443/") == "403"
assert squid_saw(r"192\.168\.1\.3 TCP_DENIED/403 .* GET http://bridge\.t\.local:443/ "), "control: squid refuses it"
# CONNECT takes the same path: HTTPS_PROXY names the proxy for https URLs.
assert agent.succeed(
f"env {agent_env(ATLAS, NO_PROXY)} curl -sS -m 15 -o /dev/null -w '%{{http_connect}}' https://forge.t.local/ 2>/dev/null || true"
).strip() == "200"
with subtest("an agent without the proxy variables goes direct"):
assert agent.succeed(
"curl -sS -m 15 -o /dev/null -w '%{http_code}' http://bridge.t.local:443/ 2>/dev/null || true"
).strip() not in ("000", "403", "407")
with subtest("the self-check passes with its credential"):
proxy.succeed("install -d -m 0700 /run/credstore")
proxy.succeed(f"printf '%s\\n' {PW['selfcheck.squid']} > /run/credstore/hive-squid.selfcheck")