fix(#3363): swarm containers write their own resolver file

A swarm service container shares the host netns and force-disables
resolvconf, so it inherits the /etc/resolv.conf nixos-containers copies in
at start (cp --remove-destination, host-side preStart, once per start) and
nothing ever refreshes it. That makes the container's resolver a snapshot of
the host's file at its boot instant.

When that snapshot is wrong the container can never recover, and the symptom
appears arbitrarily far from the cause: swarm-nats-auth cannot resolve
authelia's name, so it denies every auth-callout request and the queue
refuses every client with an authorization violation.

Give each of the four swarm containers a oneshot that writes the resolver
file itself, from the bridge IP, ordered before that container's first DNS
consumer. The shape is the one every agent container already uses.

networking.nameservers cannot do this: resolvconf is its only consumer and
these containers disable it, so setting it renders no file while still
evaluating cleanly. A static environment.etc entry cannot either -- it would
have to survive etc activation landing on the regular file the host already
copied there, which no eval can show.
This commit is contained in:
atlas 2026-08-17 14:49:51 +02:00 committed by mara
commit 5539819330
5 changed files with 113 additions and 12 deletions

View file

@ -36,6 +36,7 @@
}:
let
cfg = config.services.hyperhive.swarm.authelia;
networkCfg = config.services.hyperhive.network;
hyperhiveCfg = config.services.hyperhive;
gatewayCfg = hyperhiveCfg.gateway;
hyperhiveDomain = hyperhiveCfg.domain;
@ -740,6 +741,13 @@ in
config =
{ ... }:
{
imports = [
(import ./swarm-container-resolver.nix {
inherit (networkCfg) bridgeIp;
dnsConsumers = [ "authelia-${instance}.service" ];
})
];
system.stateVersion = "26.05";
# The authelia binary itself, so an operator who gets a shell
@ -753,9 +761,10 @@ in
# firewall.service would rewrite the HOST ruleset at every
# boot. The host firewall owns all filtering.
networking.firewall.enable = false;
# Keep the host-copied /etc/resolv.conf intact — resolvconf's
# host-tracking would regenerate it to an empty file, since
# the host's copy doesn't cross the boundary after start.
# 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;
# authelia's own secrets, generated in-container on first