mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Whether a resolver answers on the docker bridge was inferred from the host's own resolv.conf: all entries loopback was read as "a listener of ours is there, and containers must reach it on the bridge". A systemd-resolved stub binds 127.0.0.53, NetworkManager's dnsmasq binds 127.0.0.1, and a node dnsmasq binds both those and the bridge. All three look identical from resolv.conf and only the third is reachable from a container. So ordinary hosts were handed an address with nothing behind it. The Instructions job died there: daemon.json carried "dns": ["10.208.0.1"] alone, and the openresty build could not resolve dl-cdn.alpinelinux.org while the daemon, resolving through the host, pulled the same base image fine. Two netns, two outcomes.e308e18923had already fixed this once, with a reachability gate whose message names the same URL.b09f2cbd82dropped that gate andce43263745put the resolv.conf inference in its place, so the guard was gone and only the missing docker0 fact still hid it.285ba1e76cremoved that accident and the failure surfaced. svc-net-tor and NETWORK_CONTAINER_BRIDGE_RESOLVER now say it instead, the latter false by default and true in the swarm lab, which provisions the dnsmasq its nodes resolve through. Extra-vars are global, so the lab's NFS and backup hosts carry it although no listener is installed there; the clearnet resolver behind the bridge keeps them working. The docker0 fact is gone from the address too. It is not merely late, it is wrong while it is late: facts are read before the daemon config is applied, so docker0 still holds docker's default and writing default-address-pools moves the bridge out from under it. A swarm node shipped "dns": ["172.17.0.1", ...] against a live 10.208.0.1, which is why its containers asked a dead address and fell through to the public resolver, where infinito.example does not exist. The pool answers before the daemon exists and answers the same afterwards, and it is read per slice rather than per supernet, which differ once a pool does not start on a slice boundary. A list that would carry only the bridge now gets NETWORK_PUBLIC_DNS_RESOLVERS behind it, so a declaration that turns out to be wrong costs a wasted first query rather than all name resolution. An empty declaration raises. Rendered from the repository's own variable files: the Instructions host yields [] and the template omits the key, which is whatde95a95c65emitted while that job was green; lab and tor nodes yield the live bridge ahead of the clearnet resolver even with a stale docker0 fact present; an ordinary host yields only its declared resolver. The suite asserted the two behaviours this replaces, including one case added with285ba1e76cdemanding the stale fact win. They are inverted, and a cross-product over nameserver shapes, tor membership, clearnet and the docker0 fact now asserts that no shape ends up holding the bridge alone - the old suite checked each shape by itself, which is how this passed through it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>