From f6cdeabe2f57b97299308e16486958ed122315b9 Mon Sep 17 00:00:00 2001 From: hc Date: Sat, 7 Feb 2026 12:15:01 +0000 Subject: initial commit --- .gitignore | 1 + Dockerfile.mullvad | 20 +++++++++++ docker-compose.yml | 14 ++++++++ docs | 35 ++++++++++++++++++++ entrypoint.sh | 82 ++++++++++++++++++++++++++++++++++++++++++++++ torrent/Dockerfile | 14 ++++++++ torrent/docker-compose.yml | 8 +++++ wg0.conf.example | 17 ++++++++++ 8 files changed, 191 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile.mullvad create mode 100644 docker-compose.yml create mode 100644 docs create mode 100644 entrypoint.sh create mode 100644 torrent/Dockerfile create mode 100644 torrent/docker-compose.yml create mode 100644 wg0.conf.example diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c4cb47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +wg0.conf diff --git a/Dockerfile.mullvad b/Dockerfile.mullvad new file mode 100644 index 0000000..9998d60 --- /dev/null +++ b/Dockerfile.mullvad @@ -0,0 +1,20 @@ +FROM rockylinux/rockylinux:10 + +RUN dnf install -y epel-release && \ + dnf install -y \ + wireguard-tools \ + iptables \ + iproute \ + curl \ + procps-ng \ + && dnf clean all + +# Copy WireGuard config (exported from Mullvad website) +COPY wg0.conf /etc/wireguard/wg0.conf +RUN chmod 600 /etc/wireguard/wg0.conf + +# Kill switch: only allow traffic through the VPN tunnel +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..96437e5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +services: + mullvad: + build: { context: ., dockerfile: Dockerfile.mullvad } + container_name: mullvad-vpn + cap_add: [NET_ADMIN] + devices: [/dev/net/tun:/dev/net/tun] + restart: unless-stopped + + # Example: route any container through the VPN + app1: + image: rockylinux/rockylinux:10 + network_mode: "container:mullvad-vpn" + depends_on: [mullvad] + command: bash -c "dnf install -y curl && curl -s https://am.i.mullvad.net/connected && curl -s https://am.i.mullvad.net/ip && curl -s https://am.i.mullvad.net/country && curl -s https://am.i.mullvad.net/city" diff --git a/docs b/docs new file mode 100644 index 0000000..42baa53 --- /dev/null +++ b/docs @@ -0,0 +1,35 @@ +## Setup + +Both must use --in-pod=false so the torrent container can attach to the VPN container's network. +Downloads appear in /root/downloads on the host. + + cd /root/mullvad-docker && podman compose --in-pod=false up -d + cd /root/mullvad-docker/torrent && podman compose --in-pod=false up -d + +## Shell into container + + podman exec -it torrent bash + +## aria2p commands + + # Add downloads + aria2p add "magnet:?xt=urn:btih:..." + aria2p add "https://example.com/file.zip" + aria2p add /path/to/file.torrent + + # Monitor + aria2p show # list all downloads + aria2p top # live TUI + + # Control + aria2p pause # pause one + aria2p resume # resume one + aria2p remove # remove one + aria2p pause-all + aria2p resume-all + + # Cleanup + aria2p purge # clear completed/errored from list + +GID is the hex ID from aria2p show. + diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..2316e01 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,82 @@ +#!/bin/bash +set -e + +WG_CONF="/etc/wireguard/wg0.conf" + +# Parse the config file +PRIVATE_KEY=$(grep -oP 'PrivateKey\s*=\s*\K.*' "$WG_CONF" | tr -d ' ') +ADDRESS_V4=$(grep -oP 'Address\s*=\s*\K[^,]+' "$WG_CONF" | grep -v ':' | tr -d ' ') +ADDRESS_V6=$(grep -oP 'Address\s*=\s*\K.*' "$WG_CONF" | grep -oP '[^,]*::[^,]*' | tr -d ' ') +DNS_SERVERS=$(grep -oP 'DNS\s*=\s*\K.*' "$WG_CONF" | tr ',' '\n' | tr -d ' ') +PEER_PUBKEY=$(grep -oP 'PublicKey\s*=\s*\K.*' "$WG_CONF" | tr -d ' ') +ENDPOINT=$(grep -oP 'Endpoint\s*=\s*\K.*' "$WG_CONF" | tr -d ' ') +WG_ENDPOINT=$(echo "$ENDPOINT" | cut -d: -f1) +WG_PORT=$(echo "$ENDPOINT" | cut -d: -f2) + +# Set DNS manually +if [ -n "$DNS_SERVERS" ]; then + : > /etc/resolv.conf + for dns in $DNS_SERVERS; do + echo "nameserver $dns" >> /etc/resolv.conf + done +fi + +DEFAULT_IF=$(ip route | awk '/default/ {print $5; exit}') +DEFAULT_GW=$(ip route | awk '/default/ {print $3; exit}') + +# --- IPv4 kill switch --- +iptables -A INPUT -i lo -j ACCEPT +iptables -A OUTPUT -o lo -j ACCEPT +iptables -A OUTPUT -d "$WG_ENDPOINT" -p udp --dport "$WG_PORT" -j ACCEPT +iptables -A INPUT -s "$WG_ENDPOINT" -p udp --sport "$WG_PORT" -j ACCEPT +iptables -A INPUT -i wg0 -j ACCEPT +iptables -A OUTPUT -o wg0 -j ACCEPT +iptables -A INPUT -i "$DEFAULT_IF" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +# Allow container-to-container traffic +iptables -A INPUT -i eth0 -j ACCEPT +iptables -A OUTPUT -o eth0 -d 172.16.0.0/12 -j ACCEPT +iptables -A OUTPUT -o eth0 -d 10.0.0.0/8 -j ACCEPT +iptables -A OUTPUT -o eth0 -d 192.168.0.0/16 -j ACCEPT +iptables -A INPUT -j DROP +iptables -A OUTPUT -j DROP + +# --- IPv6 kill switch --- +ip6tables -A INPUT -i lo -j ACCEPT +ip6tables -A OUTPUT -o lo -j ACCEPT +ip6tables -A INPUT -i wg0 -j ACCEPT +ip6tables -A OUTPUT -o wg0 -j ACCEPT +ip6tables -A INPUT -j DROP +ip6tables -A OUTPUT -j DROP + +# --- Bring up WireGuard manually (no wg-quick) --- +ip link add wg0 type wireguard +echo "$PRIVATE_KEY" | wg set wg0 private-key /dev/stdin peer "$PEER_PUBKEY" endpoint "$ENDPOINT" allowed-ips 0.0.0.0/0,::/0 + +[ -n "$ADDRESS_V4" ] && ip addr add "$ADDRESS_V4" dev wg0 +[ -n "$ADDRESS_V6" ] && ip addr add "$ADDRESS_V6" dev wg0 + +ip link set wg0 up + +# Route the WireGuard endpoint via the real gateway (avoid routing loop) +ip route add "$WG_ENDPOINT"/32 via "$DEFAULT_GW" dev "$DEFAULT_IF" + +# Route all other traffic through the tunnel +ip route add 0.0.0.0/1 dev wg0 +ip route add 128.0.0.0/1 dev wg0 + +# IPv6 routes through the tunnel +if [ -n "$ADDRESS_V6" ]; then + ip -6 route add ::/1 dev wg0 + ip -6 route add 8000::/1 dev wg0 +fi + +echo "VPN is up. Checking connection..." +curl -s --max-time 10 https://am.i.mullvad.net/connected || echo "Warning: could not verify Mullvad connection" +echo "Public IP: $(curl -s --max-time 10 https://am.i.mullvad.net/ip) | Location: $(curl -s --max-time 10 https://am.i.mullvad.net/country), $(curl -s --max-time 10 https://am.i.mullvad.net/city)" + +echo "VPN gateway ready." + +# Keep the container running, exit gracefully on SIGTERM +trap 'echo "Shutting down VPN..."; ip link del wg0 2>/dev/null; exit 0' SIGTERM SIGINT +sleep infinity & +wait $! diff --git a/torrent/Dockerfile b/torrent/Dockerfile new file mode 100644 index 0000000..3a4fd53 --- /dev/null +++ b/torrent/Dockerfile @@ -0,0 +1,14 @@ +FROM rockylinux/rockylinux:10 + +RUN dnf install -y epel-release && \ + dnf install -y aria2 python3-pip curl tmux bmon && \ + pip install aria2p[tui] && \ + dnf clean all + +RUN echo 'alias ls="ls --color=auto"' >> /root/.bashrc && \ + echo 'alias ll="ls -lah --color=auto"' >> /root/.bashrc && \ + echo 'export PS1="[\u@torrent \W]\$ "' >> /root/.bashrc + +WORKDIR /downloads + +ENTRYPOINT ["aria2c", "--dir=/downloads", "--enable-rpc", "--rpc-listen-all", "--file-allocation=trunc", "--bt-tracker=udp://tracker.opentrackr.org:1337/announce,udp://open.stealth.si:80/announce,udp://tracker.torrent.eu.org:451/announce,udp://exodus.desync.com:6969/announce,udp://tracker.openbittorrent.com:6969/announce"] diff --git a/torrent/docker-compose.yml b/torrent/docker-compose.yml new file mode 100644 index 0000000..967934c --- /dev/null +++ b/torrent/docker-compose.yml @@ -0,0 +1,8 @@ +services: + torrent: + build: . + container_name: torrent + network_mode: "container:mullvad-vpn" + volumes: + - /root/downloads:/downloads:z + restart: unless-stopped diff --git a/wg0.conf.example b/wg0.conf.example new file mode 100644 index 0000000..893b9ee --- /dev/null +++ b/wg0.conf.example @@ -0,0 +1,17 @@ +# Download this from: https://mullvad.net/en/account/wireguard-config +# 1. Log into your Mullvad account +# 2. Go to WireGuard configuration +# 3. Generate a key and download a config file +# 4. Rename the downloaded file to wg0.conf and place it in this directory +# +# It will look something like this: + +[Interface] +PrivateKey = YOUR_PRIVATE_KEY_HERE +Address = 10.x.x.x/32,fc00:bbbb:bbbb:bb01::x:xxxx/128 +DNS = 10.64.0.1 + +[Peer] +PublicKey = SERVER_PUBLIC_KEY_HERE +AllowedIPs = 0.0.0.0/0,::0/0 +Endpoint = xxx.xxx.xxx.xxx:51820 -- cgit