diff --git a/nix/host-modules/swarm-container.nix b/nix/host-modules/swarm-container.nix new file mode 100644 index 00000000..6a247e00 --- /dev/null +++ b/nix/host-modules/swarm-container.nix @@ -0,0 +1,52 @@ +# What every hyperhive service nixos-container sets for itself, imported +# inside the container's own `config`. +# +# The host module that declares `containers.` sets the options below. +# They restate host-side facts the container cannot read on its own: its +# evaluation is nested inside `containers.`, and reading the host side +# back from in here recurses. +{ config, lib, ... }: +let + cfg = config.services.hyperhive.swarmContainer; +in +{ + options.services.hyperhive.swarmContainer = { + privateNetwork = lib.mkOption { + type = lib.types.bool; + description = '' + Must equal the host-side `containers..privateNetwork`. When + `false` the container shares the host netns, so its own + firewall.service would rewrite the HOST ruleset at every boot; the + container's firewall is turned off and the host firewall owns all + filtering. + ''; + }; + + writesOwnResolvConf = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Something in this container writes `/etc/resolv.conf` itself (the + `swarm-container-resolver.nix` unit, or a static file), so + resolvconf is forced off. Left on, host-tracking would regenerate the + file empty, since the host's copy doesn't cross the boundary after + start. + ''; + }; + }; + + config = lib.mkMerge [ + { + # A container that sets its own keeps it. Changing this value changes + # it for every container that doesn't, and that is a state migration + # for each of them. + system.stateVersion = lib.mkDefault "26.05"; + } + (lib.mkIf (!cfg.privateNetwork) { + networking.firewall.enable = false; + }) + (lib.mkIf cfg.writesOwnResolvConf { + networking.resolvconf.enable = lib.mkForce false; + }) + ]; +} diff --git a/nix/host-modules/swarm-victoriametrics.nix b/nix/host-modules/swarm-victoriametrics.nix index 14ce09ab..2f01c7b5 100644 --- a/nix/host-modules/swarm-victoriametrics.nix +++ b/nix/host-modules/swarm-victoriametrics.nix @@ -27,6 +27,10 @@ let # the required-domain assertion in hive-network.nix should be what an # operator sees, not a coercion error from here. domainBase = if swarmDomain == null then "invalid" else swarmDomain; + + # Shared host netns, like every sibling swarm container: the gateway + # reaches this at 127.0.0.1:. + privateNetwork = false; in { # What stays here is what the store IS from any hive's point of view: the @@ -190,31 +194,20 @@ in # Journal files on the host, not inside the container: nixpkgs hardcodes # --link-journal=try-guest, and EXTRA_NSPAWN_FLAGS expands after it. extraFlags = [ "--link-journal=host" ]; - # Shared host netns, like every sibling swarm container: the gateway - # reaches this at 127.0.0.1:. - privateNetwork = false; + inherit privateNetwork; config = { ... }: { imports = [ + ./swarm-container.nix (import ./swarm-container-resolver.nix { inherit (networkCfg) bridgeIp; dnsConsumers = [ "victoriametrics.service" ]; }) ]; - system.stateVersion = "26.05"; - - # This container shares the host netns, so its own firewall.service - # would rewrite the HOST ruleset at every boot. The host firewall - # owns all filtering. - networking.firewall.enable = false; - # resolvconf stays off because the resolver unit imported above - # owns /etc/resolv.conf. Leaving it on would let host-tracking - # regenerate the file empty, since the host's copy doesn't cross - # the boundary after start. - networking.resolvconf.enable = lib.mkForce false; + services.hyperhive.swarmContainer = { inherit privateNetwork; }; services.victoriametrics = { enable = true; diff --git a/nix/module-eval/swarm-services-switch.nix b/nix/module-eval/swarm-services-switch.nix index af22eaca..41defadb 100644 --- a/nix/module-eval/swarm-services-switch.nix +++ b/nix/module-eval/swarm-services-switch.nix @@ -185,6 +185,21 @@ let && !(swarmServicesOnly.systemd.sockets ? hive-priv) && !(s ? swarm-bao-queue-agent); } + { + # Both values come from ../host-modules/swarm-container.nix. Read + # through the metrics store: nothing else in this suite evaluates that + # container's config. + name = "a service container on the host netns runs no firewall or resolvconf of its own"; + ok = + let + c = swarmServicesOnly.containers.swarm-victoriametrics.config; + in + !c.networking.firewall.enable && !c.networking.resolvconf.enable; + } + { + name = "a service container that sets no stateVersion of its own is on 26.05"; + ok = swarmServicesOnly.containers.swarm-victoriametrics.config.system.stateVersion == "26.05"; + } { # An operator's explicit `false` beats every `mkDefault` assertion, # which is what keeps "asserted by whoever needs it" from being a