Use output rules based on user Tor is executed by. Since relays/bridges can use any port numbers for binding, it is insufficient to open few specific ports for output for being able to connect to any relay/bridge.
For example on Debian like OSes the user of the main Tor instance is debian-tor
.
The following rules will allow outgoing traffic to any IP address/port combo, but only for this main instance of Tor (one for IPv4 and one for IPv6):
nft insert rule ip filter OUTPUT ip protocol tcp skuid debian-tor counter accept
nft insert rule ip6 filter OUTPUT ip protocol tcp skuid debian-tor counter accept
Then use this debian-tor
user only to execute Tor and for nothing else.
Each Tor instance should be run by its own separate user.
If you have more than one Tor instance running, the following command will add such rules for each running instance:
sh -c 'for uid in $(ps -o uid -q $(pidof tor -d,) --no-headers); do nft insert rule ip filter OUTPUT ip protocol tcp skuid $uid counter accept; nft insert rule ip6 filter OUTPUT ip protocol tcp skuid $uid counter accept; done'
You can verify that the rule(s) work(s) by observing the output of:
nft list chain ip filter OUTPUT
nft list chain ip6 filter OUTPUT
Where you should see something like
... packets 1297 bytes 1680166 ...
which are the number of packets and bytes caught by your rule. So the rule works.
For input the rules can be as follows:
nft insert rule ip filter INPUT tcp dport <PORT> skuid debian-tor counter accept
nft insert rule ip6 filter INPUT tcp dport <PORT> skuid debian-tor counter accept
or for multiple ports:
nft insert rule ip filter INPUT tcp dport { <PORT>,<PORT>,<PORT>} skuid debian-tor counter accept
nft insert rule ip6 filter INPUT tcp dport { <PORT>,<PORT>,<PORT>} skuid debian-tor counter accept
Replace <PORT>
with the port number(s) you specified in your torrc
.
To verify the rules are working, observe the output of:
nft list chain ip filter INPUT
nft list chain ip6 filter INPUT