#!/bin/bash # Certificate sync, stunnel and unit installation for the DoT resolver. # # Sourced by setup_dot_resolver.sh; split out to keep it under the 250-line cap. # Sourced rather than run, so it inherits the caller's strict mode and # the helper functions and variables defined above the source line. install_cert_sync() { # Caddy renews on its own schedule; stunnel reads files from disk and must # be told. A timer copies the current cert out of the container and # reloads stunnel only when the content actually changed. sudo install -d -m 700 "$CERT_DIR" sudo tee "$SYNC_SCRIPT" >/dev/null <"\$tmp/combined.pem" if ! cmp -s "\$tmp/combined.pem" "$CERT_DIR/combined.pem"; then install -m 600 "\$tmp/combined.pem" "$CERT_DIR/combined.pem" systemctl reload-or-restart stunnel-dot.service echo "certificate updated for $DOMAIN" fi EOF sudo chmod 755 "$SYNC_SCRIPT" } install_stunnel_conf() { sudo tee "$STUNNEL_CONF" >/dev/null </dev/null </dev/null </dev/null </dev/null 2>&1 && sudo nft list table inet filter >/dev/null 2>&1; then if sudo nft list table inet filter | grep -q "iifname \"wg0\" tcp dport $DOT_PORT"; then log "nftables already permits $DOT_PORT on wg0" else sudo nft add rule inet filter input iifname wg0 tcp dport "$DOT_PORT" accept || log "WARNING: could not add nftables rule for $DOT_PORT on wg0" log "Permitted TCP $DOT_PORT on wg0 only" fi fi log "No router port-forward needed - $BIND_ADDR is WireGuard-only." return fi if command -v nft >/dev/null 2>&1 && sudo nft list table inet filter >/dev/null 2>&1; then if ! sudo nft list table inet filter | grep -q "dport $DOT_PORT"; then # Rate limited, because a publicly bound DoT endpoint is an open # resolver: TCP-only so not usable for classic UDP amplification, # but still someone else's free DNS and a standing attack surface. # A phone issues a handful of lookups a second at worst; 25/second # with a burst of 50 is far above real use and far below abuse. sudo nft add rule inet filter input tcp dport "$DOT_PORT" \ limit rate 25/second burst 50 packets accept || log "WARNING: could not add rate-limited nftables rule for $DOT_PORT" log "Permitted TCP $DOT_PORT (rate limited 25/s, burst 50)" fi fi log "Ensure your router forwards TCP $DOT_PORT to this host." } run_check() { local ok=0 systemctl is-active --quiet stunnel-dot.service || { log "FAIL stunnel-dot.service not active" ok=1 } ss -lnt 2>/dev/null | grep -qE ":$DOT_PORT\b" || { log "FAIL nothing listening on $DOT_PORT" ok=1 } # $CERT_DIR is mode 700 and owned by root because it holds a private key, # so an unprivileged [[ -s ]] reports "missing" for a file that is # present. Test as root, or --check reports a false failure on a healthy # install. sudo test -s "$CERT_DIR/combined.pem" || { log "FAIL certificate missing at $CERT_DIR/combined.pem" ok=1 } if command -v kdig >/dev/null 2>&1; then kdig +tls "@$BIND_ADDR" -p "$DOT_PORT" +short example.com >/dev/null 2>&1 || { log "FAIL DoT query did not answer" ok=1 } fi [[ $ok -eq 0 ]] && log "OK - DoT resolver healthy on $DOMAIN:$DOT_PORT" return "$ok" }