port the DNS section, as a choice rather than a decision
The original hardcoded Cloudflare plus Google with no way to say otherwise, and
rewrote /etc/systemd/resolved.conf wholesale — discarding DNSSEC, DNSOverTLS,
Domains and Cache if anything had set them, without mentioning it had. The
settings are a drop-in now, and the resolver is picked from a list with a
"keep what is there" that is the default.
The part worth having explicit is which layer is being changed. With
systemd-resolved there are two:
per-link what DHCP handed each interface, and what Tailscale installs on its
own. These answer for that link's domains — the provider's internal
names, the tailnet — and are printed by this step precisely to show
they are NOT being touched. Overriding them is how private
networking quietly stops resolving.
global the resolver used when no link claims the query. This is the one
the step sets.
On this host that distinction is live: eth0 has Hetzner's resolvers and
tailscale0 has 100.100.100.100, which is what answers ts.pastilhas.dev. Both are
left alone.
The drop-in is named 99- because systemd reads drop-ins in lexical order and the
LAST value wins. That is the opposite of sshd, whose drop-in three files away in
this same directory has to sort FIRST. Both are stated where they are written,
because getting it backwards fails silently in either direction.
resolv.conf is checked for actually pointing at resolved's stub before the
drop-in is trusted to do anything — a machine where something replaced the
symlink with a static file bypasses resolved entirely.
Resolution is tested afterwards rather than assumed. A resolver that does not
answer makes every later step fail for a reason that has nothing to do with it,
so that failure is reported and recorded rather than swallowed.
The choice names what each provider actually is, including that a resolver sees
every name the machine looks up.
Verified both paths against this host: keep reports unchanged, Quad9 renders the
right addresses, and the per-link display shows Hetzner and Tailscale correctly.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,8 @@ source "$SCRIPT_DIR/lib/disk.sh"
|
||||
source "$SCRIPT_DIR/lib/files.sh"
|
||||
# shellcheck source=lib/ssh.sh
|
||||
source "$SCRIPT_DIR/lib/ssh.sh"
|
||||
# shellcheck source=lib/network.sh
|
||||
source "$SCRIPT_DIR/lib/network.sh"
|
||||
|
||||
# Trap errors with context. Installed here rather than in lib/base.sh, because
|
||||
# that file is definitions only and a trap is a side effect on whoever sources it.
|
||||
@@ -944,13 +946,106 @@ if ! skip; then
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# 16. DNS
|
||||
# =============================================================================
|
||||
|
||||
step "DNS"
|
||||
if ! skip; then
|
||||
CURRENT_DNS="$(dns_current_global)"
|
||||
|
||||
echo ""
|
||||
info "DNS — which resolver answers public lookups"
|
||||
echo " global resolver: ${CURRENT_DNS:-none set, using whatever DHCP gave}"
|
||||
|
||||
# Shown so it is clear what this step is NOT doing. Overriding these is how a
|
||||
# provider's internal names, or the tailnet, quietly stop resolving.
|
||||
if [[ -n "$(dns_per_link)" ]]; then
|
||||
echo ""
|
||||
echo " per-interface, left untouched:"
|
||||
dns_per_link | while IFS=: read -r link servers; do
|
||||
printf ' %-14s %s\n' "$link" "$(echo "$servers" | xargs)"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Whoever runs this resolver sees every name this machine looks up."
|
||||
echo " [1] keep what is there"
|
||||
echo " [2] Cloudflare 1.1.1.1 fast, logs for 24h"
|
||||
echo " [3] Quad9 9.9.9.9 blocks known-malicious domains"
|
||||
echo " [4] Google 8.8.8.8 fast, ubiquitous"
|
||||
echo " [5] type your own"
|
||||
echo ""
|
||||
|
||||
DNS_PRIMARY=""
|
||||
DNS_FALLBACK=""
|
||||
DNS_CHOSEN=""
|
||||
while [[ -z "$DNS_CHOSEN" ]]; do
|
||||
if ! read -rp " Which one? (1-5) [1]: " DNS_CHOICE; then
|
||||
echo ""
|
||||
fail "No answer."
|
||||
fi
|
||||
case "${DNS_CHOICE:-1}" in
|
||||
1) DNS_CHOSEN="keep" ;;
|
||||
2)
|
||||
DNS_CHOSEN="Cloudflare"
|
||||
DNS_PRIMARY="1.1.1.1 2606:4700:4700::1111"
|
||||
DNS_FALLBACK="1.0.0.1 2606:4700:4700::1001"
|
||||
;;
|
||||
3)
|
||||
DNS_CHOSEN="Quad9"
|
||||
DNS_PRIMARY="9.9.9.9 2620:fe::fe"
|
||||
DNS_FALLBACK="149.112.112.112 2620:fe::9"
|
||||
;;
|
||||
4)
|
||||
DNS_CHOSEN="Google"
|
||||
DNS_PRIMARY="8.8.8.8 2001:4860:4860::8888"
|
||||
DNS_FALLBACK="8.8.4.4 2001:4860:4860::8844"
|
||||
;;
|
||||
5)
|
||||
read -rp " Primary resolver(s), space separated: " DNS_PRIMARY || fail "No answer."
|
||||
read -rp " Fallback resolver(s), or blank: " DNS_FALLBACK || fail "No answer."
|
||||
[[ -n "$DNS_PRIMARY" ]] && DNS_CHOSEN="custom" || warn "A primary resolver is needed."
|
||||
;;
|
||||
*) warn "Pick 1 to 5." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$DNS_CHOSEN" == "keep" ]]; then
|
||||
echo " keeping ${CURRENT_DNS:-the current configuration}"
|
||||
SUMMARY+=("DNS: unchanged")
|
||||
else
|
||||
echo ""
|
||||
echo " to set: ${DNS_PRIMARY}"
|
||||
[[ -n "$DNS_FALLBACK" ]] && echo " fallback: ${DNS_FALLBACK}"
|
||||
if confirm "Proceed?"; then
|
||||
dns_set_global "$DNS_PRIMARY" "$DNS_FALLBACK"
|
||||
if dns_works; then
|
||||
ok "${DNS_CHOSEN} in use, resolution verified"
|
||||
SUMMARY+=("DNS: ${DNS_CHOSEN} (${DNS_PRIMARY%% *})")
|
||||
else
|
||||
# Reported rather than swallowed. Every step after this fetches
|
||||
# something, and they would all fail for a reason that has nothing to do
|
||||
# with them.
|
||||
warn "DNS is set but a test lookup failed — check 'resolvectl status'"
|
||||
ERRORS+=("DNS: set to ${DNS_CHOSEN} but a test lookup failed")
|
||||
SUMMARY+=("DNS: ${DNS_CHOSEN}, but resolution did not verify")
|
||||
fi
|
||||
else
|
||||
warn "skipped by request"
|
||||
SUMMARY+=("DNS: SKIPPED by request")
|
||||
fi
|
||||
fi
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# NOT PORTED YET
|
||||
# =============================================================================
|
||||
#
|
||||
# Sections still to move across from scripts/setup-old/setup-ubuntu.sh, in order:
|
||||
#
|
||||
# dns · static ip · fail2ban · unattended-upgrades ·
|
||||
# static ip · fail2ban · unattended-upgrades ·
|
||||
# git config · docker · zsh + prompt (incl. .tmux.conf) · tailscale · neovim · js runtimes ·
|
||||
# dev tools · ufw · zshrc
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user