From 024067f3f84ee0a872bcff42f5361677ae2b8639 Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 29 Sep 2026 18:49:48 +0200 Subject: [PATCH] nix: share the service-container settings through one in-container module The ten hand-rolled `containers.` blocks each repeat the same in-container lines: `system.stateVersion`, a firewall turned off because the container shares the host netns, and resolvconf forced off because something in the container writes /etc/resolv.conf itself. `nix/host-modules/swarm-container.nix` now owns those lines. It is imported inside the container's own config and exposes `services.hyperhive.swarmContainer.{privateNetwork,writesOwnResolvConf}` for the host module to set. `stateVersion` is a `mkDefault`, so the two containers on another value can keep theirs. `--link-journal=host` stays per module, and so do the host-side attrs (autoStart, ephemeral, privateNetwork, bindMounts). swarm-victoriametrics is converted as the first user. Its container toplevel drvPath is unchanged. A module-eval case now forces that container's config, which nothing in the suite read before. Refs #3773 --- nix/host-modules/swarm-container.nix | 52 ++++++++++++++++++++++ nix/host-modules/swarm-victoriametrics.nix | 21 +++------ nix/module-eval/swarm-services-switch.nix | 15 +++++++ 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 nix/host-modules/swarm-container.nix 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