61 lines
2.9 KiB
Nix
61 lines
2.9 KiB
Nix
# The resolver file a swarm service container writes for itself.
|
|
#
|
|
# Every swarm service container shares the host netns (`privateNetwork =
|
|
# false`) and force-disables `resolvconf`, so that the `/etc/resolv.conf`
|
|
# `nixos-containers` copies in at start is not regenerated empty. That copy
|
|
# is a `cp --remove-destination` in the host-side preStart, run ONCE per
|
|
# container start — so the container's resolver is a snapshot of the host's
|
|
# file at its boot instant, and stays that snapshot for its whole life.
|
|
#
|
|
# A snapshot is not a resolver. Anything that makes the host's file wrong at
|
|
# that one instant — a resolvconf regeneration mid-deploy, a host that has
|
|
# not yet pointed itself at the bridge — leaves the container with a resolver
|
|
# it can never recover from, and the symptom surfaces arbitrarily far from
|
|
# the cause: a queue refusing every client because the auth-callout responder
|
|
# cannot look up its IdP.
|
|
#
|
|
# So the container writes the file itself, on every boot, from the one
|
|
# address that is correct on both sides of a netns boundary (the bridge IP —
|
|
# see `hive-gateway/default.nix`, which forces the host to the same value).
|
|
{
|
|
bridgeIp,
|
|
# Units in this container that resolve a name. The caller names them
|
|
# because this module cannot know them, and an unordered resolver write
|
|
# is a race that only shows up on a cold boot.
|
|
dnsConsumers ? [ ],
|
|
}:
|
|
{ lib, pkgs, ... }:
|
|
{
|
|
# ⚠️ `networking.nameservers` CANNOT replace this unit. `resolvconf` is its
|
|
# only consumer, and these containers disable it — so setting it renders no
|
|
# file and changes no behaviour, while still evaluating and deploying
|
|
# perfectly cleanly. It reads like a fix and is a no-op.
|
|
#
|
|
# ⚠️ Nor can a static `environment.etc."resolv.conf"`: that has to survive
|
|
# `etc` activation landing on top of the regular file the host already
|
|
# copied there, which is a runtime property no eval can demonstrate. This
|
|
# oneshot shape is the one every agent container already uses
|
|
# (`nix/agent-modules/network.nix`), so it has runtime evidence behind it.
|
|
systemd.services.swarm-bridge-dns = {
|
|
description = "point resolv.conf at the hive bridge resolver";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "local-fs.target" ];
|
|
before = [ "network-online.target" ] ++ dnsConsumers;
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
# Pin the journal identity; without it systemd derives one from the
|
|
# generated script's store path (an opaque `<hash>-…-start`).
|
|
SyslogIdentifier = "swarm-bridge-dns";
|
|
};
|
|
path = [ pkgs.coreutils ];
|
|
script = ''
|
|
set -eu
|
|
# `rm` first: this is a regular file the host copied in, not something
|
|
# to write through, and a leftover symlink would redirect the write.
|
|
rm -f /etc/resolv.conf
|
|
printf 'nameserver %s\n' ${lib.escapeShellArg bridgeIp} > /etc/resolv.conf
|
|
echo "swarm-bridge-dns: resolv.conf -> nameserver ${bridgeIp}"
|
|
'';
|
|
};
|
|
}
|