Files
esh-pfi-infrastructure/stacks/adguard-ana/conf/AdGuardHome.seed.yaml
T
vh b8003c73ae feat(dns): fleet .internal naming — git-sourced, agent-managed, three resolvers
Names for fleet hosts so addresses stop needing to be memorised. Built because
IPv6 makes that hopeless — and, more to the point, because v6 addresses are
derived rather than assigned, so they cannot reliably be written down once and
trusted either.

  dns/internal.yaml     source of truth: 38 hosts + 4 service aliases
  scripts/dns-sync.py   reconciles AdGuard resolvers against it
  stacks/adguard-ana/   the colo's resolver, which did not exist

Naming is <host>.<site>.internal with sites ana/esh/nh3 (operator's call).
.internal is ICANN-reserved for this; .local is reserved for mDNS, which is
why searxng.pfi.local was a collision that merely happened to work.

Same posture as deploy-stack.sh: file is intent, resolvers are derived state,
you see a diff before anything changes. Every name is published to every
resolver, so the site label says where a host IS, not who knows about it.

Two properties that matter:
- Authority is scoped to the ZONE, not the resolver. ESH carries hand-made
  esteban.net rewrites predating this; they are read, ignored and preserved.
  Resolver-wide authority would have silently deleted them.
- Within .internal it IS authoritative, so UI-added names get removed. That is
  the point — one place to look.

Colo gap closed: ana-docker had no resolver at all (hosts went straight to
1.1.1.1). Its AdGuard runs API on 8053 because 8080/3000 were taken, so the
port is carried per-site in the yaml rather than assumed by the script. It
ships with no blocklists — a false positive on a server network breaks
service-to-service calls for no upside.

Auth is a dedicated infra-ops AdGuard user, not the operator's account,
password vaulted at nh3-dev/adguard-infra-ops-password. Pre-change configs
backed up on each resolver. Both resolvers stayed answering across the restart.

searxng.pfi.local -> searxng.ana.internal, with the old Host() kept alongside
so nothing breaks mid-migration. matrix.pfi.local deliberately NOT migrated: a
Matrix server_name is baked into every user id, room id and signing key, so
renaming it rebuilds the homeserver's identity rather than changing a DNS name.

The v6 column is empty and correct — no fleet host has a global v6 address
yet. The file documents why addresses must be pinned statically before they go
in, since a record that silently stops matching is worse than no record.
2026-08-19 01:10:55 -07:00

43 lines
1.3 KiB
YAML

# FIRST-BOOT SEED ONLY — AdGuard rewrites this file at runtime.
#
# It exists so the container comes up already configured instead of sitting in
# the setup wizard waiting for a human. After first boot the live copy is in
# the adguard-ana_adguard_conf volume and diverges from this file; treat this
# as the bootstrap, not as ongoing config. Notably the `rewrites:` list stays
# EMPTY here — scripts/dns-sync.py owns it from dns/internal.yaml.
#
# ⚠️ NO BLOCKLISTS, DELIBERATELY. The other two AdGuards filter ads for human
# browsing; this one resolves for a rack of servers. A blocklist false-positive
# here breaks service-to-service calls at 3am for no upside, so filtering is
# off and this instance is a plain recursive resolver plus the .internal zone.
http:
address: 0.0.0.0:80
users:
- name: infra-ops
password: $2a$05$v7d1ASBg9qprZNx3Y50YqempdiL.Lo8.jMYxyVr6MroaGF0w5tQje
auth_attempts: 5
block_auth_min: 15
language: en
theme: auto
dns:
bind_hosts:
- 0.0.0.0
port: 53
upstream_dns:
- 1.1.1.1
- 1.0.0.1
bootstrap_dns:
- 1.1.1.1
- 8.8.8.8
protection_enabled: true
filtering_enabled: false
safebrowsing_enabled: false
parental_enabled: false
rewrites: []
filters: []
whitelist_filters: []
user_rules: []
log:
verbose: false
schema_version: 29