mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
fix(diagnostics): let the in-zone probes read the drop-in that holds the zone
d14354e892 added both probes reading only /etc/dnsmasq.conf, but svc-net-tor writes its onion zone to /etc/dnsmasq.d/tor-onion.conf and leaves the main file with nothing but a conf-dir pointer. Every Tor host of run 33698844864 therefore captured resolve-inzone.txt as '[no output, exit 2]' and all 471 sampler lines as inzone=unconfigured: during the resolver outage that ended Gitea#0, nobody ever asked dnsmasq for an onion name, and two investigations went without the one measurement that would have settled them.
Both probes now follow the conf-dir pointer, main file first so a lab node with its zone in the main config answers as before. Each is exercised by a test that runs it against real files rather than comparing its text, which the rescue constant had no test for at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
11c98825aa
commit
56b8f1716b
4 files changed
+72
-2
No files matched your search
@@ -12,7 +12,10 @@ sample_once() {
|
||||
}
|
||||
|
||||
zone_probe_name() {
|
||||
zone="$(sed -n 's|^[[:space:]]*address=/\([^/]*\)/.*|\1|p' "${1}" 2>/dev/null | head -n1)"
|
||||
dirs="$(sed -n 's|^[[:space:]]*conf-dir=\([^,[:space:]]*\).*|\1|p' "${1}" 2>/dev/null)"
|
||||
# shellcheck disable=SC2086 # a dnsmasq conf-dir path carries no whitespace
|
||||
zone="$(grep -rhs '^[[:space:]]*address=/' "${1}" ${dirs} |
|
||||
sed -n 's|^[[:space:]]*address=/\([^/]*\)/.*|\1|p' | head -n1)"
|
||||
[ -n "${zone}" ] || return 0
|
||||
printf 'rescue-probe.%s' "${zone}"
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ from __future__ import annotations
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from utils.cache.files import PROJECT_ROOT
|
||||
|
||||
@@ -113,6 +114,33 @@ class ZoneProbeNameTests(unittest.TestCase):
|
||||
def test_a_config_without_a_zone_yields_nothing(self):
|
||||
self.assertEqual(self._zone("no-resolv\nserver=192.0.2.1\n"), "")
|
||||
|
||||
def test_a_zone_declared_in_a_drop_in_is_found(self):
|
||||
"""svc-net-tor writes the onion zone to /etc/dnsmasq.d/tor-onion.conf
|
||||
and leaves only a conf-dir pointer in the main file, so a probe that
|
||||
reads the main file alone reports no zone on every Tor host."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
drop_in_dir = Path(tmp) / "dnsmasq.d"
|
||||
drop_in_dir.mkdir()
|
||||
(drop_in_dir / "tor-onion.conf").write_text(
|
||||
"address=/abc123.onion/127.0.0.1\n", encoding="utf-8"
|
||||
)
|
||||
main = Path(tmp) / "dnsmasq.conf"
|
||||
main.write_text(f"conf-dir={drop_in_dir},*.conf\n", encoding="utf-8")
|
||||
proc = subprocess.run(
|
||||
[
|
||||
"sh",
|
||||
"-c",
|
||||
'DNS53_SAMPLER_LIB=1 . "$1" && zone_probe_name "$2"',
|
||||
"sh",
|
||||
str(SAMPLER),
|
||||
str(main),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
self.assertEqual(proc.stdout, "rescue-probe.abc123.onion")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -44,6 +44,42 @@ def _cp(cmd, rc=0, stdout=b"", stderr=b""):
|
||||
return subprocess.CompletedProcess(cmd, rc, stdout, stderr)
|
||||
|
||||
|
||||
class InZoneProbeTests(unittest.TestCase):
|
||||
"""The probe is a shell one-liner, so run it: svc-net-tor keeps the onion
|
||||
zone in a drop-in and leaves only a conf-dir pointer in the main file."""
|
||||
|
||||
def _zone(self, main: Path) -> str:
|
||||
probe = _load()._INZONE_PROBE.replace("/etc/dnsmasq.conf", str(main))
|
||||
inner = probe.split("rescue-probe.$(", 1)[1].rsplit(")", 1)[0]
|
||||
proc = subprocess.run(
|
||||
["sh", "-c", inner], capture_output=True, text=True, check=False
|
||||
)
|
||||
return proc.stdout.strip()
|
||||
|
||||
def test_a_zone_in_a_drop_in_is_found(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
drop_ins = Path(tmp) / "dnsmasq.d"
|
||||
drop_ins.mkdir()
|
||||
(drop_ins / "tor-onion.conf").write_text(
|
||||
"address=/abc123.onion/127.0.0.1\n", encoding="utf-8"
|
||||
)
|
||||
main = Path(tmp) / "dnsmasq.conf"
|
||||
main.write_text(f"conf-dir={drop_ins},*.conf\n", encoding="utf-8")
|
||||
self.assertEqual(self._zone(main), "abc123.onion")
|
||||
|
||||
def test_a_zone_in_the_main_file_still_wins(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
main = Path(tmp) / "dnsmasq.conf"
|
||||
main.write_text("address=/infinito.test/192.0.2.1\n", encoding="utf-8")
|
||||
self.assertEqual(self._zone(main), "infinito.test")
|
||||
|
||||
def test_a_config_without_a_zone_yields_nothing(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
main = Path(tmp) / "dnsmasq.conf"
|
||||
main.write_text("no-resolv\nserver=192.0.2.1\n", encoding="utf-8")
|
||||
self.assertEqual(self._zone(main), "")
|
||||
|
||||
|
||||
class HelperTests(unittest.TestCase):
|
||||
def test_sanitize_replaces_unsafe_chars(self):
|
||||
mod = _load()
|
||||
|
||||
@@ -42,7 +42,10 @@ _SELF_IN_CONTAINER = "/tmp/rescue-self.py" # noqa: S108 - fixed staging path in
|
||||
_LOCAL_DUMPS_ENV = "INFINITO_RESCUE_LOCAL_DUMPS_DIR"
|
||||
_APP_LOG_TAIL = "find /var/log /var/www /opt /srv -xdev -type f -name '*.log' -size -20971520c -mmin -360 -exec sh -c 'echo \"===== $1\"; tail -n 400 \"$1\"' _ {} ';'"
|
||||
_PROBE_HOSTS = ("deb.debian.org", "ghcr.io", "repo.packagist.org")
|
||||
_INZONE_PROBE = "getent hosts rescue-probe.$(awk -F/ '/^address=/{print $2;exit}' /etc/dnsmasq.conf)"
|
||||
_DNSMASQ_DIRS = (
|
||||
"$(sed -n 's|^ *conf-dir=\\([^,[:space:]]*\\).*|\\1|p' /etc/dnsmasq.conf)"
|
||||
)
|
||||
_INZONE_PROBE = f"getent hosts rescue-probe.$(grep -rhs '^ *address=/' /etc/dnsmasq.conf {_DNSMASQ_DIRS} | awk -F/ '{{print $2;exit}}')"
|
||||
|
||||
|
||||
def runtime_bin() -> str | None:
|
||||
|
||||
Reference in new issue
Block a user