Files
computer-playbook/roles/svc-net-tor/templates/resolv.conf.j2
kevinveenbirkenbachandClaude Opus 5 4f44eff128 fix(svc-net-tor): bind dnsmasq statically so a failed re-enumeration cannot drop every listener
Run 34421944328 Nextcloud#13 lost the dashboard's Matomo tracker. The
dns53 sampler on mgr-01 shows no :53 socket from 01:39:52 to 01:48:44
while the dnsmasq PID stayed the same, six seconds after a short-lived
container's veth on docker0. In that window the manager could not
resolve its own onion vhosts, so sys-front-inj-matomo's site checks for
web-svc-logout (01:45:35) and web-app-dashboard (01:47:18) never reached
OpenResty, the injection was disabled as "API not ready", and the
Playwright check found no matomo.js. The other four swarm managers of
the run that carried the same listener kept it throughout.

Under bind-dynamic, dnsmasq 2.91 re-enumerates interfaces on every
netlink address event and then garbage-collects every listener it did
not re-find (network.c enumerate_interfaces), also when the enumeration
failed; release_listener has no other caller. 5aa7506e36 reproduced this
on the lab nodes and 11c98825aa already moved the bridge-less case to a
static bind. The bridge case now binds statically as well.

This supersedes two earlier rationales, both measured on the local stack:
- bb0e098a9f kept bind-dynamic because dnsmasq starts at boot before
  docker0 exists. A unit drop-in (files/dnsmasq-restart.conf,
  Restart=on-failure, RestartSec=5) now retries the start: with a missing
  listen-address the start exits 2, and dnsmasq came up 4s after the
  address appeared.
- 4739b2de99 called bind-dynamic load-bearing because docker0 can
  disappear across a docker restart. Only a start without the address is
  fatal; a bound socket survived the address being removed and re-added
  and the interface being recreated with a new ifindex, answering on the
  same socket inode each time.

The docker0 fact is re-gathered before torrc and the dnsmasq drop-in are
rendered. The play-start fact predates default-address-pools moving the
bridge (285ba1e76c), and a static bind on a stale address would make
dnsmasq exit. container_dns reads only the pool, so daemon.json is not
affected by the refresh.

The host resolv.conf moves from an inline copy content to
templates/resolv.conf.j2; both it and the drop-in render byte-identical
to what the local stack already runs.

A veth churn of 13.8k cycles did not reproduce the loss locally, under
either bind mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 12:01:51 +02:00

4 lines
76 B
Django/Jinja

nameserver 127.0.0.1
nameserver {{ networks.internet.dns }}
options ndots:1