Added files
This commit is contained in:
parent
1b69c8563e
commit
8972bfc43f
44
README.md
44
README.md
@ -1,2 +1,44 @@
|
|||||||
# torswitch
|
# TorSwitch
|
||||||
|
|
||||||
|
|
||||||
|
## What is it?
|
||||||
|
|
||||||
|
TorSwitch is a fork of TorGhost. It configures the onion router to redirect all internet traffic through SOCKS5 Tor proxy. DNS requests are also redirected via Tor, thus preventing DNS leaks. The script also disables unsafe packets exiting the system. Some packets like ping request can compromise your identity, therefore they are blocked.
|
||||||
|
|
||||||
|
|
||||||
|
## Build and install from source
|
||||||
|
|
||||||
|
If you are feeling lucky and running SystemD, then you can try out build script, that is mostly untested and deprecated:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sh ./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo python3 torswitch/torswitch.py -h
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
Make script executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd torswitch && chmod u+x torswitch.py && mv torswitch.py torswitch
|
||||||
|
```
|
||||||
|
|
||||||
|
And use it like:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
torswitch -h
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
1. Fix build script on trisquel
|
||||||
|
2. Test the OpenRC version
|
||||||
|
3. Make auto-detection of init system and merge both versions to single script
|
||||||
|
4. Rewrite in 6502 assembly
|
35
build.sh
Executable file
35
build.sh
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
echo "TorSwitch installer v1.1"
|
||||||
|
echo "Installing prerequisites"
|
||||||
|
sudo apt install tor -y
|
||||||
|
sudo apt install python3-pip -y
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Seems like your distro do not have python3-pip in repo; trying well-known package names"
|
||||||
|
sudo apt install python3-stem python3-requests python3-packaging cython3 -y
|
||||||
|
else
|
||||||
|
echo "Installing dependencies with pip3"
|
||||||
|
sudo pip3 install -r requirements.txt
|
||||||
|
fi
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cython3 ../torswitch.py --embed -3 -o torswitch.c --verbose
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo [SUCCESS] Generated C code
|
||||||
|
else
|
||||||
|
echo [ERROR] Build failed. Unable to generate C code using cython3
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# TODO: detect currently installed python version and use it
|
||||||
|
gcc -Os -static -I/usr/include/python3.8 -o torswitch torswitch.c -lpython3.8 -lpthread -lm -lutil -ldl
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo [SUCCESS] Compiled to static binary
|
||||||
|
else
|
||||||
|
echo [ERROR] Build failed
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
sudo cp -r torswitch /usr/bin/
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo [SUCCESS] Copied binary to /usr/bin
|
||||||
|
else
|
||||||
|
echo [ERROR] Unable to copy
|
||||||
|
exit 3
|
||||||
|
fi
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
stem>=1.7.1
|
||||||
|
requests>=2.22.0
|
||||||
|
cython
|
||||||
|
packaging
|
358
torswitch.py
Executable file
358
torswitch.py
Executable file
@ -0,0 +1,358 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
"""TorSwitch configures the onion router to redirect all internet traffic through SOCKS5 Tor proxy. This is version for SystemD.
|
||||||
|
|
||||||
|
Licensed under GNU GPLv3+ terms.
|
||||||
|
(c) 2023, xxx_stroboscope_420_xxx
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import getopt
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import signal
|
||||||
|
from stem import Signal
|
||||||
|
from stem.control import Controller
|
||||||
|
from packaging import version
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
VERSION = "1.2"
|
||||||
|
|
||||||
|
IP_API = "https://api.ipify.org/?format=json"
|
||||||
|
TOR_CHECK = "https://check.torproject.org"
|
||||||
|
UA = "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0"
|
||||||
|
|
||||||
|
class font:
|
||||||
|
# Colors of foreground
|
||||||
|
RED = "\033[31m"
|
||||||
|
LIGHTRED = "\033[91m"
|
||||||
|
YELLOW = "\033[93m"
|
||||||
|
GREEN = "\033[92m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
LIGHTBLUE = "\033[94m"
|
||||||
|
GRAY = "\033[90m"
|
||||||
|
WHITE = "\033[97m"
|
||||||
|
# Colors of background
|
||||||
|
BG_RED = "\033[41m"
|
||||||
|
BG_BLUE = "\033[104m"
|
||||||
|
# Special
|
||||||
|
CRIT = BG_RED + WHITE # Critical error
|
||||||
|
ERR = RED # Just error
|
||||||
|
WARN = YELLOW # Warning
|
||||||
|
TIME = GRAY # Timestamp
|
||||||
|
EXEC = LIGHTBLUE # Executed command
|
||||||
|
# Not colors
|
||||||
|
BOLD = "\033[1m"
|
||||||
|
ENDC = "\033[0m"
|
||||||
|
|
||||||
|
LOGO = f"""{font.RED + font.BOLD}
|
||||||
|
|
||||||
|
_____
|
||||||
|
|_ _|__ _ __
|
||||||
|
| |/ _ \| '__|
|
||||||
|
| | (_) | |
|
||||||
|
|_|\___/|_| SWITCH v{VERSION}
|
||||||
|
|
||||||
|
|
||||||
|
{font.ENDC}"""
|
||||||
|
USAGE = f"""
|
||||||
|
{font.BOLD}Usage:{font.ENDC}
|
||||||
|
-s, --start
|
||||||
|
Setup Tor as system-wide proxy
|
||||||
|
-r, --switch
|
||||||
|
Request new Tor exit node
|
||||||
|
-x, --stop
|
||||||
|
Shut down onion router and restore system defaults
|
||||||
|
-i, --info
|
||||||
|
Show information about current connection
|
||||||
|
-h, --help
|
||||||
|
Show this text and exit
|
||||||
|
"""
|
||||||
|
|
||||||
|
TOR_TRANS_PORT = 9040
|
||||||
|
TOR_SOCKS_PORT = 9050
|
||||||
|
TOR_CONTROL_PORT = 9051
|
||||||
|
TOR_DNS_PORT = 9053
|
||||||
|
TOR_HTTP_PORT = 9080
|
||||||
|
NON_TOR_RNG = "192.168.1.0/24 192.168.0.0/24"
|
||||||
|
|
||||||
|
FileConfigTorrcTorswitcher = "/etc/tor/torswitcherrc"
|
||||||
|
FileConfigResolv = "/etc/resolv.conf"
|
||||||
|
FileLogTorswitcher = "./torswitcher.log"
|
||||||
|
FileLogTor = "/var/log/tor/notices.log"
|
||||||
|
DirTorData = "/var/lib/tor"
|
||||||
|
|
||||||
|
StringConfigTorrcTorSwitcher = f"""
|
||||||
|
SOCKSPort {TOR_SOCKS_PORT}
|
||||||
|
HTTPTunnelPort {TOR_HTTP_PORT}
|
||||||
|
Log notice file {FileLogTor}
|
||||||
|
DataDirectory {DirTorData}
|
||||||
|
VirtualAddrNetwork 10.0.0.0/10
|
||||||
|
AutomapHostsOnResolve 1
|
||||||
|
TransPort {TOR_TRANS_PORT}
|
||||||
|
DNSPort {TOR_DNS_PORT}
|
||||||
|
ControlPort {TOR_CONTROL_PORT}
|
||||||
|
RunAsDaemon 1
|
||||||
|
"""
|
||||||
|
StringConfigResolv = "nameserver 127.0.0.1"
|
||||||
|
|
||||||
|
LogToFile = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Remove any colors from text
|
||||||
|
def strip_colors(text):
|
||||||
|
return text.replace(font.RED,"").replace(font.LIGHTRED,"").replace(font.YELLOW,"").replace(font.GREEN,"").replace(font.BLUE,"")\
|
||||||
|
.replace(font.LIGHTBLUE,"").replace(font.GRAY,"").replace(font.WHITE,"").replace(font.BG_RED,"").replace(font.CRIT,"")\
|
||||||
|
.replace(font.ERR,"").replace(font.WARN,"").replace(font.TIME,"").replace(font.EXEC,"").replace(font.BOLD,"").replace(font.ENDC,"")\
|
||||||
|
.replace(font.BG_BLUE,"")
|
||||||
|
|
||||||
|
# Print log line
|
||||||
|
def log(text, endt="\n"):
|
||||||
|
global LogToFile
|
||||||
|
global FileLogTorswitcher
|
||||||
|
now = time.strftime("%H:%M:%S", time.localtime())
|
||||||
|
print(f"{font.TIME}[{now}]{font.ENDC} {text}", end=endt)
|
||||||
|
if LogToFile:
|
||||||
|
try:
|
||||||
|
with open(FileLogTorswitcher, "at") as fd:
|
||||||
|
fd.write(f"[{now}] {strip_colors(text)}{endt}")
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"{font.ERR}Error: can not write log line to file '{FileLogTorswitcher}'{font.ENDC}")
|
||||||
|
|
||||||
|
# Print log line w/o time stamp
|
||||||
|
def logapp(text, endt="\n"):
|
||||||
|
global LogToFile
|
||||||
|
global FileLogTorswitcher
|
||||||
|
print(f"{text}", end=endt)
|
||||||
|
if LogToFile:
|
||||||
|
try:
|
||||||
|
with open(FileLogTorswitcher, "at") as fd:
|
||||||
|
fd.write(f"{strip_colors(text)}{endt}")
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"{font.ERR}Error: can not write log line to file '{FileLogTorswitcher}'{font.ENDC}")
|
||||||
|
|
||||||
|
# Handler for interrupt signal
|
||||||
|
def sigint_handler(signum, frame):
|
||||||
|
log(f"{font.WARN}User interrupt ! shutting down{font.ENDC}")
|
||||||
|
stop_tor_proxy()
|
||||||
|
|
||||||
|
# Execute command with printing it at terminal
|
||||||
|
def execute(cmd):
|
||||||
|
log(f"{font.EXEC}Executing '{cmd}'...{font.ENDC}")
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
# Get current public IP
|
||||||
|
def ip():
|
||||||
|
retries = 20
|
||||||
|
while retries:
|
||||||
|
retries -= 1
|
||||||
|
try:
|
||||||
|
jsonRes = requests.get(IP_API,headers={"User-Agent":UA}).json()
|
||||||
|
return jsonRes["ip"]
|
||||||
|
except:
|
||||||
|
log(f"{font.ERR}Error: cant fetch IP{font.ENDC}")
|
||||||
|
continue
|
||||||
|
return "cant fetch ip address"
|
||||||
|
|
||||||
|
# Check if we connected via Tor network
|
||||||
|
def check_tor():
|
||||||
|
retries = 20
|
||||||
|
while retries:
|
||||||
|
retries -= 1
|
||||||
|
try:
|
||||||
|
resp = requests.get(TOR_CHECK)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
log(f"{font.ERR}Error: cant access check.torproject.org{font.ENDC}")
|
||||||
|
continue
|
||||||
|
if "Congratulations. This browser is configured to use Tor." in resp.text:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
log(f"{font.ERR}Error: something went wrong while trying to access check.torproject.org{font.ENDC}")
|
||||||
|
continue
|
||||||
|
log(f"{font.ERR}Error: retries limit exceeded{font.ENDC}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if we running as root
|
||||||
|
def check_root():
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
log(f"{font.CRIT}CRITICAL: you must be root, say the magic word 'sudo'. Aborting...{font.ENDC}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Check if file contains supplied string
|
||||||
|
def file_contains(path, text):
|
||||||
|
try:
|
||||||
|
with open(path, "rt") as fd:
|
||||||
|
buff = fd.read()
|
||||||
|
return (text in buff)
|
||||||
|
except Exception as exc:
|
||||||
|
log(f"{font.WARN}Warning: error occured while trying to read file '{path}': {exc}{font.ENDC}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Print logo
|
||||||
|
def logo():
|
||||||
|
print(LOGO) # TODO: make this look better
|
||||||
|
|
||||||
|
# Print usage text
|
||||||
|
def usage():
|
||||||
|
logo()
|
||||||
|
print(USAGE)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def setup_tor_proxy():
|
||||||
|
log(f"{font.GREEN}Trying to setup onion router as system-wide proxy{font.ENDC}")
|
||||||
|
check_root()
|
||||||
|
if os.path.exists(FileConfigTorrcTorswitcher) and file_contains(FileConfigTorrcTorswitcher, StringConfigTorrcTorSwitcher):
|
||||||
|
log(f"Torrc file ('{FileConfigTorrcTorswitcher}') already configured")
|
||||||
|
else:
|
||||||
|
log("Writing torcc file... ", "")
|
||||||
|
with open(FileConfigTorrcTorswitcher, "wt") as fd:
|
||||||
|
fd.write(StringConfigTorrcTorSwitcher)
|
||||||
|
logapp(f"{font.GREEN}[done]{font.ENDC}")
|
||||||
|
if file_contains(FileConfigResolv, StringConfigResolv):
|
||||||
|
log(f"DNS '{FileConfigResolv}' file already configured")
|
||||||
|
else:
|
||||||
|
log(f"Saving original DNS '{FileConfigResolv}' file")
|
||||||
|
execute(f"sudo cp '{FileConfigResolv}' '{FileConfigResolv}.bak'")
|
||||||
|
log("Now creating our new... ", "")
|
||||||
|
with open(FileConfigResolv, "wt") as fd:
|
||||||
|
fd.write(StringConfigResolv)
|
||||||
|
logapp(f"{font.GREEN}[done]{font.ENDC}")
|
||||||
|
|
||||||
|
log("Stopping tor service")
|
||||||
|
execute("sudo systemctl stop tor")
|
||||||
|
log("Freeing tor control port")
|
||||||
|
execute(f"sudo fuser -k {TOR_CONTROL_PORT}/tcp > /dev/null 2>&1")
|
||||||
|
log("Starting new tor daemon")
|
||||||
|
execute(f"sudo -u debian-tor tor -f {FileConfigTorrcTorswitcher} > /dev/null")
|
||||||
|
log("Setting up iptables rules")
|
||||||
|
|
||||||
|
iptables_rules = f"""
|
||||||
|
NON_TOR="{NON_TOR_RNG}"
|
||||||
|
TOR_UID={subprocess.getoutput('id -ur debian-tor')}
|
||||||
|
TRANS_PORT="{TOR_TRANS_PORT}"
|
||||||
|
|
||||||
|
iptables -F
|
||||||
|
iptables -t nat -F
|
||||||
|
|
||||||
|
iptables -t nat -A OUTPUT -m owner --uid-owner $TOR_UID -j RETURN
|
||||||
|
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports {TOR_DNS_PORT}
|
||||||
|
for NET in $NON_TOR 127.0.0.0/9 127.128.0.0/10; do
|
||||||
|
iptables -t nat -A OUTPUT -d $NET -j RETURN
|
||||||
|
done
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --syn -j REDIRECT --to-ports $TRANS_PORT
|
||||||
|
|
||||||
|
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
for NET in $NON_TOR 127.0.0.0/8; do
|
||||||
|
iptables -A OUTPUT -d $NET -j ACCEPT
|
||||||
|
done
|
||||||
|
iptables -A OUTPUT -m owner --uid-owner $TOR_UID -j ACCEPT
|
||||||
|
iptables -A OUTPUT -j REJECT
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute(iptables_rules)
|
||||||
|
log("Are we connected to Tor?.. ", "")
|
||||||
|
if not check_tor():
|
||||||
|
logapp(f"{font.RED}[no]{font.ENDC}")
|
||||||
|
log(f"{font.CRIT}CRITICAL: we are NOT connected to Tor network! Reverting changes...{font.ENDC}")
|
||||||
|
stop_tor_proxy()
|
||||||
|
return False
|
||||||
|
logapp(f"{font.GREEN}[yes]{font.ENDC}")
|
||||||
|
log("Fetching current IP")
|
||||||
|
log(f"Current IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stop_tor_proxy():
|
||||||
|
log(f"{font.RED}Restoring system defaults and shutting down onion router{font.ENDC}")
|
||||||
|
check_root()
|
||||||
|
log(f"Restoring DNS '{FileConfigResolv}' file")
|
||||||
|
execute(f"mv '{FileConfigResolv}.bak' '{FileConfigResolv}'")
|
||||||
|
log(f"Flushing iptables, resetting to default")
|
||||||
|
IpFlush = """
|
||||||
|
iptables -P INPUT ACCEPT
|
||||||
|
iptables -P FORWARD ACCEPT
|
||||||
|
iptables -P OUTPUT ACCEPT
|
||||||
|
iptables -t nat -F
|
||||||
|
iptables -t mangle -F
|
||||||
|
iptables -F
|
||||||
|
iptables -X
|
||||||
|
"""
|
||||||
|
execute(IpFlush)
|
||||||
|
log("Freeing tor control port")
|
||||||
|
execute(f"sudo fuser -k {TOR_CONTROL_PORT}/tcp > /dev/null 2>&1")
|
||||||
|
# TODO: stop tor?
|
||||||
|
log("Restarting Network Manager")
|
||||||
|
execute('service network-manager restart')
|
||||||
|
time.sleep(3)
|
||||||
|
# R u rly want make request 2 some proprietary service without any proxying?
|
||||||
|
#log("Fetching current IP")
|
||||||
|
#log(f"Current IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
|
||||||
|
def switch_exit_node():
|
||||||
|
log(f"{font.YELLOW}Requesting new Tor exit node{font.ENDC}")
|
||||||
|
check_root()
|
||||||
|
if not check_tor():
|
||||||
|
log(f"{font.CRIT}CRITICAL: you are not connected to Tor network{font.ENDC}")
|
||||||
|
return
|
||||||
|
log("Fetching current IP")
|
||||||
|
log(f"Current IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
log("Checking tor pid... ", "")
|
||||||
|
if not subprocess.getoutput('id -ur debian-tor').isdigit():
|
||||||
|
log(f"{font.CRIT}seems like there is no tor process running! Aborting...{font.ENDC}")
|
||||||
|
sys.exit(2)
|
||||||
|
logapp(f"{font.GREEN}[OK]{font.ENDC}")
|
||||||
|
log("Please wait...")
|
||||||
|
time.sleep(7)
|
||||||
|
log("Requesting new circuit... ", "")
|
||||||
|
with Controller.from_port(port=TOR_CONTROL_PORT) as controller:
|
||||||
|
controller.authenticate()
|
||||||
|
controller.signal(Signal.NEWNYM)
|
||||||
|
logapp(f"{font.GREEN}[done]{font.ENDC}")
|
||||||
|
log("Fetching updated IP")
|
||||||
|
log(f"New IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
|
||||||
|
def show_connection_info():
|
||||||
|
logo()
|
||||||
|
log(f"{font.BG_BLUE + font.WHITE}Tor status:{font.ENDC} ", "")
|
||||||
|
if not check_tor():
|
||||||
|
logapp(f"{font.RED}DISCONNECTED{font.ENDC}")
|
||||||
|
return
|
||||||
|
logapp(f"{font.GREEN}CONNECTED{font.ENDC}")
|
||||||
|
log(f"{font.BG_BLUE + font.WHITE}IP:{font.ENDC} {font.GREEN}{ip()}{font.ENDC}\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
signal.signal(signal.SIGINT, sigint_handler)
|
||||||
|
if len(sys.argv) <= 1:
|
||||||
|
usage()
|
||||||
|
try:
|
||||||
|
(opts, args) = getopt.getopt(sys.argv[1:], "sxrih", [
|
||||||
|
"start", "stop", "switch", "info", "help"])
|
||||||
|
except:
|
||||||
|
usage()
|
||||||
|
sys.exit(3)
|
||||||
|
if not opts:
|
||||||
|
usage()
|
||||||
|
sys.exit(3)
|
||||||
|
for (o, a) in opts:
|
||||||
|
if o in ("-s", "--start"):
|
||||||
|
if setup_tor_proxy():
|
||||||
|
log(f"{font.BLUE}>>>{font.ENDC} {font.GREEN}Now you have +100 anonimity points!{font.ENDC} {font.BLUE}<<<{font.ENDC}")
|
||||||
|
elif o in ("-x", "--stop"):
|
||||||
|
stop_tor_proxy()
|
||||||
|
log(f"{font.BLUE}>>>{font.ENDC} {font.RED}Bye, anonymity, bye!{font.ENDC} {font.BLUE}<<<{font.ENDC}")
|
||||||
|
elif o in ("-r", "--switch"):
|
||||||
|
switch_exit_node()
|
||||||
|
elif o in ("-i", "--info"):
|
||||||
|
show_connection_info()
|
||||||
|
elif o in ("-h", "--help"):
|
||||||
|
usage()
|
||||||
|
else:
|
||||||
|
usage()
|
360
torswitch_openrc.py
Executable file
360
torswitch_openrc.py
Executable file
@ -0,0 +1,360 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
"""TorSwitch configures the onion router to redirect all internet traffic through SOCKS5 Tor proxy. This is version for OpenRC. Warning! It is mostly untested!
|
||||||
|
|
||||||
|
Licensed under GNU GPLv3+ terms.
|
||||||
|
(c) 2023, xxx_stroboscope_420_xxx
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
import getopt
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import signal
|
||||||
|
from stem import Signal
|
||||||
|
from stem.control import Controller
|
||||||
|
from packaging import version
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
VERSION = "1.2"
|
||||||
|
|
||||||
|
IP_API = "https://api.ipify.org/?format=json"
|
||||||
|
TOR_CHECK = "https://check.torproject.org"
|
||||||
|
UA = "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0"
|
||||||
|
|
||||||
|
class font:
|
||||||
|
# Colors of foreground
|
||||||
|
RED = "\033[31m"
|
||||||
|
LIGHTRED = "\033[91m"
|
||||||
|
YELLOW = "\033[93m"
|
||||||
|
GREEN = "\033[92m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
LIGHTBLUE = "\033[94m"
|
||||||
|
GRAY = "\033[90m"
|
||||||
|
WHITE = "\033[97m"
|
||||||
|
# Colors of background
|
||||||
|
BG_RED = "\033[41m"
|
||||||
|
BG_BLUE = "\033[104m"
|
||||||
|
# Special
|
||||||
|
CRIT = BG_RED + WHITE # Critical error
|
||||||
|
ERR = RED # Just error
|
||||||
|
WARN = YELLOW # Warning
|
||||||
|
TIME = GRAY # Timestamp
|
||||||
|
EXEC = LIGHTBLUE # Executed command
|
||||||
|
# Not colors
|
||||||
|
BOLD = "\033[1m"
|
||||||
|
ENDC = "\033[0m"
|
||||||
|
|
||||||
|
LOGO = f"""{font.RED + font.BOLD}
|
||||||
|
|
||||||
|
_____
|
||||||
|
|_ _|__ _ __
|
||||||
|
| |/ _ \| '__|
|
||||||
|
| | (_) | |
|
||||||
|
|_|\___/|_| SWITCH v{VERSION}
|
||||||
|
( OpenRC edition )
|
||||||
|
|
||||||
|
{font.ENDC}"""
|
||||||
|
USAGE = f"""
|
||||||
|
{font.BOLD}Usage:{font.ENDC}
|
||||||
|
-s, --start
|
||||||
|
Setup Tor as system-wide proxy
|
||||||
|
-r, --switch
|
||||||
|
Request new Tor exit node
|
||||||
|
-x, --stop
|
||||||
|
Shut down onion router and restore system defaults
|
||||||
|
-i, --info
|
||||||
|
Show information about current connection
|
||||||
|
-h, --help
|
||||||
|
Show this text and exit
|
||||||
|
"""
|
||||||
|
|
||||||
|
TOR_TRANS_PORT = 9040
|
||||||
|
TOR_SOCKS_PORT = 9050
|
||||||
|
TOR_CONTROL_PORT = 9051
|
||||||
|
TOR_DNS_PORT = 9053
|
||||||
|
TOR_HTTP_PORT = 9080
|
||||||
|
TOR_USER = "tor"
|
||||||
|
NON_TOR_RNG = "192.168.1.0/24 192.168.0.0/24"
|
||||||
|
|
||||||
|
#FileConfigTorrcReal = "/etc/tor/torrc"
|
||||||
|
FileConfigTorrcTorswitcher = "/etc/tor/torswitcherrc"
|
||||||
|
FileConfigResolv = "/etc/resolv.conf"
|
||||||
|
FileLogTorswitcher = "./torswitcher.log"
|
||||||
|
FileLogTor = "/var/log/tor/notices.log"
|
||||||
|
DirTorData = "/var/lib/tor"
|
||||||
|
|
||||||
|
StringConfigTorrcTorSwitcher = f"""
|
||||||
|
SOCKSPort {TOR_SOCKS_PORT}
|
||||||
|
HTTPTunnelPort {TOR_HTTP_PORT}
|
||||||
|
Log notice file {FileLogTor}
|
||||||
|
DataDirectory {DirTorData}
|
||||||
|
VirtualAddrNetwork 10.0.0.0/10
|
||||||
|
AutomapHostsOnResolve 1
|
||||||
|
TransPort {TOR_TRANS_PORT}
|
||||||
|
DNSPort {TOR_DNS_PORT}
|
||||||
|
ControlPort {TOR_CONTROL_PORT}
|
||||||
|
RunAsDaemon 1
|
||||||
|
"""
|
||||||
|
StringConfigResolv = "nameserver 127.0.0.1"
|
||||||
|
|
||||||
|
LogToFile = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Remove any colors from text
|
||||||
|
def strip_colors(text):
|
||||||
|
return text.replace(font.RED,"").replace(font.LIGHTRED,"").replace(font.YELLOW,"").replace(font.GREEN,"").replace(font.BLUE,"")\
|
||||||
|
.replace(font.LIGHTBLUE,"").replace(font.GRAY,"").replace(font.WHITE,"").replace(font.BG_RED,"").replace(font.CRIT,"")\
|
||||||
|
.replace(font.ERR,"").replace(font.WARN,"").replace(font.TIME,"").replace(font.EXEC,"").replace(font.BOLD,"").replace(font.ENDC,"")\
|
||||||
|
.replace(font.BG_BLUE,"")
|
||||||
|
|
||||||
|
# Print log line
|
||||||
|
def log(text, endt="\n"):
|
||||||
|
global LogToFile
|
||||||
|
global FileLogTorswitcher
|
||||||
|
now = time.strftime("%H:%M:%S", time.localtime())
|
||||||
|
print(f"{font.TIME}[{now}]{font.ENDC} {text}", end=endt)
|
||||||
|
if LogToFile:
|
||||||
|
try:
|
||||||
|
with open(FileLogTorswitcher, "at") as fd:
|
||||||
|
fd.write(f"[{now}] {strip_colors(text)}{endt}")
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"{font.ERR}Error: can not write log line to file '{FileLogTorswitcher}'{font.ENDC}")
|
||||||
|
|
||||||
|
# Print log line w/o time stamp
|
||||||
|
def logapp(text, endt="\n"):
|
||||||
|
global LogToFile
|
||||||
|
global FileLogTorswitcher
|
||||||
|
print(f"{text}", end=endt)
|
||||||
|
if LogToFile:
|
||||||
|
try:
|
||||||
|
with open(FileLogTorswitcher, "at") as fd:
|
||||||
|
fd.write(f"{strip_colors(text)}{endt}")
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"{font.ERR}Error: can not write log line to file '{FileLogTorswitcher}'{font.ENDC}")
|
||||||
|
|
||||||
|
# Handler for interrupt signal
|
||||||
|
def sigint_handler(signum, frame):
|
||||||
|
log(f"{font.WARN}User interrupt ! shutting down{font.ENDC}")
|
||||||
|
stop_tor_proxy()
|
||||||
|
|
||||||
|
# Execute command with printing it at terminal
|
||||||
|
def execute(cmd):
|
||||||
|
log(f"{font.EXEC}Executing '{cmd}'...{font.ENDC}")
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
# Get current public IP
|
||||||
|
def ip():
|
||||||
|
retries = 20
|
||||||
|
while retries:
|
||||||
|
retries -= 1
|
||||||
|
try:
|
||||||
|
jsonRes = requests.get(IP_API,headers={"User-Agent":UA}).json()
|
||||||
|
return jsonRes["ip"]
|
||||||
|
except:
|
||||||
|
log(f"{font.ERR}Error: cant fetch IP{font.ENDC}")
|
||||||
|
continue
|
||||||
|
return "cant fetch ip address"
|
||||||
|
|
||||||
|
# Check if we connected via Tor network
|
||||||
|
def check_tor():
|
||||||
|
retries = 20
|
||||||
|
while retries:
|
||||||
|
retries -= 1
|
||||||
|
try:
|
||||||
|
resp = requests.get(TOR_CHECK)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
log(f"{font.ERR}Error: cant access check.torproject.org{font.ENDC}")
|
||||||
|
continue
|
||||||
|
if "Congratulations. This browser is configured to use Tor." in resp.text:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
log(f"{font.ERR}Error: something went wrong while trying to access check.torproject.org{font.ENDC}")
|
||||||
|
continue
|
||||||
|
log(f"{font.ERR}Error: retries limit exceeded{font.ENDC}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if we running as root
|
||||||
|
def check_root():
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
log(f"{font.CRIT}CRITICAL: you must be root, say the magic word 'sudo'. Aborting...{font.ENDC}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Check if file contains supplied string
|
||||||
|
def file_contains(path, text):
|
||||||
|
try:
|
||||||
|
with open(path, "rt") as fd:
|
||||||
|
buff = fd.read()
|
||||||
|
return (text in buff)
|
||||||
|
except Exception as exc:
|
||||||
|
log(f"{font.WARN}Warning: error occured while trying to read file '{path}': {exc}{font.ENDC}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Print logo
|
||||||
|
def logo():
|
||||||
|
print(LOGO) # TODO: make this look better
|
||||||
|
|
||||||
|
# Print usage text
|
||||||
|
def usage():
|
||||||
|
logo()
|
||||||
|
print(USAGE)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def setup_tor_proxy():
|
||||||
|
log(f"{font.GREEN}Trying to setup onion router as system-wide proxy{font.ENDC}")
|
||||||
|
check_root()
|
||||||
|
if os.path.exists(FileConfigTorrcTorswitcher) and file_contains(FileConfigTorrcTorswitcher, StringConfigTorrcTorSwitcher):
|
||||||
|
log(f"Torrc file ('{FileConfigTorrcTorswitcher}') already configured")
|
||||||
|
else:
|
||||||
|
log("Writing torcc file... ", "")
|
||||||
|
with open(FileConfigTorrcTorswitcher, "wt") as fd:
|
||||||
|
fd.write(StringConfigTorrcTorSwitcher)
|
||||||
|
logapp(f"{font.GREEN}[done]{font.ENDC}")
|
||||||
|
if file_contains(FileConfigResolv, StringConfigResolv):
|
||||||
|
log(f"DNS '{FileConfigResolv}' file already configured")
|
||||||
|
else:
|
||||||
|
log(f"Saving original DNS '{FileConfigResolv}' file")
|
||||||
|
execute(f"sudo cp '{FileConfigResolv}' '{FileConfigResolv}.bak'")
|
||||||
|
log("Now creating our new... ", "")
|
||||||
|
with open(FileConfigResolv, "wt") as fd:
|
||||||
|
fd.write(StringConfigResolv)
|
||||||
|
logapp(f"{font.GREEN}[done]{font.ENDC}")
|
||||||
|
|
||||||
|
log("Stopping tor service")
|
||||||
|
execute("sudo rc-service tor stop")
|
||||||
|
log("Freeing tor control port")
|
||||||
|
execute(f"sudo fuser -k {TOR_CONTROL_PORT}/tcp > /dev/null 2>&1")
|
||||||
|
log("Starting new tor daemon")
|
||||||
|
execute(f"sudo -u {TOR_USER} tor -f {FileConfigTorrcTorswitcher} > /dev/null")
|
||||||
|
log("Setting up iptables rules")
|
||||||
|
|
||||||
|
iptables_rules = f"""
|
||||||
|
NON_TOR="{NON_TOR_RNG}"
|
||||||
|
TOR_UID={subprocess.getoutput(f'id -ur {TOR_USER}')}
|
||||||
|
TRANS_PORT="{TOR_TRANS_PORT}"
|
||||||
|
|
||||||
|
iptables -F
|
||||||
|
iptables -t nat -F
|
||||||
|
|
||||||
|
iptables -t nat -A OUTPUT -m owner --uid-owner $TOR_UID -j RETURN
|
||||||
|
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports {TOR_DNS_PORT}
|
||||||
|
for NET in $NON_TOR 127.0.0.0/9 127.128.0.0/10; do
|
||||||
|
iptables -t nat -A OUTPUT -d $NET -j RETURN
|
||||||
|
done
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --syn -j REDIRECT --to-ports $TRANS_PORT
|
||||||
|
|
||||||
|
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
for NET in $NON_TOR 127.0.0.0/8; do
|
||||||
|
iptables -A OUTPUT -d $NET -j ACCEPT
|
||||||
|
done
|
||||||
|
iptables -A OUTPUT -m owner --uid-owner $TOR_UID -j ACCEPT
|
||||||
|
iptables -A OUTPUT -j REJECT
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute(iptables_rules)
|
||||||
|
log("Are we connected to Tor?.. ", "")
|
||||||
|
if not check_tor():
|
||||||
|
logapp(f"{font.RED}[no]{font.ENDC}")
|
||||||
|
log(f"{font.CRIT}CRITICAL: we are NOT connected to Tor network! Reverting changes...{font.ENDC}")
|
||||||
|
stop_tor_proxy()
|
||||||
|
return False
|
||||||
|
logapp(f"{font.GREEN}[yes]{font.ENDC}")
|
||||||
|
log("Fetching current IP")
|
||||||
|
log(f"Current IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stop_tor_proxy():
|
||||||
|
log(f"{font.RED}Restoring system defaults and shutting down onion router{font.ENDC}")
|
||||||
|
check_root()
|
||||||
|
log(f"Restoring DNS '{FileConfigResolv}' file")
|
||||||
|
execute(f"mv '{FileConfigResolv}.bak' '{FileConfigResolv}'")
|
||||||
|
log(f"Flushing iptables, resetting to default")
|
||||||
|
IpFlush = """
|
||||||
|
iptables -P INPUT ACCEPT
|
||||||
|
iptables -P FORWARD ACCEPT
|
||||||
|
iptables -P OUTPUT ACCEPT
|
||||||
|
iptables -t nat -F
|
||||||
|
iptables -t mangle -F
|
||||||
|
iptables -F
|
||||||
|
iptables -X
|
||||||
|
"""
|
||||||
|
execute(IpFlush)
|
||||||
|
log("Freeing tor control port")
|
||||||
|
execute(f"sudo fuser -k {TOR_CONTROL_PORT}/tcp > /dev/null 2>&1")
|
||||||
|
# TODO: stop tor?
|
||||||
|
# TODO: V check if it really necessary V
|
||||||
|
#log("Restarting Network Manager")
|
||||||
|
#execute('rc-service NetworkManager restart')
|
||||||
|
time.sleep(3)
|
||||||
|
# R u rly want make request 2 some proprietary service without any proxying?
|
||||||
|
#log("Fetching current IP")
|
||||||
|
#log(f"Current IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
|
||||||
|
def switch_exit_node():
|
||||||
|
log(f"{font.YELLOW}Requesting new Tor exit node{font.ENDC}")
|
||||||
|
check_root()
|
||||||
|
if not check_tor():
|
||||||
|
log(f"{font.CRIT}CRITICAL: you are not connected to Tor network{font.ENDC}")
|
||||||
|
return
|
||||||
|
log("Fetching current IP")
|
||||||
|
log(f"Current IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
log("Checking tor pid... ", "")
|
||||||
|
if not subprocess.getoutput(f'id -ur {TOR_USER}').isdigit():
|
||||||
|
log(f"{font.CRIT}seems like there is no tor process running! Aborting...{font.ENDC}")
|
||||||
|
sys.exit(2)
|
||||||
|
logapp(f"{font.GREEN}[OK]{font.ENDC}")
|
||||||
|
log("Please wait...")
|
||||||
|
time.sleep(7)
|
||||||
|
log("Requesting new circuit... ", "")
|
||||||
|
with Controller.from_port(port=TOR_CONTROL_PORT) as controller:
|
||||||
|
controller.authenticate()
|
||||||
|
controller.signal(Signal.NEWNYM)
|
||||||
|
logapp(f"{font.GREEN}[done]{font.ENDC}")
|
||||||
|
log("Fetching updated IP")
|
||||||
|
log(f"New IP is {font.GREEN}{ip()}{font.ENDC}")
|
||||||
|
|
||||||
|
def show_connection_info():
|
||||||
|
logo()
|
||||||
|
log(f"{font.BG_BLUE + font.WHITE}Tor status:{font.ENDC} ", "")
|
||||||
|
if not check_tor():
|
||||||
|
logapp(f"{font.RED}DISCONNECTED{font.ENDC}")
|
||||||
|
return
|
||||||
|
logapp(f"{font.GREEN}CONNECTED{font.ENDC}")
|
||||||
|
log(f"{font.BG_BLUE + font.WHITE}IP:{font.ENDC} {font.GREEN}{ip()}{font.ENDC}\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
signal.signal(signal.SIGINT, sigint_handler)
|
||||||
|
if len(sys.argv) <= 1:
|
||||||
|
usage()
|
||||||
|
try:
|
||||||
|
(opts, args) = getopt.getopt(sys.argv[1:], "sxrih", [
|
||||||
|
"start", "stop", "switch", "info", "help"])
|
||||||
|
except:
|
||||||
|
usage()
|
||||||
|
sys.exit(3)
|
||||||
|
if not opts:
|
||||||
|
usage()
|
||||||
|
sys.exit(3)
|
||||||
|
for (o, a) in opts:
|
||||||
|
if o in ("-s", "--start"):
|
||||||
|
if setup_tor_proxy():
|
||||||
|
log(f"{font.BLUE}>>>{font.ENDC} {font.GREEN}Now you have +100 anonimity points!{font.ENDC} {font.BLUE}<<<{font.ENDC}")
|
||||||
|
elif o in ("-x", "--stop"):
|
||||||
|
stop_tor_proxy()
|
||||||
|
log(f"{font.BLUE}>>>{font.ENDC} {font.RED}Bye, anonymity, bye!{font.ENDC} {font.BLUE}<<<{font.ENDC}")
|
||||||
|
elif o in ("-r", "--switch"):
|
||||||
|
switch_exit_node()
|
||||||
|
elif o in ("-i", "--info"):
|
||||||
|
show_connection_info()
|
||||||
|
elif o in ("-h", "--help"):
|
||||||
|
usage()
|
||||||
|
else:
|
||||||
|
usage()
|
Loading…
Reference in New Issue
Block a user