I want to run an open Tor router.

My exit policy will be similar to ReducedExitPolicy.

But I also want to make it hard for the tor network to abuse my resources.

Cases I want to prevent clients from doing via Tor:

  • Hammering one site with very many packets.
  • Aggresive netscans of whole IP blocks

Cases I do NOT want to prevent clients from doing via Tor:

  • uploading a few hudreds of image files to the cloud
  • seeding a torrent

My question is, can this be done at all, and how?

My first thought was some firewall (Linux/iptables or *BSD/ipfw/pf) - but this will probably be useless due to inherent properties of the Onion router.

Is there any ongoing torproject team development on this topic?

I also ask for general hints on securing Tor exit nodes.

Update (Sep 2012)

From helpful answers and some other research I think this can not be done.

The best you can do to stop people from abusing exit node to contribute in DDOS, is to detect very frequent packets directed to one IP.

The "very frequent" threshold depends on total node bandwidth... If it is wrong, there will be false positives, blocking legitimate traffic of realtime TCP apps and traffic sourced from very many clients to one destination.

Update (Dec 2014)

My predictions were obviously true - I had several network abuse complaints from my internet provider.

To avoid service shutdown I had to employ following set of iptables rules (ONEW is a chain for outgoing TCP SYN (aka NEW) packets :

I'm not sure it will suffice but here it is:

-A ONEW -o lo -j ACCEPT
-A ONEW -p udp --dport 53 -m limit --limit 2/sec --limit-burst 5 -j ACCEPT
-A ONEW -m hashlimit --hashlimit-upto 1/second --hashlimit-mode dstip --hashlimit-dstmask 24 --hashlimit-name ONEW -j ACCEPT
-A ONEW -m limit --limit 1/sec -j LOG --log-prefix "REJECTED: "
-A ONEW -j REJECT --reject-with icmp-admin-prohibited

5 Answers 5


Keep in mind that:

  • Tor clients switch virtual circuits every 10 minutes or so to my current understanding. This means the source IP is changing around that time frame. You are unlikely to prevent any behavior you deem malicious for longer than that duration.

  • Note that the fact that Tor only proxies TCP traffic and not any other protocol limits abuse possibilities quite a bit.

iptables can let you treat new outgoing TCP connections differently than existing ones. Anything that is ESTABLISHED,RELATED should be ACCEPTED or put through a "existing TCP connections" chain, and outgoing TCP that doesn't get caught by that could be rate limited. Any outgoing Tor traffic should be subject to this.

I believe between the above and using the "Reduced Exit Policy" would be about the best you can do.

Ideally, don't run anything else on your Tor box except:

  • You'll probably at least have SSH up, put it on a different port than 22.
  • You'll probably want to run a simple webserver to display this page. A chroot'ed mini-httpd instance should do. Don't use inetd.

Don't run Tor on a box that is being used for anything else. Make sure you have read the "Exit Relays" section of the Tor Legal FAQ and fully understand its implications. Also read and do all of this.


It will be harder than usual to prevent these attacks since the source IP is not constant. However, to my knowledge the routes in tor are only changed every few minutes or so.

So you could still deploy some of the standard limitation/filtering rules but with a higher threshold, since you must assume there is a whole network behind your source IPs.

You can filter:

  • bad or typical fingerprinting/scanning packets (bad TCP/IP flags, XMAS, most ICMP types etc)
  • INVALID packets that don't fit to ongoing or new connections (-m state)
  • NEW connections starting at some rather high threshold

However, be aware that such things are typically done on inbound traffic. You don't know what kind of protocols your "customers" will run and you might restrict them in ways that can be annoying/unclear.

Also, for rate-limiting NEW (or stateless) packets you may want to consider some more involved scheme where the rejected(never DROP unless its obviously an attack!) packets are randomized. This way, a regular user can just try to hit reload and get lucky, even though the overall rate is currently at the limit, while a concurrent port scanner will not be able to circumvent your rate limit.

Also ask on the Tor mailing lists, you're probably not the first to have such thoughts: https://lists.torproject.org/cgi-bin/mailman/listinfo


First off I wouldn't suggest iptables to solve all of this, truely an ideal exit Tor node would load balace traffic though a few VPN tunnels to keep ISP's eyes off the packets and true destination and/or utilize caching proxy to keep outbound repeat requests to popular static content to a minimum... while looking into those options here's a band-aid for the abuse complaint problems;

Sources of information used



Combining the two source links into rules that can be used to frustrate bots trying to use your Tor exit node for port scanning. Note this may make hackers using your exit node very un-happy as these rules causes nmap hang-time.

## Network interface used by Tor exit daemon
## Ports that Tor exit daemon binds to, maybe comma or space sepperated.
## Time to ban connections out in secconds, default equates to 10 minutes, same as default Tor cercut.
## How long to monitor conections in seconds, default equates to 10 minutes.
## How many new connections can be placed to a server in aloted update time limits. May nead to increes this depending on exit node usage and remote servers usages.
## How long to monitor connections for in minuets, default is 15 minutes but could be lessoned.
## Hom many connections to accept untill un-matched

iptables -N out_temp_ban -m comment --comment "Make custom chain for tracking ban time limits" || exit 1
iptables -A out_temp_ban -m recent --set --name temp_tcp_ban -p TCP -j DROP -m comment --comment "Ban any TCP packet coming to this chain" || exit 1

iptables -N out_vuln_scan -m comment --comment "Make custom chain for mitigating port scans originating from ${_tor_iface}" || exit 1
for _tor_port in ${_tor_ports//,/ }; do
    iptables -A out_vuln_scan -p TCP -o ${_tor_iface} --sport ${_tor_port} -m recent --name temp_tcp_ban --update --seconds ${_ban_time} -j DROP -m comment --comment "Update ban time if IP address is found in temp_tcp_ban list" || exit 1
    iptables -A out_vuln_scan -p TCP -o ${_tor_iface} --sport ${_tor_port} -m state --state NEW -m recent --set -m comment --comment "Monitor number of new conncetions to ${_server_iface}" || exit 1
    iptables -A out_vuln_scan -p TCP -o ${_tor_iface} --sport ${_tor_port} -m state --state NEW -m recent --update --seconds 30 --hitcout 10 -j out_temp_ban -m comment --comment "Ban address when to many new connections are attempted on ${_tor_iface}" || exit 1
iptables -A out_vuln_scan -j RETURN -m comment --comment "Return un-matched packets for further processing" || exit 1

## Add rules to accept/allow outbound packets
iptables -N tor_out -m comment --comment "Make custom chain for allowing Tor exit node services" || exit 1
for _tor_port in ${_tor_ports//,/ }; do
    iptables -A tor_out -p TCP -o ${_tor_iface} --sport ${_tor_port} -m state --state NEW -m recent --set --name limit_${_tor_port} -m comment --comment "Track out-going tcp connections from port ${_tor_port}" || exit 1
    iptables -A tor_out -p TCP -o ${_tor_iface} --sport ${_tor_port} -m state --state NEW -m recent --update --seconds ${_outgoing_tcp_update_seconds:-60} --hitcount ${_outgoing_tcp_hitcount:-8} --rttl --name limit_${_tor_port} -j LOG --log-prefix "TCP flooding port ${_tor_port}" -m comment --comment "Log atempts to flood port ${_tor_port} from your server" || exit 1
    iptables -A tor_out -p TCP -o ${_tor_iface} --sport ${_tor_port} -m state --state NEW -m recent --update --seconds ${_outgoing_tcp_update_seconds:-60} --hitcount ${_outgoing_tcp_hitcount:-8} --rttl --name limit_${_tor_port} -j DROP -m comment --comment "Drop attempts to flood port ${_tor_port} from your server" || exit 1
    iptables -A tor_out -p TCP -o ${_tor_iface} --sport ${_tor_port} -m limit --limit ${_outgoing_tcp_burst_minute:-15}/minute --limit-burst ${_outgoing_tcp_burst_limit:-1000} -j ACCEPT -m comment --comment "Accept with conditions new connections from port ${_tor_port} from your server" || exit 1
iptables -A tor_out -j RETURN -m comment ---comment "Reurn un-matched packets for further filtering or default polices to take effect." || exit 1
## Activate jumps from default output chain to new custom filtering chains
iptables -A OUTPUT -p TCP -o ${_tor_iface} -j out_vuln_scan -m comment --comment "Jump outbound packets through vulnerability scaning mitigation" || exit 1
iptables -A OUTPUT -p TCP -o ${_tor_iface} -j tor_out -m comment --comment "Jump outbound packets through conditional acceptance" || exit 1

Run above with bash to have magics preformed on variables with , cammas ie;

user@host~# bash iptables_limit_tor.sh

Here's that list of variables again


Note you may also wish to filter new outbound connections for -m state NEW ! --syn kinds of funny buisness used by some bots for finding exploitable servers here's an example chain that you could have prefice the above two for further filtering such malformed chatter

iptables -N out_bad_packets -m comment --comment "Make new chain for filtering malformed packets" || exit 1
iptables -A out_bad_packets -p TCP --fragment -j out_temp_ban -m comment --comment "Drop all fragmented packets" || exit 1
iptables -A out_bad_packets -p TCP -m state --state INVALID -j out_temp_ban -m comment --comment "Drop all invalid packets" || exit 1
iptables -A out_bad_packets -p TCP ! --syn -m state --state NEW -j out_temp_ban -m comment --comment "Drop new non-syn packets" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL NONE -j out_temp_ban -m comment --comment "Drop NULL scan" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL ALL -j out_temp_ban -m comment --comment "Drop XMAS scan"|| exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL FIN,URG,PSH -j out_temp_ban -m comment --comment "Drop stealth scan 1" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL SYN,RST,ACK,FIN,URG -j out_temp_ban -m comment --comment "Drop pscan 1"|| exit 1
iptables -A out_bad_packets -p TCP --tcp-flags SYN,FIN SYN,FIN -j out_temp_ban -m comment --comment "Drop pscan 2" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags FIN,RST FIN,RST -j out_temp_ban -m comment --comment "Drop pscan 3" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags SYN,RST SYN,RST -j out_temp_ban -m comment --comment "Drop SYN-RST scan" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ACK,URG URG -j out_temp_ban -m comment --comment "Drop URG scans" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL SYN,FIN -j out_temp_ban -m comment --comment "Drop SYNFIN scan" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL URG,PSH,FIN -j out_temp_ban -m comment --comment "Drop nmap Xmas scan" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL FIN -j out_temp_ban -m comment --comment "Drop FIN scan" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags ALL URG,PSH,SYN,FIN -j out_temp_ban -m comment --comment "Drop nmap-id scan" || exit 1
iptables -A out_bad_packets -p TCP --tcp-flags RST RST -o ${_tor_iface} --sport ${_tor_port} -m limit --limit 2/second --limit-burst 3 -j out_temp_ban -m comment --comment "Mitigate Smurf attacks from excesive RST packets"
iptables -A out_bad_packets -p TCP --tcp-flags RST RST -o ${_tor_iface} --sport ${_tor_port} -m limit --limit 2/second --limit-burst 2 -j RETURN -m comment --comment "Ban Smurf attacks using excesive RST packets"
iptables -A out_bad_packets -j RETURN -m comment --comment "Return un-matched packets for further processing." || exit 1

However, the above chain would be very restrictive as any matched packet will have the IP banned (perhaps change -j out_temp_ban to -j DROP or -j REJECT for testing) for however many seconds chosen in that chain's rules. This set of rules could also cause faulse positives when badly coded apps on the client's end re-connect over a new Tor cercut.


Software to consider for further shapping traffic Check out firejail for Linux, the source is on Github and Source forge and the man pages can be found on the old home page, a wordpress sub domain, and DigitalOcean has a guide for Nginx with PHP and Firejail that with a little modification could give you far more incite as to where network should be throttled back. There are other tools such as KVM too that can be used to keep spiciffic services within operational boundries so shop arround to find the one that works best for your system.

Yet another option would be to run fail2ban in such a way that when a mad sys-admin attepts a http or ssl connection to your IP that a rule is added to drop -m state --state NEW connections to those requesting your exit notice page. This if combined with sane un-ban time limmits could allow the remote server a break while thier sys-admin mutters about log polution ;-) However, that is beyond the scope of this current answer and dependant upon what software you are using to serve exit notice pages; hint both nginx and apache will serve the first vhost or server block in your configurations if now URL was requested. If using something else other than apache or nginx you'll want to consult the man pages but for me it was as simple as setting the first vhost to log to a different file and have fail2ban add any IPs from that log to a temp ban list; this also works great for banning bots on public servers because they usually use an IP address and not providing a domain request results in the server serving up the bot trap, or in this case, exit notice.

I'd lean twords running a restricted Tor exit policy (looks like you've got that handled) and then pushing traffic through VPN tunnels, extra credit points for load balancing between multipule tunnels. Because this would cause less disruption to the Tor network traffic and keep your ISP's eyes clouded to the fact that you're running an exit node... unless they wish admit to sniffing and cracking your VPN traffic. This is because running rules that temp-ban or allow for remote host to self-ban could lead to a breach of privacy to your node's clients where as pushing the traffic out to a VPN (or few) would aid your client's privacy and keep your ISP from being hounded with requests for your network traffic logs by any goverment capible of running whois www.some.domain.




I took a trip into my extencive notes and pulled up the configs for public servers that I use

Here's the fail2ban jail.local stansa

enabled  = true
port = http,https
filter = apache-ipscan
logpath = /var/log/apache*/*error_ip*
action = iptables-repeater[name=ipscan]
maxretry = 1

And here's the filter apache-ipscan.conf file

_apache_error_msg = \[[^]]*\] \[\S*:error\] \[pid \d+\] \[client <HOST>(:\d{1,5})?\]
failregex = \[client <HOST>\] client denied by server .*(?i)/.*
#   ^%(_apache_error_msg)s (AH0\d+: )?client denied by server configuration: (uri )?.*$
#            ^%(_apache_error_msg)s script '\S+' not found or unable to stat(, referer: \S+)?\s*$
ignoreregex = 
# DEV Notes: 
# the web server only responds to clients with a valid Host: 
# header. anyone who tries using IP only will get shunted into 
# the dummy-error.log and get a client-denied message
# the second regex catches folks with otherwise valid CGI paths but no good Host: header
# Author: Paul Heinlein

And here's the action iptables-repeater.conf file

# Fail2Ban configuration file
# Author: Phil Hagen <[email protected]>
# Author: Cyril Jaquier
# Modified by Yaroslav Halchenko for multiport banning and Lukas Camenzind for persistent banning
# Modified by S0AndS0 to combine features of previous Authors and Modders
# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
actionstart = iptables -N fail2ban-BADIPS-<name>
              iptables -A fail2ban-BADIPS-<name> -j RETURN
          iptables -I INPUT -j fail2ban-BADIPS-<name>
          ## Comment above line and uncomment bello line to use multiport and protocol in addition to named jails
          #iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-BADIPS-<name>
          # set up from the static file
          #cat /etc/fail2ban/ip.blocklist.<name> |grep -v ^\s*#|awk '{print $1}' | while read IP; do iptables -I fail2ban-BADIPS-<name> 1 -s $IP -j DROP; done
          cat /etc/fail2ban/ip.blocklist.<name> |grep -v ^\s*#|awk '{print $1}' | while read IP; do iptables -I fail2ban-BADIPS-<name> 1 -d $IP -j DROP; done
          ## Comment above line and uncomment bellow line to check if there are blacklist files to load before attempting to load them
          # if [ -f /etc/fail2ban/ip.blacklist.<name> ]; then cat /etc/fail2ban/ip.blacklist.<name> | grep -e <name>$ | cut -d "," -s -f 1 | while read IP; do iptables -I fail2ban-BADIPS-<name> 1 -s $IP -j DROP; done; fi
# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-BADIPS-<name>
         iptables -F fail2ban-BADIPS-<name> 
         iptables -X fail2ban-BADIPS-<name>
# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#actioncheck = iptables -n -L INPUT | grep -q fail2ban-BADIPS-<name>
actioncheck = iptables -n -L OUTPUT | grep -q fail2ban-BADIPS-<name>
# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#actionban = if ! iptables -C fail2ban-BADIPS-<name> -s <ip> -j DROP; then iptables -I fail2ban-BADIPS-<name> 1 -s <ip> -j DROP; fi
actionban = if ! iptables -C fail2ban-BADIPS-<name> -d <ip> -j DROP; then iptables -I fail2ban-BADIPS-<name> 1 -d <ip> -j DROP; fi
# Add offenders to local blacklist, if not already there
        if ! grep -Fxq '<ip>,<name>' /etc/fail2ban/ip.blocklist.<name>; then echo "<ip>,<name> # fail2ban/$( date '+%%Y-%%m-%%d %%T' ): auto-add for BadIP offender" >> /etc/fail2ban/ip.blocklist.<name>; fi
# Report offenders to badips.com
#        wget -q -O /dev/null www.badips.com/add/<name>/<ip>
# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#actionunban = iptables -D fail2ban-REPEAT-<name> -s <ip> -j DROP
actionunban = iptables -D fail2ban-REPEAT-<name> -d <ip> -j DROP
# Disabled clearing out entry from ip.blacklist (somehow happens after each stop of fail2ban)
#sed --in-place '/<ip>,<name>/d' /etc/fail2ban/ip.blacklist.<name>
# Defaut name of the chain
# Defaut name of the chain
name = BADIPS
# Option:  port
# Notes.:  specifies port to monitor
# Values:  [ NUM | STRING ]  Default:
#port = ssh
# Option:  protocol
# Notes.:  internally used by config reader for interpolations.
# Values:  [ tcp | udp | icmp | all ] Default: tcp

Note above filter has been edited to block OUTPUT on the start/stop actions but you'll still want to add the -p TCP -m state --state NEW configs to each line to only have new outbound connections banned from the logged IP address.

Last is setting up a Apache vHost config that routs those not requesting a domain to a specifide access and error log and setting the allowed vs denied access such that it always errors, not even the loopback should be able to pull up the page without popping errors. Last but not least is setting the error page for Apache to the default exit notice from Tor so that that is served instead of 503 or 404 bland messages. Or if you've added the state lines to iptables actions for fail2ban you could easily just point to the same log file that is used by your exit notice. The result would be that your server would not be able to make new connections out to the server's IP that checked your IP address but established and related connections would still be permitted, ie they could still browse your other pages but you could not browse through thiers.

  • Most welcomed, if you enjoyed that I've just pushed a large sum of scripts/notes to GitHub that you may wish to take a look-though. I started this project privetly over a year ago but now that health is an issue I've made it public for debugging and adding features in case I can't finish it; that and sertain actions taken localy and globaly by others have encurged me to take a stand for making personal privacy an easier switch.
    – S0AndS0
    Jan 13, 2016 at 8:22
  • I've written up another project and pushed it to GitHub. This aims to aid server admins in protecting their server's logs by using GnuPG asymmetric encryption. So long as your exit node or hidden service doesn't hold the related private key, the above project should keep past logs from leaking the IP addresses of other nodes connecting to your own node.
    – S0AndS0
    Oct 10, 2016 at 23:14

The limited bandwidth of the rest of the Tor network will solve those problems for you. Also if you are worried, run just relay, not an exit node.


I have a better solution : squid cache server. Squid cache server available to configure defining acl and you deny or accept each acl. It's very interesting that squid team defining a set of rules in their wiki that your question found there iptables,PF or others can't do your jobs , because just work in another layers.

  • I don't see any sensible way of combining Squid (which I know and like) with Tor...
    – filiprem
    Aug 16, 2012 at 19:20
  • try with Zebra route. Aug 16, 2012 at 19:26
  • Do you mean redirecting tor exit traffic which goes to port 80, and piping it through squid to add some control? This resolves only a small part of the problem. The real cause is preventing Tor abuse for any IP based DDOS.
    – filiprem
    Aug 16, 2012 at 19:48
  • You can use design your network in three layer: 1. outside layer 2. process layer. 3.user/server layer ====> It cause your security will be improved. Aug 16, 2012 at 20:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .