move the squid host options to deploy.hive-controller.squid
This commit is contained in:
parent
c9b5705ad8
commit
da16487800
11 changed files with 168 additions and 57 deletions
|
|
@ -254,6 +254,45 @@ in
|
|||
[ "services" "hyperhive" "c0re" "adminUsers" ]
|
||||
[ "services" "hyperhive" "deploy" "hive-controller" "adminUsers" ]
|
||||
)
|
||||
|
||||
# The egress proxy runs only on a hive host, so its settings sit with the
|
||||
# controller's. The read-only ones moved without a forward.
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "hyperhive" "squid" "allowedDomains" ]
|
||||
[ "services" "hyperhive" "deploy" "hive-controller" "squid" "allowedDomains" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "hyperhive" "squid" "baseAllowedDomains" ]
|
||||
[ "services" "hyperhive" "deploy" "hive-controller" "squid" "baseAllowedDomains" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "hyperhive" "squid" "usersFile" ]
|
||||
[ "services" "hyperhive" "deploy" "hive-controller" "squid" "usersFile" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "hyperhive" "squid" "selfcheck" "unrestrictedCanary" ]
|
||||
[
|
||||
"services"
|
||||
"hyperhive"
|
||||
"deploy"
|
||||
"hive-controller"
|
||||
"squid"
|
||||
"selfcheck"
|
||||
"unrestrictedCanary"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "hyperhive" "squid" "selfcheck" "canary" ]
|
||||
[
|
||||
"services"
|
||||
"hyperhive"
|
||||
"deploy"
|
||||
"hive-controller"
|
||||
"squid"
|
||||
"selfcheck"
|
||||
"canary"
|
||||
]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "hyperhive" "otel" "extraResourceAttributes" ]
|
||||
[ "services" "hyperhive" "deploy" "hive-controller" "otel" "extraResourceAttributes" ]
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ in
|
|||
# `-` so a host without the squid users directory still starts the daemon.
|
||||
ReadWritePaths = [
|
||||
"/var/lib/hive-gateway/conf"
|
||||
"-${builtins.dirOf config.services.hyperhive.squid.usersFile}"
|
||||
"-${builtins.dirOf config.services.hyperhive.deploy.hive-controller.squid.usersFile}"
|
||||
];
|
||||
ProtectKernelTunables = true; # no sysctl writes
|
||||
ProtectKernelLogs = true; # no /dev/kmsg access needed
|
||||
|
|
|
|||
|
|
@ -290,21 +290,23 @@ in
|
|||
# credentials for squid, and the list of the users whose agent holds the
|
||||
# swarm's `unrestricted_internet` flag; it reads the credentials from the
|
||||
# store under the identity above.
|
||||
HIVE_SQUID_USERS_FILE = config.services.hyperhive.squid.usersFile;
|
||||
HIVE_SQUID_UNRESTRICTED_FILE = config.services.hyperhive.squid.unrestrictedFile;
|
||||
HIVE_SQUID_USERS_FILE = config.services.hyperhive.deploy.hive-controller.squid.usersFile;
|
||||
HIVE_SQUID_UNRESTRICTED_FILE =
|
||||
config.services.hyperhive.deploy.hive-controller.squid.unrestrictedFile;
|
||||
# The swarm's egress allow-list, which `workers::squid_allow` renders into
|
||||
# the two files; the seed is the list the files hold until the swarm has one,
|
||||
# and the always-allowed names are those it never repeats.
|
||||
HIVE_SQUID_ALLOWED_FILE = config.services.hyperhive.squid.allowedFile;
|
||||
HIVE_SQUID_AGENT_ALLOW_FILE = config.services.hyperhive.squid.agentAllowFile;
|
||||
HIVE_SQUID_ALLOWED_SEED_FILE = config.services.hyperhive.squid.allowedSeedFile;
|
||||
HIVE_SQUID_ALWAYS_ALLOWED = lib.concatStringsSep " " config.services.hyperhive.squid.alwaysAllowed;
|
||||
HIVE_SQUID_ALLOWED_FILE = config.services.hyperhive.deploy.hive-controller.squid.allowedFile;
|
||||
HIVE_SQUID_AGENT_ALLOW_FILE = config.services.hyperhive.deploy.hive-controller.squid.agentAllowFile;
|
||||
HIVE_SQUID_ALLOWED_SEED_FILE =
|
||||
config.services.hyperhive.deploy.hive-controller.squid.allowedSeedFile;
|
||||
HIVE_SQUID_ALWAYS_ALLOWED = lib.concatStringsSep " " config.services.hyperhive.deploy.hive-controller.squid.alwaysAllowed;
|
||||
# The egress proxy facts every container is handed (`HiveFacts` in
|
||||
# `hive-priv-sock`). They sit with the users file above because that is what
|
||||
# makes squid's credential check and per-agent policy real on this host: an
|
||||
# agent sends nothing through the proxy unless it reads both. Absent on a
|
||||
# hive that predates them, so its agents go direct.
|
||||
HIVE_SQUID_PROXY = config.services.hyperhive.squid.listen;
|
||||
HIVE_SQUID_PROXY = config.services.hyperhive.deploy.hive-controller.squid.listen;
|
||||
HIVE_EGRESS_NO_PROXY = lib.concatStringsSep "," egressDirectHosts;
|
||||
}
|
||||
// lib.optionalAttrs (haveBaoClientIdentity && baoDeploy.serverCaFile != null) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ let
|
|||
&& tls.baoHiveIdentityCertFile != null
|
||||
&& tls.baoHiveIdentityKeyFile != null;
|
||||
|
||||
credentialFile = "/run/credstore/${hyperhiveCfg.squid.selfcheck.credentialName}";
|
||||
credentialFile = "/run/credstore/${hyperhiveCfg.deploy.hive-controller.squid.selfcheck.credentialName}";
|
||||
|
||||
storeRetry = import ./lib/store-retry.nix { };
|
||||
storeRefreshTimer = import ./lib/store-refresh-timer.nix { };
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
}:
|
||||
let
|
||||
hyperhiveCfg = config.services.hyperhive;
|
||||
cfg = hyperhiveCfg.squid;
|
||||
cfg = hyperhiveCfg.deploy.hive-controller.squid;
|
||||
netCfg = hyperhiveCfg.network;
|
||||
|
||||
port = 3128;
|
||||
|
|
@ -232,7 +232,7 @@ let
|
|||
};
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.squid = {
|
||||
options.services.hyperhive.deploy.hive-controller.squid = {
|
||||
listen = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = listen;
|
||||
|
|
@ -492,7 +492,7 @@ in
|
|||
{
|
||||
assertion = !(builtins.elem cfg.selfcheck.unrestrictedCanary (allowed ++ base));
|
||||
message = ''
|
||||
services.hyperhive.squid.selfcheck.unrestrictedCanary must be a name outside
|
||||
services.hyperhive.deploy.hive-controller.squid.selfcheck.unrestrictedCanary must be a name outside
|
||||
allowedDomains and baseAllowedDomains, or the self-check cannot tell an unrestricted user from a
|
||||
restricted one.
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# The default of `services.hyperhive.squid.baseAllowedDomains`, and the names
|
||||
# The default of `services.hyperhive.deploy.hive-controller.squid.baseAllowedDomains`, and the names
|
||||
# swarm-controller refuses as allow-list entries because squid already allows
|
||||
# them to every authenticated user: the hosts Claude Code requires
|
||||
# (<https://code.claude.com/docs/en/network-config>).
|
||||
|
|
|
|||
|
|
@ -867,8 +867,8 @@ in
|
|||
# The names squid allows every authenticated agent by default. The
|
||||
# allow-list route refuses them as entries
|
||||
# (swarm-controller/src/egress_allow.rs). A hive that overrides
|
||||
# `squid.baseAllowedDomains` is not reflected here; its hive-c0re
|
||||
# skips the entries its own list covers.
|
||||
# `deploy.hive-controller.squid.baseAllowedDomains` is not reflected
|
||||
# here; its hive-c0re skips the entries its own list covers.
|
||||
SWARM_CONTROLLER_SQUID_BASE_DOMAINS = lib.concatStringsSep " " (
|
||||
import ./lib/squid-base-domains.nix
|
||||
);
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ let
|
|||
name = "the controller is handed the base list squid defaults to";
|
||||
ok =
|
||||
let
|
||||
base = baoControllerHere.services.hyperhive.squid.baseAllowedDomains;
|
||||
base = baoControllerHere.services.hyperhive.deploy.hive-controller.squid.baseAllowedDomains;
|
||||
in
|
||||
base != [ ]
|
||||
&&
|
||||
|
|
|
|||
|
|
@ -1621,6 +1621,73 @@ let
|
|||
&& (locs h.swarm.forge.domain)."/".proxyPass == "http://127.0.0.1:3000/"
|
||||
&& !allLocal.containers.hive-matrix.config.services.matrix-tuwunel.settings.global.allow_encryption;
|
||||
}
|
||||
{
|
||||
# Each old spelling lands on the new path, with its own rename warning;
|
||||
# the squid.conf it renders equals the one the new spelling renders, and
|
||||
# differs from the default's (the control that the values reach it).
|
||||
name = "services.hyperhive.squid.* settings still apply, with a rename warning each";
|
||||
ok =
|
||||
let
|
||||
old = hiveWithController {
|
||||
squid = {
|
||||
allowedDomains = [ "legacy.example" ];
|
||||
baseAllowedDomains = [ "base.example" ];
|
||||
usersFile = "/var/lib/legacy-squid/users";
|
||||
selfcheck.unrestrictedCanary = "legacy-u.example";
|
||||
selfcheck.canary = "legacy-c.example";
|
||||
};
|
||||
};
|
||||
new = hiveWithController {
|
||||
deploy.hive-controller.squid = {
|
||||
allowedDomains = [ "legacy.example" ];
|
||||
baseAllowedDomains = [ "base.example" ];
|
||||
usersFile = "/var/lib/legacy-squid/users";
|
||||
selfcheck.unrestrictedCanary = "legacy-u.example";
|
||||
selfcheck.canary = "legacy-c.example";
|
||||
};
|
||||
};
|
||||
sq = old.services.hyperhive.deploy.hive-controller.squid;
|
||||
warned =
|
||||
m: path:
|
||||
lib.any (
|
||||
w: lib.hasInfix "services.hyperhive.squid.${path}" w && lib.hasInfix "has been renamed" w
|
||||
) m.warnings;
|
||||
in
|
||||
sq.allowedDomains == [ "legacy.example" ]
|
||||
&& sq.baseAllowedDomains == [ "base.example" ]
|
||||
&& sq.usersFile == "/var/lib/legacy-squid/users"
|
||||
&& sq.allowedFile == "/var/lib/legacy-squid/allowed"
|
||||
&& sq.selfcheck.unrestrictedCanary == "legacy-u.example"
|
||||
&& sq.selfcheck.canary == "legacy-c.example"
|
||||
&& lib.all (warned old) [
|
||||
"allowedDomains"
|
||||
"baseAllowedDomains"
|
||||
"usersFile"
|
||||
"selfcheck.unrestrictedCanary"
|
||||
"selfcheck.canary"
|
||||
]
|
||||
&& !(warned bare "allowedDomains")
|
||||
&& old.systemd.services.hive-c0re.environment.HIVE_SQUID_USERS_FILE == "/var/lib/legacy-squid/users"
|
||||
&& old.services.squid.configText == new.services.squid.configText
|
||||
&& lib.hasInfix "legacy-c.example" old.services.squid.configText
|
||||
&& !(lib.hasInfix "legacy-c.example" bare.services.squid.configText);
|
||||
}
|
||||
{
|
||||
# The default render, pinned so moving the options changes none of it:
|
||||
# the listen address squid binds and hive-c0re hands out, and the users
|
||||
# file hive-c0re writes.
|
||||
name = "squid's default listen address and users file reach squid.conf and hive-c0re unchanged";
|
||||
ok =
|
||||
let
|
||||
e = bare.systemd.services.hive-c0re.environment;
|
||||
conf = bare.services.squid.configText;
|
||||
in
|
||||
e.HIVE_SQUID_PROXY == "${bare.services.hyperhive.network.bridgeIp}:3128"
|
||||
&& e.HIVE_SQUID_USERS_FILE == "/var/lib/hive-squid/users"
|
||||
&& lib.hasInfix "http_port ${e.HIVE_SQUID_PROXY}" conf
|
||||
&& lib.hasInfix "basic_ncsa_auth /var/lib/hive-squid/users" conf
|
||||
&& lib.hasInfix "acl allowed_dst dstdomain cache.nixos.org" conf;
|
||||
}
|
||||
{
|
||||
# A hive's rendered identity, pinned so moving the option changes none of
|
||||
# it: the coordinator's name and client, the dashboard name, the CN of the
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ let
|
|||
unrestricted = at "http_access allow unrestricted";
|
||||
authed = at "http_access deny !authed";
|
||||
# The generated per-agent `http_access allow` rules sit in this file.
|
||||
agentRules = at "include ${hiveOtel.services.hyperhive.squid.agentAllowFile}";
|
||||
agentRules = at "include ${hiveOtel.services.hyperhive.deploy.hive-controller.squid.agentAllowFile}";
|
||||
firstAllow = lib.min agentRules (lib.min base (lib.min unrestricted allow));
|
||||
in
|
||||
allow != null
|
||||
|
|
@ -550,9 +550,10 @@ let
|
|||
acl = lib.findFirst (l: lib.hasPrefix "acl allowed_dst dstdomain " l) "" (map lib.trim lines);
|
||||
baseAcl = lib.findFirst (l: lib.hasPrefix "acl base_dst dstdomain " l) "" (map lib.trim lines);
|
||||
in
|
||||
acl == "acl allowed_dst dstdomain ${hiveOtel.services.hyperhive.squid.selfcheck.canary}"
|
||||
acl
|
||||
== "acl allowed_dst dstdomain ${hiveOtel.services.hyperhive.deploy.hive-controller.squid.selfcheck.canary}"
|
||||
&& baseAcl == "acl base_dst dstdomain api.anthropic.com claude.ai platform.claude.com"
|
||||
&& hiveOtel.services.hyperhive.squid.selfcheck.canary == "cache.nixos.org"
|
||||
&& hiveOtel.services.hyperhive.deploy.hive-controller.squid.selfcheck.canary == "cache.nixos.org"
|
||||
&& !(builtins.elem hiveOtel.services.hyperhive.swarm.forge.domain (lib.splitString " " acl));
|
||||
}
|
||||
{
|
||||
|
|
@ -592,7 +593,7 @@ let
|
|||
conf = hiveOtel.services.squid.configText;
|
||||
lines = lib.splitString "\n" conf;
|
||||
at = l: lib.lists.findFirstIndex (x: x == l) null lines;
|
||||
list = hiveOtel.services.hyperhive.squid.unrestrictedFile;
|
||||
list = hiveOtel.services.hyperhive.deploy.hive-controller.squid.unrestrictedFile;
|
||||
in
|
||||
at "http_access allow unrestricted" != null
|
||||
&& at "http_access deny !allowed_dst" != null
|
||||
|
|
@ -609,15 +610,15 @@ let
|
|||
name = "squid is reloaded when its unrestricted-users list changes";
|
||||
ok =
|
||||
let
|
||||
list = hiveOtel.services.hyperhive.squid.unrestrictedFile;
|
||||
list = hiveOtel.services.hyperhive.deploy.hive-controller.squid.unrestrictedFile;
|
||||
rules = hiveOtel.systemd.tmpfiles.rules;
|
||||
path = hiveOtel.systemd.paths.squid-unrestricted-reload;
|
||||
svc = hiveOtel.systemd.services.squid-unrestricted-reload.serviceConfig;
|
||||
in
|
||||
path.pathConfig.PathChanged == [
|
||||
list
|
||||
hiveOtel.services.hyperhive.squid.allowedFile
|
||||
hiveOtel.services.hyperhive.squid.agentAllowFile
|
||||
hiveOtel.services.hyperhive.deploy.hive-controller.squid.allowedFile
|
||||
hiveOtel.services.hyperhive.deploy.hive-controller.squid.agentAllowFile
|
||||
]
|
||||
&& hiveOtel.systemd.services.squid-unrestricted-reload.startLimitIntervalSec == 0
|
||||
&& builtins.elem "paths.target" path.wantedBy
|
||||
|
|
@ -635,7 +636,7 @@ let
|
|||
name = "squid reads the swarm's allow-list from a file seeded with the hive's own names";
|
||||
ok =
|
||||
let
|
||||
sq = hiveOtelAllLocal.services.hyperhive.squid;
|
||||
sq = hiveOtelAllLocal.services.hyperhive.deploy.hive-controller.squid;
|
||||
lines = lib.splitString "\n" hiveOtelAllLocal.services.squid.configText;
|
||||
rules = hiveOtelAllLocal.systemd.tmpfiles.rules;
|
||||
env = hiveOtelAllLocal.systemd.services.hive-c0re.environment;
|
||||
|
|
@ -675,7 +676,7 @@ let
|
|||
let
|
||||
conf = hiveOtel.services.squid.configText;
|
||||
lines = lib.splitString "\n" conf;
|
||||
users = hiveOtel.services.hyperhive.squid.usersFile;
|
||||
users = hiveOtel.services.hyperhive.deploy.hive-controller.squid.usersFile;
|
||||
rules = hiveOtel.systemd.tmpfiles.rules;
|
||||
sc = hiveOtel.systemd.services.squid-selfcheck.serviceConfig;
|
||||
in
|
||||
|
|
@ -686,7 +687,9 @@ let
|
|||
&& users == "/var/lib/hive-squid/users"
|
||||
&& builtins.elem "d /var/lib/hive-squid 2750 hive-core squid - -" rules
|
||||
&& builtins.elem "f ${users} 0640 hive-core squid - -" rules
|
||||
&& sc.ImportCredential == hiveOtel.services.hyperhive.squid.selfcheck.credentialName
|
||||
&&
|
||||
sc.ImportCredential
|
||||
== hiveOtel.services.hyperhive.deploy.hive-controller.squid.selfcheck.credentialName
|
||||
&& hiveOtelAllLocal.systemd.services.hive-c0re.environment.HIVE_SQUID_USERS_FILE == users;
|
||||
}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -175,13 +175,13 @@ pkgs.testers.runNixOSTest {
|
|||
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";
|
||||
services.hyperhive.squid.selfcheck.canary = "cache.t.local";
|
||||
services.hyperhive.squid.baseAllowedDomains = [
|
||||
services.hyperhive.deploy.hive-controller.squid.selfcheck.unrestrictedCanary = "other.t.local";
|
||||
services.hyperhive.deploy.hive-controller.squid.selfcheck.canary = "cache.t.local";
|
||||
services.hyperhive.deploy.hive-controller.squid.baseAllowedDomains = [
|
||||
"api.t.local"
|
||||
"basebridge.t.local"
|
||||
];
|
||||
services.hyperhive.squid.allowedDomains = [
|
||||
services.hyperhive.deploy.hive-controller.squid.allowedDomains = [
|
||||
"forge.t.local"
|
||||
"loop.t.local"
|
||||
"bridge.t.local"
|
||||
|
|
|
|||
Loading…
Reference in a new issue