Server mode routes all outbound traffic through Tor while preserving inbound connections (HAProxy, etc) on the public IP. Fixes kill switch blocking response packets by adding ESTABLISHED,RELATED conntrack rule, and adds PREROUTING chain for LAN client Tor routing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
339 lines
8.5 KiB
Bash
Executable File
339 lines
8.5 KiB
Bash
Executable File
#!/bin/sh /etc/rc.common
|
|
# SecuBox Tor Shield - Tor anonymization service
|
|
# Copyright (C) 2025 CyberMind.fr
|
|
|
|
START=95
|
|
STOP=10
|
|
USE_PROCD=1
|
|
|
|
PROG=/usr/sbin/torctl
|
|
CONFIG=tor-shield
|
|
TORRC=/var/run/tor/torrc
|
|
TOR_DATA=/var/lib/tor
|
|
TOR_RUN=/var/run/tor
|
|
|
|
. /lib/functions.sh
|
|
|
|
generate_torrc() {
|
|
local enabled mode dns_over_tor socks_port socks_addr trans_port dns_port
|
|
local bridges_enabled bridge_type exit_nodes exclude_exit_nodes strict_nodes
|
|
|
|
config_load "$CONFIG"
|
|
config_get enabled main enabled '0'
|
|
config_get mode main mode 'transparent'
|
|
config_get dns_over_tor main dns_over_tor '1'
|
|
|
|
config_get socks_port socks port '9050'
|
|
config_get socks_addr socks address '127.0.0.1'
|
|
|
|
config_get trans_port trans port '9040'
|
|
config_get dns_port trans dns_port '9053'
|
|
|
|
config_get bridges_enabled bridges enabled '0'
|
|
config_get bridge_type bridges type 'obfs4'
|
|
|
|
config_get exit_nodes security exit_nodes ''
|
|
config_get exclude_exit_nodes security exclude_exit_nodes ''
|
|
config_get strict_nodes security strict_nodes '0'
|
|
|
|
mkdir -p "$TOR_RUN" "$TOR_DATA"
|
|
|
|
# Clean up stale files that may have wrong ownership
|
|
rm -f "$TOR_RUN/tor.pid" "$TOR_RUN/control" "$TOR_RUN/control_auth_cookie" 2>/dev/null
|
|
rm -f "$TOR_DATA/lock" 2>/dev/null
|
|
|
|
chown tor:tor "$TOR_RUN" "$TOR_DATA"
|
|
chmod 700 "$TOR_RUN" "$TOR_DATA"
|
|
|
|
cat > "$TORRC" << EOF
|
|
# SecuBox Tor Shield - Auto-generated config
|
|
# Do not edit - managed by tor-shield
|
|
|
|
User tor
|
|
DataDirectory $TOR_DATA
|
|
Log notice syslog
|
|
ControlSocket $TOR_RUN/control
|
|
ControlSocketsGroupWritable 1
|
|
CookieAuthentication 1
|
|
CookieAuthFile $TOR_RUN/control_auth_cookie
|
|
CookieAuthFileGroupReadable 1
|
|
|
|
# SOCKS proxy
|
|
SocksPort $socks_addr:$socks_port
|
|
SocksPolicy accept 127.0.0.1
|
|
SocksPolicy accept 192.168.0.0/16
|
|
SocksPolicy accept 10.0.0.0/8
|
|
SocksPolicy reject *
|
|
EOF
|
|
|
|
# Transparent proxy mode
|
|
if [ "$mode" = "transparent" ]; then
|
|
cat >> "$TORRC" << EOF
|
|
|
|
# Transparent proxy
|
|
TransPort 0.0.0.0:$trans_port
|
|
EOF
|
|
fi
|
|
|
|
# DNS over Tor
|
|
if [ "$dns_over_tor" = "1" ]; then
|
|
cat >> "$TORRC" << EOF
|
|
|
|
# DNS over Tor
|
|
DNSPort 0.0.0.0:$dns_port
|
|
AutomapHostsOnResolve 1
|
|
AutomapHostsSuffixes .onion,.exit
|
|
VirtualAddrNetworkIPv4 10.192.0.0/10
|
|
EOF
|
|
fi
|
|
|
|
# Bridge configuration - only enable if bridges are configured
|
|
if [ "$bridges_enabled" = "1" ]; then
|
|
# Count bridge lines first
|
|
BRIDGE_COUNT=0
|
|
count_bridge() { BRIDGE_COUNT=$((BRIDGE_COUNT + 1)); }
|
|
config_list_foreach bridges bridge_lines count_bridge
|
|
|
|
# Only add UseBridges if there are actual bridges configured
|
|
if [ "$BRIDGE_COUNT" -gt 0 ]; then
|
|
cat >> "$TORRC" << EOF
|
|
|
|
# Bridge mode
|
|
UseBridges 1
|
|
EOF
|
|
# Add bridge lines from config
|
|
config_list_foreach bridges bridge_lines add_bridge_line
|
|
fi
|
|
fi
|
|
|
|
# Exit node restrictions
|
|
if [ -n "$exit_nodes" ]; then
|
|
echo "ExitNodes $exit_nodes" >> "$TORRC"
|
|
fi
|
|
if [ -n "$exclude_exit_nodes" ]; then
|
|
echo "ExcludeExitNodes $exclude_exit_nodes" >> "$TORRC"
|
|
fi
|
|
if [ "$strict_nodes" = "1" ]; then
|
|
echo "StrictNodes 1" >> "$TORRC"
|
|
fi
|
|
|
|
# Hidden services
|
|
config_foreach add_hidden_service hidden_service
|
|
|
|
# GeoIP files
|
|
if [ -f /usr/share/tor/geoip ]; then
|
|
echo "GeoIPFile /usr/share/tor/geoip" >> "$TORRC"
|
|
fi
|
|
if [ -f /usr/share/tor/geoip6 ]; then
|
|
echo "GeoIPv6File /usr/share/tor/geoip6" >> "$TORRC"
|
|
fi
|
|
|
|
# Ensure torrc is readable by tor user
|
|
chown tor:tor "$TORRC"
|
|
}
|
|
|
|
add_bridge_line() {
|
|
echo "Bridge $1" >> "$TORRC"
|
|
}
|
|
|
|
add_hidden_service() {
|
|
local cfg="$1"
|
|
local enabled name local_port virtual_port
|
|
|
|
config_get enabled "$cfg" enabled '0'
|
|
[ "$enabled" = "1" ] || return
|
|
|
|
config_get name "$cfg" name "hidden_$cfg"
|
|
config_get local_port "$cfg" local_port '80'
|
|
config_get virtual_port "$cfg" virtual_port '80'
|
|
|
|
local hs_dir="$TOR_DATA/hidden_service_$name"
|
|
mkdir -p "$hs_dir"
|
|
chown tor:tor "$hs_dir"
|
|
chmod 700 "$hs_dir"
|
|
|
|
cat >> "$TORRC" << EOF
|
|
|
|
# Hidden Service: $name
|
|
HiddenServiceDir $hs_dir
|
|
HiddenServicePort $virtual_port 127.0.0.1:$local_port
|
|
EOF
|
|
}
|
|
|
|
setup_iptables() {
|
|
local mode trans_port dns_port dns_over_tor kill_switch
|
|
|
|
config_load "$CONFIG"
|
|
config_get mode main mode 'transparent'
|
|
config_get kill_switch main kill_switch '1'
|
|
config_get dns_over_tor main dns_over_tor '1'
|
|
config_get trans_port trans port '9040'
|
|
config_get dns_port trans dns_port '9053'
|
|
|
|
# Get Tor user ID
|
|
local tor_uid=$(id -u tor 2>/dev/null || echo "tor")
|
|
|
|
# Remove from chains first (to allow chain deletion)
|
|
iptables -t nat -D OUTPUT -j TOR_SHIELD 2>/dev/null
|
|
iptables -t filter -D OUTPUT -j TOR_SHIELD 2>/dev/null
|
|
iptables -t nat -D PREROUTING -i br-lan -j TOR_SHIELD_LAN 2>/dev/null
|
|
|
|
# Clear existing Tor rules
|
|
for chain in TOR_SHIELD TOR_SHIELD_LAN; do
|
|
iptables -t nat -F $chain 2>/dev/null
|
|
iptables -t nat -X $chain 2>/dev/null
|
|
done
|
|
iptables -t filter -F TOR_SHIELD 2>/dev/null
|
|
iptables -t filter -X TOR_SHIELD 2>/dev/null
|
|
|
|
[ "$mode" = "transparent" ] || return 0
|
|
|
|
# Create chains (ignore "already exists" errors)
|
|
iptables -t nat -N TOR_SHIELD 2>/dev/null || true
|
|
iptables -t filter -N TOR_SHIELD 2>/dev/null || true
|
|
|
|
# Exclude Tor traffic
|
|
iptables -t nat -A TOR_SHIELD -m owner --uid-owner $tor_uid -j RETURN
|
|
|
|
# Exclude local networks
|
|
config_list_foreach trans excluded_ips add_excluded_ip
|
|
|
|
# Redirect DNS if enabled
|
|
if [ "$dns_over_tor" = "1" ]; then
|
|
iptables -t nat -A TOR_SHIELD -p udp --dport 53 -j REDIRECT --to-ports $dns_port
|
|
iptables -t nat -A TOR_SHIELD -p tcp --dport 53 -j REDIRECT --to-ports $dns_port
|
|
fi
|
|
|
|
# Redirect TCP to transparent proxy
|
|
iptables -t nat -A TOR_SHIELD -p tcp -j REDIRECT --to-ports $trans_port
|
|
|
|
# Add to OUTPUT chain
|
|
iptables -t nat -A OUTPUT -j TOR_SHIELD
|
|
|
|
# Kill switch - block non-Tor traffic
|
|
if [ "$kill_switch" = "1" ]; then
|
|
iptables -t filter -A TOR_SHIELD -m owner --uid-owner $tor_uid -j ACCEPT
|
|
iptables -t filter -A TOR_SHIELD -d 127.0.0.0/8 -j ACCEPT
|
|
config_list_foreach trans excluded_ips add_excluded_filter_ip
|
|
# Allow response packets for inbound connections (HAProxy, etc)
|
|
iptables -t filter -A TOR_SHIELD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
|
iptables -t filter -A TOR_SHIELD -j REJECT
|
|
iptables -t filter -A OUTPUT -j TOR_SHIELD
|
|
fi
|
|
|
|
# LAN client Tor routing via PREROUTING
|
|
setup_lan_proxy
|
|
}
|
|
|
|
add_excluded_ip() {
|
|
iptables -t nat -A TOR_SHIELD -d "$1" -j RETURN
|
|
}
|
|
|
|
add_excluded_filter_ip() {
|
|
iptables -t filter -A TOR_SHIELD -d "$1" -j ACCEPT
|
|
}
|
|
|
|
add_excluded_lan_ip() {
|
|
iptables -t nat -A TOR_SHIELD_LAN -d "$1" -j RETURN
|
|
}
|
|
|
|
setup_lan_proxy() {
|
|
local lan_proxy
|
|
config_get lan_proxy trans lan_proxy '0'
|
|
[ "$lan_proxy" = "1" ] || return 0
|
|
|
|
local trans_port dns_port dns_over_tor
|
|
config_get trans_port trans port '9040'
|
|
config_get dns_port trans dns_port '9053'
|
|
config_get dns_over_tor main dns_over_tor '1'
|
|
|
|
# Create PREROUTING chain
|
|
iptables -t nat -N TOR_SHIELD_LAN 2>/dev/null || true
|
|
|
|
# Exclude local destinations
|
|
config_list_foreach trans excluded_ips add_excluded_lan_ip
|
|
|
|
# Redirect DNS
|
|
if [ "$dns_over_tor" = "1" ]; then
|
|
iptables -t nat -A TOR_SHIELD_LAN -p udp --dport 53 -j REDIRECT --to-ports $dns_port
|
|
iptables -t nat -A TOR_SHIELD_LAN -p tcp --dport 53 -j REDIRECT --to-ports $dns_port
|
|
fi
|
|
|
|
# Redirect TCP to Tor
|
|
iptables -t nat -A TOR_SHIELD_LAN -p tcp -j REDIRECT --to-ports $trans_port
|
|
|
|
# Apply to LAN interface
|
|
iptables -t nat -A PREROUTING -i br-lan -j TOR_SHIELD_LAN
|
|
}
|
|
|
|
remove_iptables() {
|
|
# Remove from chains
|
|
iptables -t nat -D OUTPUT -j TOR_SHIELD 2>/dev/null
|
|
iptables -t filter -D OUTPUT -j TOR_SHIELD 2>/dev/null
|
|
iptables -t nat -D PREROUTING -i br-lan -j TOR_SHIELD_LAN 2>/dev/null
|
|
|
|
# Flush and remove all chains
|
|
for chain in TOR_SHIELD TOR_SHIELD_LAN; do
|
|
iptables -t nat -F $chain 2>/dev/null
|
|
iptables -t nat -X $chain 2>/dev/null
|
|
done
|
|
iptables -t filter -F TOR_SHIELD 2>/dev/null
|
|
iptables -t filter -X TOR_SHIELD 2>/dev/null
|
|
}
|
|
|
|
start_service() {
|
|
local enabled
|
|
|
|
config_load "$CONFIG"
|
|
config_get enabled main enabled '0'
|
|
|
|
[ "$enabled" = "1" ] || {
|
|
echo "Tor Shield is disabled. Enable with: uci set tor-shield.main.enabled=1"
|
|
return 0
|
|
}
|
|
|
|
# Generate torrc
|
|
generate_torrc
|
|
|
|
# Setup iptables rules
|
|
setup_iptables
|
|
|
|
# Start Tor via procd
|
|
procd_open_instance tor
|
|
procd_set_param command /usr/sbin/tor -f "$TORRC"
|
|
procd_set_param respawn 3600 5 5
|
|
procd_set_param stdout 1
|
|
procd_set_param stderr 1
|
|
procd_set_param pidfile "$TOR_RUN/tor.pid"
|
|
procd_close_instance
|
|
}
|
|
|
|
stop_service() {
|
|
# Remove iptables rules
|
|
remove_iptables
|
|
|
|
# Kill tor process
|
|
if [ -f "$TOR_RUN/tor.pid" ]; then
|
|
kill $(cat "$TOR_RUN/tor.pid") 2>/dev/null
|
|
rm -f "$TOR_RUN/tor.pid"
|
|
fi
|
|
}
|
|
|
|
service_triggers() {
|
|
procd_add_reload_trigger "$CONFIG"
|
|
}
|
|
|
|
reload_service() {
|
|
stop
|
|
start
|
|
}
|
|
|
|
restart_service() {
|
|
stop
|
|
start
|
|
}
|
|
|
|
status() {
|
|
"$PROG" status
|
|
}
|