Setup of a Tor relay as a middle relay or guard relay, as an example:
### 1) Internet connection
Fiber-optic FTTH connection: 150 Mbit/s download / 150 Mbit/s upload.
### 2) UXG Pro + UniFi Network Application + UniFi Switch
UXG Pro (UniFi firmware version 5.1.26 minimum) + Raspberry Pi running Debian as an external UniFi Network Application, version 10.4.57 minimum (not used as a Tor relay) + UniFi Switch Flex Mini 2.5G.
#### 2a) IPv4 configuration
In the Network Controller window:
Click **Settings** → **Internet** → **Internet 1**.
A side panel opens.
Configure:
* VLAN ID: 7
* IPv4 configuration:
* Connection: PPPoE
* DNS server: Automatic
#### 2b) IPv6 configuration
Configure:
* IPv6: SLAAC
* Prefix Delegation: /56
(required because of `ORPort [::]:9001` in `torrc`)
* DNS server: Automatic
Click **Save**.
#### 2c) Automatic Firewall State Timeouts
In the Network Controller window:
Click **UniFi Devices** → **Gateway Pro**.
A side panel opens.
Go to:
**Settings** → **Advanced** → disable **Automatic Firewall State Timeouts** (mandatory).
Enter the following values manually:
* ICMP: 20 sec
* Other: 200 sec
* TCP Close: 5 sec
* TCP Close Wait: 10 sec
* TCP Established: 3600 sec
* TCP Fin Wait: 20 sec
* TCP Last ACK: 20 sec
* TCP Syn Recv: 5 sec
* TCP Syn Sent: 5 sec
* TCP Time Wait: 10 sec
* UDP Other: 600 sec
* UDP Stream: 600 sec
Click **Save**.
Target:
* Packet loss: 0%
* Latency: max. 10 ms
#### 2d) Optional: increase conntrack table size
```bash
sudo nano /etc/sysctl.d/XX-tor.conf
```
Add:
```text
net.netfilter.nf_conntrack_max=140000
```
#### 2e) Port forwarding for port 9001
In the Network Controller window:
Click **Settings** → **Port Forwarding** → **Create New Rule**.
Example configuration:
* Name: Port9001
* WAN: WAN1
* WAN Port: 9001
* From: Any
* Forward IP Address: 192.168.1.200
* Forward Port: 9001
* Protocol: TCP
Click **Save**.
#### 2f) Disable UniFi Threat Management / IDS/IPS depending on CPU load
The firewall is sufficient for this setup.
In the Network Controller window:
Click **Settings** → **CyberSecure** → set **Intrusion Prevention** to **Off**.
Click **Save**.
### 3) Raspberry Pi 5 as Tor relay
Raspberry Pi 5 with 4 GB RAM, or multiple devices, used as Tor relays.
Example name:
**Port9001**
Use a High Endurance SD card, or preferably an SSD.
Maximum relay bandwidth: approximately 30 Mbit/s.
Optionally install an ICE Tower cooler.
Power consumption: approximately 5 watts.
A Raspberry Pi 4 is not useful for this setup.
An Intel Core i5-12500T may be capable of approximately 80 Mbit/s (?).
#### 3a) Configure a fixed IP address on the UXG Pro
In the Network Controller window:
Click **Client Devices** → select the **Raspberry Pi**.
A side panel opens.
Go to **Settings**.
Configure:
* Name: Port9001
* IP Settings:
* Fixed IP Address: 192.168.1.200
Click **Save**.
Optional:
Create a separate VLAN and enable network isolation.
#### 3b) Raspberry Pi: install and configure Tor
Install Tor first.
Then open a terminal window and run:
```bash
sudo pcmanfm
```
Press Enter.
A new file manager window opens.
Open:
```text
/etc/tor/torrc
```
Insert the following configuration:
```text
SocksPort 0
Log notice file /var/log/tor/notices.log
ControlPort 9051
CookieAuthentication 1
ORPort 9001
ORPort [::]:9001
ExitPolicy reject *:*
ExitRelay 0
Nickname xx
RelayBandwidthRate 30 Mbits
RelayBandwidthBurst 31 Mbits
MaxMemInQueues 0
DisableDebuggerAttachment 1
AvoidDiskWrites 1
HeartbeatPeriod 3 hours
DataDirectory /var/lib/tor
ContactInfo xx@xx.com
MyFamily $xx, $xy # optional
FamilyID xxxx # optional
```
#### 3c) Increase the systemd file descriptor limit
Run:
```bash
sudo pcmanfm
```
Open:
```text
/lib/systemd/system/tor.service
```
Under `[Service]`, add:
```text
LimitNOFILE=65535
```
#### 3d) Kernel/network settings
Run:
```bash
sudo pcmanfm
```
Open either:
```text
/etc/sysctl.conf
`
``
or preferably:
```text
/etc/sysctl.d/tor.conf
```
Add:
```text
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.ip_local_port_range = 15000 64000
```
### 4) Ask ChatGPT
Anti DDoS strong (like toralf):
DDoS protection for IPv4 and IPv6 addresses per Tor relay:
```text
0.0.0.0:9001
[::]:9001
```
-–
## Objective
IPv4 > 3/min → block for 7 days
IPv4 > 7/h → block for 7 days
IPv6 > 3/min → block the /64 for 7 days
IPv6 > 7/h → block the /64 for 7 days
> 5 simultaneous connections from one source → DROP
> ESTABLISHED → never touch
ORPort → 9001
IPv4 → 0.0.0.0:9001
IPv6 → [::]:9001
-–
IPv4: `tor-ddos-v4`
IPv6: `tor-ddos-v6`
3/min
7/hour
7-day timeout
maximum 5 simultaneous connections per IPv4 address or IPv6 /64
persistent storage
restore at boot
automatic save every 5 minutes
-–
```bash
sudo -i
```
## 1. Stop Tor and install packages
```bash
systemctl status tor.service --no-pager
systemctl status tor@default.service --no-pager
sudo systemctl stop tor.service
sudo systemctl stop tor@default.service 2>/dev/null || true
sudo ss -lntp | grep ‘:9001’ || true
sudo apt update
sudo apt install -y jq ipset iptables
jq --version
ipset --version
iptables --version
ip6tables --version
```
-–
## 2. Create IPv4 and IPv6 blocklists
IPv4 blocks individual addresses. IPv6 stores the complete /64 network.
```bash
sudo ipset create tor-ddos-v4 hash:ip \
family inet timeout 604800 -exist
sudo ipset create tor-ddos-v6 hash:ip \
family inet6 netmask 64 timeout 604800 -exist
```
Check:
```bash
sudo ipset list tor-ddos-v4 | head -12
sudo ipset list tor-ddos-v6 | head -12
```
For IPv6, the following must appear:
```text
Type: hash:ip
Header: family inet6 … netmask 64 timeout 604800
```
-–
## 3. Create firewall chains
```bash
sudo iptables -N TOR-DDOS-V4
sudo ip6tables -N TOR-DDOS-V6
```
Always accept established connections:
```bash
sudo iptables -I INPUT 1 \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT
sudo ip6tables -I INPUT 1 \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT
```
Route new SYN connections to port 9001 into the protection chains:
```bash
sudo iptables -A INPUT \
-p tcp --dport 9001 --syn \
-m conntrack --ctstate NEW \
-j TOR-DDOS-V4
sudo ip6tables -A INPUT \
-p tcp --dport 9001 --syn \
-m conntrack --ctstate NEW \
-j TOR-DDOS-V6
```
-–
## 4. Drop sources that are already blocked
Add only one such DROP rule for each protocol:
```bash
sudo iptables -A TOR-DDOS-V4 \
-m set --match-set tor-ddos-v4 src \
-j DROP
sudo ip6tables -A TOR-DDOS-V6 \
-m set --match-set tor-ddos-v6 src \
-j DROP
```
-–
## 5. Block more than three new connections per minute
IPv4:
```bash
sudo iptables -A TOR-DDOS-V4 \
-p tcp --dport 9001 \
-m hashlimit \
--hashlimit-above 3/minute \
--hashlimit-burst 3 \
--hashlimit-mode srcip \
--hashlimit-name tor-ddos-v4-3m \
-j SET --add-set tor-ddos-v4 src --exist
```
IPv6, grouped by /64:
```bash
sudo ip6tables -A TOR-DDOS-V6 \
-p tcp --dport 9001 \
-m hashlimit \
--hashlimit-above 3/minute \
--hashlimit-burst 3 \
--hashlimit-mode srcip \
--hashlimit-srcmask 64 \
--hashlimit-name tor-ddos-v6-3m \
-j SET --add-set tor-ddos-v6 src --exist
```
-–
## 6. Block more than seven new connections per hour
IPv4:
```bash
sudo iptables -A TOR-DDOS-V4 \
-p tcp --dport 9001 \
-m hashlimit \
--hashlimit-above 7/hour \
--hashlimit-burst 7 \
--hashlimit-mode srcip \
--hashlimit-name tor-ddos-v4-7h \
-j SET --add-set tor-ddos-v4 src --exist
```
IPv6, grouped by /64:
```bash
sudo ip6tables -A TOR-DDOS-V6 \
-p tcp --dport 9001 \
-m hashlimit \
--hashlimit-above 7/hour \
--hashlimit-burst 7 \
--hashlimit-mode srcip \
--hashlimit-srcmask 64 \
--hashlimit-name tor-ddos-v6-7h \
-j SET --add-set tor-ddos-v6 src --exist
```
-–
## 7. Maximum of five simultaneous connections
IPv4 per individual address:
```bash
sudo iptables -A TOR-DDOS-V4 \
-p tcp --dport 9001 \
-m connlimit \
--connlimit-above 5 \
--connlimit-mask 32 \
--connlimit-saddr \
-j DROP
```
IPv6 grouped by /64:
```bash
sudo ip6tables -A TOR-DDOS-V6 \
-p tcp --dport 9001 \
-m connlimit \
--connlimit-above 5 \
--connlimit-mask 64 \
--connlimit-saddr \
-j DROP
```
Accept all remaining new Tor connections:
```bash
sudo iptables -A TOR-DDOS-V4 \
-p tcp --dport 9001 \
-j ACCEPT
sudo ip6tables -A TOR-DDOS-V6 \
-p tcp --dport 9001 \
-j ACCEPT
```
Check:
```bash
sudo iptables -L TOR-DDOS-V4 -n -v --line-numbers
sudo ip6tables -L TOR-DDOS-V6 -n -v --line-numbers
```
Expected order:
```text
1 DROP match-set
2 SET above 3/min burst 3
3 SET above 7/hour burst 7
4 DROP connlimit above 5
5 ACCEPT tcp dpt:9001
```
-–
## 8. Save the firewall state for the first time
```bash
sudo mkdir -p /var/lib/tor-ddos
sudo chmod 700 /var/lib/tor-ddos
```
The redirection must also be performed with root privileges:
```bash
sudo sh -c \
‘/usr/sbin/iptables-save > /var/lib/tor-ddos/rules.v4’
sudo sh -c \
‘/usr/sbin/ip6tables-save > /var/lib/tor-ddos/rules.v6’
sudo sh -c \
‘/usr/sbin/ipset save tor-ddos-v4 > /var/lib/tor-ddos/tor-ddos-v4.ipset’
sudo sh -c \
‘/usr/sbin/ipset save tor-ddos-v6 > /var/lib/tor-ddos/tor-ddos-v6.ipset’
```
Check:
```bash
sudo ls -lh /var/lib/tor-ddos
```
Four files must exist and each must be larger than zero bytes.
-–
## 9. Create the restore script
```bash
sudo tee /root/tor-ddos-restore.sh >/dev/null <<‘EOF’
#!/bin/bash
set -e
/usr/sbin/ipset restore -exist \
< /var/lib/tor-ddos/tor-ddos-v4.ipset
/usr/sbin/ipset restore -exist \
< /var/lib/tor-ddos/tor-ddos-v6.ipset
/usr/sbin/iptables-restore \
< /var/lib/tor-ddos/rules.v4
/usr/sbin/ip6tables-restore \
< /var/lib/tor-ddos/rules.v6
EOF
sudo chmod 700 /root/tor-ddos-restore.sh
```
-–
## 10. Create the restore service
```bash
sudo tee /etc/systemd/system/tor-ddos-restore.service >/dev/null <<‘EOF’
[Unit]
Description=Restore Tor DDoS firewall rules
Before=tor.service tor@default.service
After=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/root/tor-ddos-restore.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
```
-–
## 11. Create the save script
```bash
sudo tee /root/tor-ddos-save.sh >/dev/null <<‘EOF’
#!/bin/bash
set -e
/usr/sbin/iptables-save \
> /var/lib/tor-ddos/rules.v4
/usr/sbin/ip6tables-save \
> /var/lib/tor-ddos/rules.v6
/usr/sbin/ipset save tor-ddos-v4 \
> /var/lib/tor-ddos/tor-ddos-v4.ipset
/usr/sbin/ipset save tor-ddos-v6 \
> /var/lib/tor-ddos/tor-ddos-v6.ipset
EOF
sudo chmod 700 /root/tor-ddos-save.sh
```
-–
## 12. Create the save service
```bash
sudo tee /etc/systemd/system/tor-ddos-save.service >/dev/null <<‘EOF’
[Unit]
Description=Save Tor DDoS firewall state
After=tor-ddos-restore.service
[Service]
Type=oneshot
ExecStart=/root/tor-ddos-save.sh
EOF
```
-–
## 13. Create a repeating five-minute timer
```bash
sudo tee /etc/systemd/system/tor-ddos-save.timer >/dev/null <<‘EOF’
[Unit]
Description=Periodic Tor DDoS firewall state save
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
AccuracySec=30s
Unit=tor-ddos-save.service
[Install]
WantedBy=timers.target
EOF
```
-–
## 14. Create a dedicated real tor.service
```bash
sudo tee /etc/systemd/system/tor.service >/dev/null <<‘EOF’
[Unit]
Description=Tor relay
After=network-online.target tor-ddos-restore.service
Wants=network-online.target tor-ddos-restore.service
[Service]
Type=notify
NotifyAccess=all
User=debian-tor
Group=debian-tor
ExecStartPre=/usr/bin/tor --verify-config -f /etc/tor/torrc
ExecStart=/usr/bin/tor --RunAsDaemon 0 -f /etc/tor/torrc
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStartSec=300
TimeoutStopSec=60
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
```
Check the configuration:
```bash
sudo -u debian-tor /usr/bin/tor \
--RunAsDaemon 0 \
-f /etc/tor/torrc \
--verify-config
```
Expected:
```text
Configuration was valid
```
-–
## 15. Enable the services
```bash
sudo systemctl daemon-reload
sudo systemctl enable tor-ddos-restore.service
sudo systemctl enable --now tor-ddos-save.timer
sudo systemctl enable tor.service
```
Disable and mask the default instance:
```bash
sudo systemctl disable --now tor@default.service 2>/dev/null || true
sudo systemctl mask tor@default.service
sudo systemctl reset-failed tor@default.service
```
-–
## 16. Save once and test the restore
```bash
sudo systemctl start tor-ddos-save.service
sudo systemctl restart tor-ddos-restore.service
```
Check:
```bash
systemctl status tor-ddos-save.service --no-pager -l
systemctl status tor-ddos-restore.service --no-pager -l
```
After successful execution, the save service will return to:
```text
inactive (dead)
```
This is normal for `Type=oneshot` without `RemainAfterExit`.
The restore service must show:
```text
Active: active (exited)
status=0/SUCCESS
```
-–
## 17. Start Tor
```bash
sudo systemctl restart tor.service
```
Check:
```bash
systemctl status tor.service --no-pager -l
systemctl is-enabled tor.service
systemctl is-enabled tor@default.service
systemctl is-active tor.service
systemctl is-active tor@default.service
sudo ss -lntp |
grep -E ‘:(9001|9050|9051)\b’ || true
```
Expected:
```text
tor.service: enabled and active
tor@default.service: masked and inactive
```
The following ports may be open:
```text
127.0.0.1:9051
0.0.0.0:9001
[::]:9001
```
Port 9050 must not appear.
-–
## 18. Check the firewall
```bash
sudo ipset list tor-ddos-v4 |
grep -E ‘Type:|Header:|References:|Number of entries’
sudo ipset list tor-ddos-v6 |
grep -E ‘Type:|Header:|References:|Number of entries’
sudo iptables -L INPUT -n -v --line-numbers
sudo ip6tables -L INPUT -n -v --line-numbers
sudo iptables -L TOR-DDOS-V4 -n -v --line-numbers
sudo ip6tables -L TOR-DDOS-V6 -n -v --line-numbers
systemctl list-timers --all tor-ddos-save.timer
```
-–
## 19. Reboot
```bash
sudo systemctl start tor-ddos-save.service
sudo reboot
```
-–
## 20. Check everything after reboot
```bash
systemctl status tor-ddos-restore.service --no-pager -l
systemctl status tor-ddos-save.timer –
no-pager -l
systemctl status tor.service --no-pager -l
systemctl is-enabled tor.service
systemctl is-enabled tor@default.service
sudo ss -lntp | grep ‘:9001’
sudo ipset list tor-ddos-v4 |
grep -E ‘Type:|Header:|References:|Number of entries’
sudo ipset list tor-ddos-v6 |
grep -E ‘Type:|Header:|References:|Number of entries’
sudo iptables -L INPUT -n -v --line-numbers
sudo ip6tables -L INPUT -n -v --line-numbers
sudo iptables -L TOR-DDOS-V4 -n -v --line-numbers
sudo ip6tables -L TOR-DDOS-V6 -n -v --line-numbers
systemctl list-timers --all tor-ddos-save.timer
```