fix(svc-net-tor): bound the two ports that answered everyone

An audit of what the host actually filters found a single input chain in the whole ruleset, covering three tor ports. ss showed two more binds with nothing in front of them: the SOCKS port on 0.0.0.0, and dnsmasq on the bridge address.

Each gets the layer it belongs in. SOCKS gets a packet filter rule against the same CIDRs SocksPolicy already accepts, so no client loses access while the filter closes if tor's own config ever stops enforcing. Reusing the narrower guard set was deliberately avoided: it excludes the LAN blocks SocksPolicy admits, so it would have changed the policy rather than hardened it.

dnsmasq gets local-service, which answers only directly connected networks. A packet filter rule there would have sat on the container DNS path, which daemon.json points at that same bridge address, in exchange for closing an exposure that needs an extra route onto the bridge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-09-15 20:53:14 +02:00
1 parent 508bc5adac
commit c89ca42c46
4 files changed
+16

No files matched your search

@@ -4,6 +4,7 @@ listen-address=127.0.0.1
listen-address={{ TOR_CONTAINER_DNS_HOST }}
{% endif %}
bind-interfaces
local-service
no-resolv
{% endif %}
no-hosts
@@ -8,10 +8,17 @@ table inet {{ FIREWALL_TABLE }} {
elements = { {{ TOR_EGRESS_CLIENT_CIDRS | join(', ') }} }
}
set socks_admitted {
type ipv4_addr
flags interval
elements = { {{ NETWORK_INTERNAL_CIDRS | join(', ') }} }
}
chain guard {
{% for entry in TOR_EGRESS_GUARDED_PORTS %}
ip saddr != @admitted {{ entry.protocol }} dport {{ entry.port }} drop
{% endfor %}
ip saddr != @socks_admitted tcp dport {{ TOR_SOCKS_PORT }} drop
}
chain input {
+1
View File
@@ -11,6 +11,7 @@ TOR_USE_ENTRY_GUARDS: "{{ lookup('config', application_id, 'services.tor.use_ent
TOR_FLAVOR: "{{ lookup('config', application_id, 'services.tor.flavor') }}"
TOR_TRANS_PORT: "{{ lookup('config', application_id, 'services.tor.ports.local.trans') }}"
TOR_DNS_PORT: "{{ lookup('config', application_id, 'services.tor.ports.local.dns') }}"
TOR_SOCKS_PORT: "{{ lookup('config', application_id, 'services.tor.ports.local.socks') }}"
TOR_DNSMASQ_CONF: "/etc/dnsmasq.d/tor-onion.conf"
TOR_EGRESS_GUARDED_PORTS:
@@ -48,7 +48,14 @@ COMMON = {
],
"TOR_EGRESS_VIRTUAL_NET_IPV4": "10.192.0.0/10",
"TOR_TRANS_PORT": "9040",
"TOR_SOCKS_PORT": "9050",
"TOR_CONTAINER_DNS_HOST": "172.17.0.1",
"NETWORK_INTERNAL_CIDRS": [
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
],
}
MODES = ("compose", "swarm")