summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhc <hc@a.nub.ninja>2026-02-07 12:15:01 +0000
committerhc <hc@a.nub.ninja>2026-02-07 12:15:01 +0000
commitf6cdeabe2f57b97299308e16486958ed122315b9 (patch)
treec570f1b5ef373eb251c8504992b95f1b7789746f
initial commitHEADmain
-rw-r--r--.gitignore1
-rw-r--r--Dockerfile.mullvad20
-rw-r--r--docker-compose.yml14
-rw-r--r--docs35
-rw-r--r--entrypoint.sh82
-rw-r--r--torrent/Dockerfile14
-rw-r--r--torrent/docker-compose.yml8
-rw-r--r--wg0.conf.example17
8 files changed, 191 insertions, 0 deletions
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 @@
1FROM rockylinux/rockylinux:10
2
3RUN dnf install -y epel-release && \
4 dnf install -y \
5 wireguard-tools \
6 iptables \
7 iproute \
8 curl \
9 procps-ng \
10 && dnf clean all
11
12# Copy WireGuard config (exported from Mullvad website)
13COPY wg0.conf /etc/wireguard/wg0.conf
14RUN chmod 600 /etc/wireguard/wg0.conf
15
16# Kill switch: only allow traffic through the VPN tunnel
17COPY entrypoint.sh /entrypoint.sh
18RUN chmod +x /entrypoint.sh
19
20ENTRYPOINT ["/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 @@
1services:
2 mullvad:
3 build: { context: ., dockerfile: Dockerfile.mullvad }
4 container_name: mullvad-vpn
5 cap_add: [NET_ADMIN]
6 devices: [/dev/net/tun:/dev/net/tun]
7 restart: unless-stopped
8
9 # Example: route any container through the VPN
10 app1:
11 image: rockylinux/rockylinux:10
12 network_mode: "container:mullvad-vpn"
13 depends_on: [mullvad]
14 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 @@
1## Setup
2
3Both must use --in-pod=false so the torrent container can attach to the VPN container's network.
4Downloads appear in /root/downloads on the host.
5
6 cd /root/mullvad-docker && podman compose --in-pod=false up -d
7 cd /root/mullvad-docker/torrent && podman compose --in-pod=false up -d
8
9## Shell into container
10
11 podman exec -it torrent bash
12
13## aria2p commands
14
15 # Add downloads
16 aria2p add "magnet:?xt=urn:btih:..."
17 aria2p add "https://example.com/file.zip"
18 aria2p add /path/to/file.torrent
19
20 # Monitor
21 aria2p show # list all downloads
22 aria2p top # live TUI
23
24 # Control
25 aria2p pause <GID> # pause one
26 aria2p resume <GID> # resume one
27 aria2p remove <GID> # remove one
28 aria2p pause-all
29 aria2p resume-all
30
31 # Cleanup
32 aria2p purge # clear completed/errored from list
33
34GID is the hex ID from aria2p show.
35
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100644
index 0000000..2316e01
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,82 @@
1#!/bin/bash
2set -e
3
4WG_CONF="/etc/wireguard/wg0.conf"
5
6# Parse the config file
7PRIVATE_KEY=$(grep -oP 'PrivateKey\s*=\s*\K.*' "$WG_CONF" | tr -d ' ')
8ADDRESS_V4=$(grep -oP 'Address\s*=\s*\K[^,]+' "$WG_CONF" | grep -v ':' | tr -d ' ')
9ADDRESS_V6=$(grep -oP 'Address\s*=\s*\K.*' "$WG_CONF" | grep -oP '[^,]*::[^,]*' | tr -d ' ')
10DNS_SERVERS=$(grep -oP 'DNS\s*=\s*\K.*' "$WG_CONF" | tr ',' '\n' | tr -d ' ')
11PEER_PUBKEY=$(grep -oP 'PublicKey\s*=\s*\K.*' "$WG_CONF" | tr -d ' ')
12ENDPOINT=$(grep -oP 'Endpoint\s*=\s*\K.*' "$WG_CONF" | tr -d ' ')
13WG_ENDPOINT=$(echo "$ENDPOINT" | cut -d: -f1)
14WG_PORT=$(echo "$ENDPOINT" | cut -d: -f2)
15
16# Set DNS manually
17if [ -n "$DNS_SERVERS" ]; then
18 : > /etc/resolv.conf
19 for dns in $DNS_SERVERS; do
20 echo "nameserver $dns" >> /etc/resolv.conf
21 done
22fi
23
24DEFAULT_IF=$(ip route | awk '/default/ {print $5; exit}')
25DEFAULT_GW=$(ip route | awk '/default/ {print $3; exit}')
26
27# --- IPv4 kill switch ---
28iptables -A INPUT -i lo -j ACCEPT
29iptables -A OUTPUT -o lo -j ACCEPT
30iptables -A OUTPUT -d "$WG_ENDPOINT" -p udp --dport "$WG_PORT" -j ACCEPT
31iptables -A INPUT -s "$WG_ENDPOINT" -p udp --sport "$WG_PORT" -j ACCEPT
32iptables -A INPUT -i wg0 -j ACCEPT
33iptables -A OUTPUT -o wg0 -j ACCEPT
34iptables -A INPUT -i "$DEFAULT_IF" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
35# Allow container-to-container traffic
36iptables -A INPUT -i eth0 -j ACCEPT
37iptables -A OUTPUT -o eth0 -d 172.16.0.0/12 -j ACCEPT
38iptables -A OUTPUT -o eth0 -d 10.0.0.0/8 -j ACCEPT
39iptables -A OUTPUT -o eth0 -d 192.168.0.0/16 -j ACCEPT
40iptables -A INPUT -j DROP
41iptables -A OUTPUT -j DROP
42
43# --- IPv6 kill switch ---
44ip6tables -A INPUT -i lo -j ACCEPT
45ip6tables -A OUTPUT -o lo -j ACCEPT
46ip6tables -A INPUT -i wg0 -j ACCEPT
47ip6tables -A OUTPUT -o wg0 -j ACCEPT
48ip6tables -A INPUT -j DROP
49ip6tables -A OUTPUT -j DROP
50
51# --- Bring up WireGuard manually (no wg-quick) ---
52ip link add wg0 type wireguard
53echo "$PRIVATE_KEY" | wg set wg0 private-key /dev/stdin peer "$PEER_PUBKEY" endpoint "$ENDPOINT" allowed-ips 0.0.0.0/0,::/0
54
55[ -n "$ADDRESS_V4" ] && ip addr add "$ADDRESS_V4" dev wg0
56[ -n "$ADDRESS_V6" ] && ip addr add "$ADDRESS_V6" dev wg0
57
58ip link set wg0 up
59
60# Route the WireGuard endpoint via the real gateway (avoid routing loop)
61ip route add "$WG_ENDPOINT"/32 via "$DEFAULT_GW" dev "$DEFAULT_IF"
62
63# Route all other traffic through the tunnel
64ip route add 0.0.0.0/1 dev wg0
65ip route add 128.0.0.0/1 dev wg0
66
67# IPv6 routes through the tunnel
68if [ -n "$ADDRESS_V6" ]; then
69 ip -6 route add ::/1 dev wg0
70 ip -6 route add 8000::/1 dev wg0
71fi
72
73echo "VPN is up. Checking connection..."
74curl -s --max-time 10 https://am.i.mullvad.net/connected || echo "Warning: could not verify Mullvad connection"
75echo "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)"
76
77echo "VPN gateway ready."
78
79# Keep the container running, exit gracefully on SIGTERM
80trap 'echo "Shutting down VPN..."; ip link del wg0 2>/dev/null; exit 0' SIGTERM SIGINT
81sleep infinity &
82wait $!
diff --git a/torrent/Dockerfile b/torrent/Dockerfile
new file mode 100644
index 0000000..3a4fd53
--- /dev/null
+++ b/torrent/Dockerfile
@@ -0,0 +1,14 @@
1FROM rockylinux/rockylinux:10
2
3RUN dnf install -y epel-release && \
4 dnf install -y aria2 python3-pip curl tmux bmon && \
5 pip install aria2p[tui] && \
6 dnf clean all
7
8RUN echo 'alias ls="ls --color=auto"' >> /root/.bashrc && \
9 echo 'alias ll="ls -lah --color=auto"' >> /root/.bashrc && \
10 echo 'export PS1="[\u@torrent \W]\$ "' >> /root/.bashrc
11
12WORKDIR /downloads
13
14ENTRYPOINT ["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 @@
1services:
2 torrent:
3 build: .
4 container_name: torrent
5 network_mode: "container:mullvad-vpn"
6 volumes:
7 - /root/downloads:/downloads:z
8 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 @@
1# Download this from: https://mullvad.net/en/account/wireguard-config
2# 1. Log into your Mullvad account
3# 2. Go to WireGuard configuration
4# 3. Generate a key and download a config file
5# 4. Rename the downloaded file to wg0.conf and place it in this directory
6#
7# It will look something like this:
8
9[Interface]
10PrivateKey = YOUR_PRIVATE_KEY_HERE
11Address = 10.x.x.x/32,fc00:bbbb:bbbb:bb01::x:xxxx/128
12DNS = 10.64.0.1
13
14[Peer]
15PublicKey = SERVER_PUBLIC_KEY_HERE
16AllowedIPs = 0.0.0.0/0,::0/0
17Endpoint = xxx.xxx.xxx.xxx:51820