dns-audit.sh 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #!/usr/bin/env bash
  2. # net-ops :: linux/dns-audit.sh
  3. # Deep DNS forensics for Linux. Use when probe.sh shows rung 4 (dig) PASS
  4. # but rung 5 (getent / resolvectl) FAIL.
  5. set -u
  6. # Shared terminal toolkit (skills/_lib/term.sh) — colorized, ASCII-aware section
  7. # headers. Dump/fixer output isn't a checklist, so it uses the bare-header style
  8. # (a deliberate exception per docs/TERMINAL-DESIGN.md), not the enclosing panel.
  9. __nlib="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../_lib" 2>/dev/null && pwd || true)"
  10. if [ -n "${__nlib:-}" ] && [ -f "$__nlib/term.sh" ]; then . "$__nlib/term.sh"; term_init
  11. else term_header() { printf '== %s ==\n' "${1:-}"; }; fi
  12. # shellcheck source=../_lib/redact.sh
  13. source "$(dirname "$0")/../_lib/redact.sh"
  14. parse_redact_flag "$@"
  15. maybe_redact_self "$@"
  16. term_header "/etc/nsswitch.conf (hosts line)"
  17. grep "^hosts:" /etc/nsswitch.conf 2>/dev/null || echo " (no hosts entry)"
  18. echo
  19. term_header "/etc/resolv.conf"
  20. if [[ -L /etc/resolv.conf ]]; then
  21. echo " Type: symlink -> $(readlink /etc/resolv.conf)"
  22. else
  23. echo " Type: regular file"
  24. fi
  25. echo " Modified: $(stat -c '%y' /etc/resolv.conf 2>/dev/null || stat -f '%Sm' /etc/resolv.conf 2>/dev/null)"
  26. echo " --- contents ---"
  27. cat /etc/resolv.conf 2>/dev/null | sed 's/^/ /'
  28. echo
  29. term_header "systemd-resolved"
  30. if systemctl is-active systemd-resolved >/dev/null 2>&1; then
  31. echo " Service: active"
  32. echo " --- resolvectl status ---"
  33. resolvectl status 2>/dev/null | sed 's/^/ /'
  34. else
  35. echo " Service: inactive or not installed"
  36. fi
  37. echo
  38. term_header "NetworkManager DNS config"
  39. if command -v nmcli >/dev/null 2>&1; then
  40. echo " --- nmcli dev show (DNS lines) ---"
  41. nmcli dev show 2>/dev/null | grep -E 'DEVICE|IP4.DNS|IP6.DNS|DOMAIN' | sed 's/^/ /'
  42. echo
  43. echo " --- NetworkManager dns mode ---"
  44. awk '/\[main\]/,/\[/{if(/^dns/) print}' /etc/NetworkManager/NetworkManager.conf 2>/dev/null | sed 's/^/ /' || true
  45. ls -la /etc/NetworkManager/conf.d/ 2>/dev/null | sed 's/^/ /' || true
  46. else
  47. echo " nmcli not installed"
  48. fi
  49. echo
  50. term_header "dnsmasq"
  51. if pgrep -x dnsmasq >/dev/null; then
  52. pid=$(pgrep -x dnsmasq | head -1)
  53. echo " Running, PID $pid"
  54. ps -o command -p "$pid" 2>/dev/null | sed 's/^/ /'
  55. else
  56. echo " not running"
  57. fi
  58. for d in /etc/dnsmasq.d /etc/NetworkManager/dnsmasq.d; do
  59. [[ -d "$d" ]] && { echo " $d contents:"; ls "$d" 2>/dev/null | sed 's/^/ /'; }
  60. done
  61. echo
  62. term_header "Local DNS listeners"
  63. ss -tulnp 2>/dev/null | awk 'NR==1 || $5 ~ /:53$/' | sed 's/^/ /'
  64. echo
  65. term_header "/etc/hosts (non-comment)"
  66. grep -vE '^\s*(#|$)' /etc/hosts 2>/dev/null | sed 's/^/ /' || echo " (no custom entries)"
  67. echo
  68. term_header "VPN / WireGuard interfaces"
  69. ip -br link 2>/dev/null | awk '/^(wg|tun|tap|nordlynx|proton|mullvad|nextdns)/' | sed 's/^/ /' || true
  70. if command -v wg >/dev/null 2>&1; then
  71. echo " --- wg show ---"
  72. wg show 2>/dev/null | sed 's/^/ /' | head -30 || true
  73. fi
  74. echo
  75. term_header "ATTRIBUTION HINTS"
  76. # Inspect nameservers visible across the stack for known patterns
  77. ns_list=$( {
  78. awk '/^nameserver/{print $2}' /etc/resolv.conf 2>/dev/null
  79. resolvectl status 2>/dev/null | awk '/Current DNS Server:|DNS Servers:/{for(i=4;i<=NF;i++)print $i}'
  80. nmcli -t -f IP4.DNS,IP6.DNS dev show 2>/dev/null | awk -F: '{print $2}'
  81. } | sort -u | grep -v '^$' )
  82. while read -r n; do
  83. [[ -z "$n" ]] && continue
  84. case "$n" in
  85. 10.2.0.*) echo " $n :: likely Proton VPN gateway" ;;
  86. 10.64.0.*) echo " $n :: likely Mullvad gateway" ;;
  87. 10.211.*|10.212.*) echo " $n :: likely Cisco AnyConnect" ;;
  88. 100.100.100.100) echo " $n :: Tailscale MagicDNS (expected)" ;;
  89. 127.0.0.53) echo " $n :: systemd-resolved stub (expected on most systems)" ;;
  90. 127.0.0.1|127.0.0.2) echo " $n :: local DNS proxy (dnsmasq, NextDNS, AdGuard, etc.)" ;;
  91. esac
  92. done <<< "$ns_list"