From 9a0e6e2f5a0390bc0ac5e733fc7ac39d6db30332 Mon Sep 17 00:00:00 2001 From: Hardcore Sushi Date: Thu, 25 May 2023 22:12:53 +0200 Subject: [PATCH] TorVirt --- README.md | 31 ++++------ gateway/Dockerfile | 4 +- gateway/torrc | 2 +- gateway/wait.sh | 9 +-- hidemypussy.sh | 97 -------------------------------- network.xml | 14 +++-- pictures/NetworkManager.png | Bin 40895 -> 0 bytes torvirt | 109 ++++++++++++++++++++++++++++++++++++ 8 files changed, 138 insertions(+), 128 deletions(-) delete mode 100755 hidemypussy.sh delete mode 100644 pictures/NetworkManager.png create mode 100755 torvirt diff --git a/README.md b/README.md index 87ad5b1..ff582e4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Hide My Pussy ! -Whonix setup with KVM as workstation and a docker container as gateway. +# TorVirt +Whonix-like setup with a [libvirt](https://libvirt.org) workstation and a docker container as the [Tor](https://torproject.org) gateway. ![Logo: KVM vers docker vers Tor](pictures/logo.jpg) @@ -7,7 +7,7 @@ Whonix setup with KVM as workstation and a docker container as gateway. [Whonix](https://www.whonix.org) is a secure anonymization tool that creates a virtual machine in which all internet traffic is routed through [Tor](https://www.torproject.org/) via another virtual machine. These two VMs are connected in an internal virtual network. The only way for the workstation to access the Internet is to use the gateway, and thus anonymizing the network traffic. This allows you to run all kind of applications without worrying of IP leaks. ## What are the advatage of this project over original Whonix ? -Whonix uses one VM for the workstation and another VM for the tor gateway. If you don't have enough RAM, this may be resource expensive. With Hide my Pussy, you can improve the workstation performances by running the gateway inside a docker container instead of a full VM. +Whonix uses one VM for the workstation and another VM for the tor gateway. If you don't have enough RAM, this may be resource expensive. With TorVirt, you can improve the workstation performances by running the gateway inside a docker container instead of a full VM. ## Is it as secure as Whonix ? Docker containers share the same kernel as the host. This means that if someone manages to exploit a software in the gateway container (such as the tor daemon) and then exploits a vulnerability in the kernel, he could gain access to the host's operating system. To mitigate this risk, the gateway container is started with `--cap-drop=ALL` and the only additional software installed is the tor daemon, which runs under normal user privileges. @@ -16,7 +16,7 @@ Docker containers share the same kernel as the host. This means that if someone Install dependencies (debian-based): ``` -sudo apt-get install virt-manager docker.io +sudo apt-get install virt-manager docker.io bridge-utils ``` Clone the repo: @@ -48,11 +48,11 @@ __Don't continue if the verification fails !__ Configure your host: ``` -sudo ./hidemypussy.sh configure +sudo ./torvirt.sh configure ``` ## Create the workstation -_In this tutorial, we will use the Kicksecure VM from the Whonix project. However, it's not strictly needed. You can use any other VM instead. For that, just configure your custom VM to use the network "hidemypussy"._ +_In this tutorial, we will use the Kicksecure VM from the Whonix project. However, it's not strictly needed. You can use any other VM instead. For that, just configure your custom VM to use the network "torvirt"._ Download the Kicksecure VM from https://www.whonix.org/wiki/Kicksecure/KVM. Don't forget to __verify OpenPGP signatures__ ! \ Then, extract the downloaded archive: @@ -63,28 +63,19 @@ Move the workstation image to the libvirt's images folder: ``` sudo mv Kicksecure*.qcow2 /var/lib/libvirt/images/Kicksecure.qcow2 ``` -Edit the workstation XML file to use the hidemypussy network: +Edit the workstation XML file to use the torvirt network: ``` -sed "s/network='default'/network='hidemypussy'/g" Kicksecure*.xml > Kicksecure-hidemypussy.xml +sed "s/network='default'/network='torvirt'/g" Kicksecure*.xml > Kicksecure-torvirt.xml ``` Import the workstation VM: ``` -sudo virsh define Kicksecure-hidemypussy.xml +sudo virsh define Kicksecure-torvirt.xml ``` ## Start the gateway ``` -sudo ./hidemypussy.sh start +sudo ./torvirt.sh start ``` ## Start the workstation -The easiest way to start the workstation is through the `virt-manager` GUI. \ -Once started, you will need to configure the VM to use the tor gateway as default gateway and default DNS server. The default IP for the gateway is `10.152.152.10`. You can get it by running `sudo docker exec hidemypussy ifconfig vmnet-cont | awk '/inet addr/{print substr($2,6)}'`. \ -Inside the workstation, run: -``` -sudo echo "nameserver 10.152.152.10" > /etc/resolv.conf -sudo ip route add default via 10.152.152.10 -``` -To make this changes persistent, you can make a script that runs these 2 commands at startup or manually configure the interface in Network-Manager: - -![Network-Manager screenshot in manual configuration with gateway and DNS server set to the gateway container's IP](pictures/NetworkManager.png) +The easiest way to start the workstation is through the `virt-manager` GUI. diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 504ad6f..c0194ac 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -1,8 +1,8 @@ FROM alpine:latest -RUN apk update +RUN apk update && apk upgrade RUN apk add tor RUN adduser -D user USER user COPY torrc /etc/tor/torrc COPY wait.sh /home/user/wait.sh -ENTRYPOINT ["/home/user/wait.sh", "tor"] +CMD ["/home/user/wait.sh", "tor"] diff --git a/gateway/torrc b/gateway/torrc index 86d262a..307a317 100644 --- a/gateway/torrc +++ b/gateway/torrc @@ -2,4 +2,4 @@ VirtualAddrNetworkIPv4 10.192.0.0/10 AutomapHostsOnResolve 1 SocksPort 0 TransPort 10.152.152.10:9040 IsolateClientAddr IsolateClientProtocol IsolateDestAddr IsolateDestPort -DNSPort 10.152.152.10:5353 \ No newline at end of file +DNSPort 10.152.152.10:5353 diff --git a/gateway/wait.sh b/gateway/wait.sh index da2e018..a674d99 100755 --- a/gateway/wait.sh +++ b/gateway/wait.sh @@ -1,7 +1,8 @@ #!/bin/sh -start_file="/home/user/start" -while [ ! -f $start_file ]; do - sleep 1 -done +# wait for USR1 +sleep infinity & PID=$! +trap "kill $PID" USR1 +wait + exec $@ diff --git a/hidemypussy.sh b/hidemypussy.sh deleted file mode 100755 index dd11887..0000000 --- a/hidemypussy.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -project_name="hidemypussy" -img_name=$project_name -cont_name=$project_name -docker_folder="gateway" -network_file="network.xml" -network=$project_name -tor_trans_port="9040" -tor_dns_port="5353" -tor_virt_addr="10.192.0.0/10" -gtw_ip="10.152.152.10/18" -start_file="/home/user/start" -veth_host="vmnet-host" -veth_cont="vmnet-cont" - -print_help() { - echo -e "Usage: $0 - -ACTIONS: - - c, configure Install dependencies, configure network and build gateway docker image - - s, start Start the gateway -" -} - -virsh_get_field() { - echo $(virsh net-info $network | grep $1 | tr -s " " | cut -d " " -f 2) -} - -if [ "$#" -eq 0 ]; then - print_help -elif [ "$EUID" -ne 0 ]; then - echo "Error: root access required" -else - case $1 in - "s" | "start") - #check whether network and gateway have been configured - if [ -z "$(virsh net-list --all | grep $network)" ]; then - echo "Error: network $network not found. Did you run \"$0 configure\" first ?" - exit - elif [ -z "$(docker images | grep $img_name)" ]; then - echo "Error: docker image $img_name not found. Did you run \"$0 configure\" first ?" - exit - fi - if [ "$(docker ps -q -f name=$cont_name)" ]; then - echo "Error: conatiner $cont_name is already running" - exit - elif [ "$(docker ps -aq -f status=exited -f name=$cont_name)" ]; then - docker rm $cont_name - fi - #start $network - network_started=$(virsh_get_field "Active") - if [ $network_started = "no" ]; then - virsh net-start $network - fi - brif=$(virsh_get_field "Bridge") - #configure veth interfaces - ip link add $veth_cont type veth peer name $veth_host - brctl addif $brif $veth_host - ip link set $veth_host up - #start gateway on wait.sh - docker run --rm -itd --cap-drop=ALL --name $cont_name $img_name - pid=$(docker inspect -f '{{.State.Pid}}' $cont_name) - #setup gateway networing inside $network - ip link set netns $pid dev $veth_cont - nsenter -t $pid -n ip link set $veth_cont up - nsenter -t $pid -n ip addr add $gtw_ip dev $veth_cont - #allow *.onion - nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_cont -p tcp -d $tor_virt_addr --syn -j REDIRECT --to-ports $tor_trans_port - #redirect DNS to tor - nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_cont -p udp --dport 53 -j REDIRECT --to-ports $tor_dns_port - nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_cont -p udp --dport $tor_dns_port -j REDIRECT --to-ports $tor_dns_port - #redirect TCP to tor - nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_cont -p tcp --syn -j REDIRECT --to-ports $tor_trans_port - #start tor - docker exec $cont_name touch $start_file - docker attach $cont_name - ;; - "c" | "configure") - dockerfile="$docker_folder/Dockerfile" - if [ ! -f $network_file ]; then - echo "Error: $network_file not found" - exit - elif [ ! -f $dockerfile ]; then - echo "Error: $dockerfile not found" - exit - fi - virsh net-define $network_file - pushd $docker_folder - docker build -t $img_name . - ;; - *) - echo "Unkown action: $1" - print_help - ;; - esac -fi diff --git a/network.xml b/network.xml index 7f3e748..b53b927 100644 --- a/network.xml +++ b/network.xml @@ -1,9 +1,15 @@ - - hidemypussy + + torvirt - + - + + + + + + + diff --git a/pictures/NetworkManager.png b/pictures/NetworkManager.png deleted file mode 100644 index 26243f47142e7fc0258f22acd9e15c9103a52fbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40895 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 diff --git a/torvirt b/torvirt new file mode 100755 index 0000000..4e4a350 --- /dev/null +++ b/torvirt @@ -0,0 +1,109 @@ +#!/bin/bash + +set -e + +project_name="torvirt" +img_name=$project_name +container_name=$project_name +docker_folder="gateway" +network_file="network.xml" +network=$project_name +tor_trans_port="9040" +tor_dns_port="5353" +tor_virt_addr="10.192.0.0/10" +gtw_ip="10.152.152.10/18" +veth_host="$project_name-host" +veth_gw="$project_name-gw" + +ERROR_INVALID_ACTION=1 +ERROR_ALREADY_CONFIGURED=2 +ERROR_NOT_CONFIGURED=3 +ERROR_ALREADY_RUNNING=4 + +print_help() { + echo -e "Usage: $0 + +ACTIONS: + c, configure Install dependencies, configure network and build gateway docker image + s, start Start the gateway +" +} + +exit_with() { + echo $2 >&2 + exit $1 +} + +virsh_get_field() { + virsh net-info $network |awk "/^$1:/{print \$2}" +} + +if [ "$#" -eq 0 ]; then + print_help + exit +fi +case $1 in +"s" | "start") + # check whether network and gateway have been configured + if [ -z "$(virsh net-list --all | grep $network)" ]; then + exit_with $ERROR_NOT_CONFIGURED "Error: network $network not found. Did you run \"$project_name configure\" ?" + fi + output=$(docker images ls -q -f reference=$img_name) + if [ -z "$output" ]; then + exit_with $ERROR_NOT_CONFIGURED "Error: docker image $img_name not found. Did you run \"$project_name configure\" first ?" + fi + output=$(docker ps -q -f name=$container_name) + if [ "$output" ]; then + exit_with $ERROR_ALREADY_RUNNING "Error: conatiner $container_name is already running" + fi + output=$(docker ps -aq -f status=exited -f name=$container_name) + if [ "$output" ]; then + docker rm $container_name + fi + # start $network + network_started=$(virsh_get_field "Active") + if [ $network_started = "no" ]; then + virsh net-start $network + fi + brif=$(virsh_get_field "Bridge") + # configure veth interfaces + if ip link show $veth_host >/dev/null 2>/dev/null; then + ip link del $veth_host + fi + ip link add $veth_gw type veth peer name $veth_host + brctl addif $brif $veth_host + ip link set $veth_host up + # start gateway on wait.sh + docker run --rm -itd --cap-drop=ALL --name $container_name $img_name >/dev/null + pid=$(docker inspect -f '{{.State.Pid}}' $container_name) + # setup gateway networing inside $network + ip link set netns $pid dev $veth_gw + nsenter -t $pid -n ip link set $veth_gw up + nsenter -t $pid -n ip addr add $gtw_ip dev $veth_gw + # allow *.onion + nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_gw -p tcp -d $tor_virt_addr --syn -j REDIRECT --to-ports $tor_trans_port + # redirect DNS to tor + nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_gw -p udp --dport 53 -j REDIRECT --to-ports $tor_dns_port + nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_gw -p udp --dport $tor_dns_port -j REDIRECT --to-ports $tor_dns_port + # redirect TCP to tor + nsenter -t $pid -n iptables -t nat -A PREROUTING -i $veth_gw -p tcp --syn -j REDIRECT --to-ports $tor_trans_port + # start tor + docker kill -s USR1 $container_name >/dev/null + docker attach $container_name + ;; +"c" | "configure") + if virsh net-info $network >/dev/null 2>/dev/null; then + exit_with $ERROR_ALREADY_CONFIGURED "Error: network $network already exists." + fi + output=$(docker images ls -q -f reference=$img_name) + if [ "$output" ]; then + exit_with $ERROR_ALREADY_CONFIGURED "Error: image $img_name already exists" + fi + virsh net-define $network_file + cd $docker_folder && docker build -t $img_name . + ;; +*) + print_help + exit_with $ERROR_INVALID_ACTION "Unkown action: $1" + ;; +esac