From f686d8811bbede87b1c7803ce93c56e95fb89b68 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Thu, 9 Jul 2026 07:37:14 +0200 Subject: [PATCH] =?UTF-8?q?feat(toolbox):=20move=20Tor=20egress=20DNSPort?= =?UTF-8?q?=205353=E2=86=929053=20(avahi=20conflict)=20+=20Unbound=20.onio?= =?UTF-8?q?n=20forward-zone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../secubox-toolbox/conf/48-secubox-onion.conf | 7 +++++++ packages/secubox-toolbox/conf/nft-toolbox-tor.nft | 2 +- .../secubox-toolbox/conf/torrc-toolbox-egress.conf | 2 +- .../sbin/secubox-toolbox-tor-reconcile | 12 +++++++++++- packages/secubox-toolbox/tests/test_onion_dns.py | 14 ++++++++++++++ 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 packages/secubox-toolbox/conf/48-secubox-onion.conf create mode 100644 packages/secubox-toolbox/tests/test_onion_dns.py diff --git a/packages/secubox-toolbox/conf/48-secubox-onion.conf b/packages/secubox-toolbox/conf/48-secubox-onion.conf new file mode 100644 index 00000000..e68645d4 --- /dev/null +++ b/packages/secubox-toolbox/conf/48-secubox-onion.conf @@ -0,0 +1,7 @@ +# SecuBox: resolve .onion via the toolbox Tor egress DNSPort (automap). +# Installed by secubox-toolbox-tor-reconcile only while egress is armed. +server: + do-not-query-localhost: no +forward-zone: + name: "onion." + forward-addr: 127.0.0.1@9053 diff --git a/packages/secubox-toolbox/conf/nft-toolbox-tor.nft b/packages/secubox-toolbox/conf/nft-toolbox-tor.nft index 03528371..384968fd 100644 --- a/packages/secubox-toolbox/conf/nft-toolbox-tor.nft +++ b/packages/secubox-toolbox/conf/nft-toolbox-tor.nft @@ -30,7 +30,7 @@ table inet toolbox_tor { oifname "lo" return # DNS always via Tor (no leak), even to local resolvers — before the # tor_exempt return so own-subnet resolvers don't bypass it. - ip daddr != 127.0.0.0/8 meta skuid "secubox-toolbox" meta l4proto { tcp, udp } th dport 53 redirect to :5353 + ip daddr != 127.0.0.0/8 meta skuid "secubox-toolbox" meta l4proto { tcp, udp } th dport 53 redirect to :9053 # Own services / LAN / loopback → DIRECT (never torified). ip daddr @tor_exempt return # Everything else from the worker uid → Tor TransPort. diff --git a/packages/secubox-toolbox/conf/torrc-toolbox-egress.conf b/packages/secubox-toolbox/conf/torrc-toolbox-egress.conf index 0b7e4faf..6e3e1656 100644 --- a/packages/secubox-toolbox/conf/torrc-toolbox-egress.conf +++ b/packages/secubox-toolbox/conf/torrc-toolbox-egress.conf @@ -8,7 +8,7 @@ SocksPort 127.0.0.1:9050 TransPort 127.0.0.1:9040 -DNSPort 127.0.0.1:5353 +DNSPort 127.0.0.1:9053 AutomapHostsOnResolve 1 VirtualAddrNetworkIPv4 10.192.0.0/10 diff --git a/packages/secubox-toolbox/sbin/secubox-toolbox-tor-reconcile b/packages/secubox-toolbox/sbin/secubox-toolbox-tor-reconcile index b7762201..b7b5362e 100755 --- a/packages/secubox-toolbox/sbin/secubox-toolbox-tor-reconcile +++ b/packages/secubox-toolbox/sbin/secubox-toolbox-tor-reconcile @@ -28,6 +28,8 @@ NFT_DROPIN=/etc/nftables.d/zz-secubox-toolbox-tor.nft TORRC_DROPIN_SRC=/usr/lib/secubox/toolbox/conf/torrc-toolbox-egress.conf TORRC_DROPIN=/etc/tor/torrc.d/10-secubox-toolbox-egress.conf TORRC_MAIN=/etc/tor/torrc +UNBOUND_ONION_SRC=/usr/lib/secubox/toolbox/conf/48-secubox-onion.conf +UNBOUND_ONION_DST=/etc/unbound/unbound.conf.d/48-secubox-onion.conf log() { logger -t "secubox-${MODULE}" -- "$*" 2>/dev/null || true; echo "[$MODULE] $*"; } @@ -39,7 +41,7 @@ want_from_flag() { } arm() { - log "arming kbin Tor egress (TransPort 9040 / DNSPort 5353)" + log "arming kbin Tor egress (TransPort 9040 / DNSPort 9053)" mkdir -p /etc/tor/torrc.d install -m 0644 "$TORRC_DROPIN_SRC" "$TORRC_DROPIN" # Debian's torrc does not %include torrc.d by default — add it idempotently. @@ -54,6 +56,13 @@ arm() { populate_exempt systemctl restart tor 2>/dev/null || systemctl start tor 2>/dev/null || \ log "WARN tor failed to (re)start — egress fail-closed until it does" + # Resolve .onion via the egress DNSPort (automap). Fail-safe: never abort + # arm() on a rejected drop-in — just remove it and keep going. + if [ -f "$UNBOUND_ONION_SRC" ] && [ -d /etc/unbound/unbound.conf.d ]; then + install -m 0644 "$UNBOUND_ONION_SRC" "$UNBOUND_ONION_DST" + unbound-checkconf >/dev/null 2>&1 && systemctl reload unbound 2>/dev/null || \ + { rm -f "$UNBOUND_ONION_DST"; log "WARN unbound .onion forward-zone rejected — removed"; } + fi log "ARMED" } @@ -92,6 +101,7 @@ disarm() { rm -f "$NFT_DROPIN" if table_present; then nft delete table inet toolbox_tor 2>/dev/null || true; fi rm -f "$TORRC_DROPIN" + if [ -f "$UNBOUND_ONION_DST" ]; then rm -f "$UNBOUND_ONION_DST"; systemctl reload unbound 2>/dev/null || true; fi # Stop tor to minimise attack surface when not in use (no hidden services # are owned by this drop-in; secubox-tor manages those separately). systemctl stop tor 2>/dev/null || true diff --git a/packages/secubox-toolbox/tests/test_onion_dns.py b/packages/secubox-toolbox/tests/test_onion_dns.py new file mode 100644 index 00000000..3ff3e7dc --- /dev/null +++ b/packages/secubox-toolbox/tests/test_onion_dns.py @@ -0,0 +1,14 @@ +from pathlib import Path +CONF = Path(__file__).resolve().parents[1] / "conf" +def test_dnsport_moved_off_avahi_5353(): + torrc = (CONF / "torrc-toolbox-egress.conf").read_text() + assert "DNSPort 127.0.0.1:9053" in torrc + assert "5353" not in torrc # avahi owns 5353 +def test_nft_redirect_targets_9053(): + nft = (CONF / "nft-toolbox-tor.nft").read_text() + assert "redirect to :9053" in nft + assert ":5353" not in nft +def test_unbound_onion_forward_zone_valid(): + conf = (CONF / "48-secubox-onion.conf").read_text() + assert 'name: "onion."' in conf + assert "forward-addr: 127.0.0.1@9053" in conf