Skip to content

Commit 0957e3e

Browse files
authored
Add files via upload
UPDATE v09.2025 : Should works with lastest Gluetun, mod, and Qbittorrent
1 parent cf4ad93 commit 0957e3e

File tree

8 files changed

+387
-1
lines changed

8 files changed

+387
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,4 @@ This project is licensed under the MIT License – see the LICENSE file for deta
177177
---
178178

179179
## Github
180-
[https://github.com/Damocles-fr/](https://github.com/Damocles-fr)
180+
[https://github.com/Damocles-fr/](https://github.com/Damocles-fr)

docker-compose.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
services:
2+
gluetun:
3+
image: ghcr.io/qdm12/gluetun:latest
4+
container_name: gluetun
5+
restart: unless-stopped
6+
cap_add:
7+
- NET_ADMIN
8+
devices:
9+
- /dev/net/tun:/dev/net/tun
10+
sysctls:
11+
- net.ipv6.conf.all.disable_ipv6=1
12+
environment:
13+
- VPN_SERVICE_PROVIDER=protonvpn
14+
- VPN_TYPE=wireguard
15+
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
16+
- WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
17+
- SERVER_COUNTRIES=${SERVER_COUNTRIES}
18+
- SERVER_CITIES=${SERVER_CITIES}
19+
- VPN_PORT_FORWARDING=on
20+
- DOT=${DOT}
21+
- DNS_ADDRESS=${DNS_ADDRESS}
22+
- TZ=${TZ}
23+
volumes:
24+
- ${CONFIG_ROOT}/gluetun:/gluetun
25+
# Publish qB WebUI through gluetun (internal 8080) to host 8081
26+
ports:
27+
- "${WEBUI_HOST_PORT:-8081}:8080/tcp"
28+
healthcheck:
29+
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:9999"]
30+
interval: 40s
31+
timeout: 15s
32+
retries: 5
33+
labels:
34+
- com.centurylinklabs.watchtower.enable=true
35+
36+
qbittorrent:
37+
image: lscr.io/linuxserver/qbittorrent:latest
38+
container_name: qbittorrent
39+
restart: unless-stopped
40+
network_mode: "service:gluetun"
41+
depends_on:
42+
gluetun:
43+
condition: service_healthy
44+
environment:
45+
- PUID=${PUID}
46+
- PGID=${PGID}
47+
- TZ=${TZ}
48+
- WEBUI_PORT=8080
49+
- QBITTORRENT_INTERFACE=tun0
50+
# Port Forwarding Mod (Syncs qBittorrent with Gluetun)
51+
- DOCKER_MODS=ghcr.io/t-anc/gsp-qbittorent-gluetun-sync-port-mod:main
52+
- GSP_GTN_API_KEY=${GSP_GTN_API_KEY}
53+
- GSP_QBITTORRENT_PORT=${GSP_QBITTORRENT_PORT}
54+
- GSP_MINIMAL_LOGS=${GSP_MINIMAL_LOGS}
55+
ulimits:
56+
nofile:
57+
soft: 32768
58+
hard: 65536
59+
volumes:
60+
- ${CONFIG_ROOT}/qBittorrent:/config
61+
- ${DOWNLOADS_ROOT}:/Downloads
62+
labels:
63+
- com.centurylinklabs.watchtower.enable=true
64+
65+
watchtower:
66+
image: containrrr/watchtower:latest
67+
container_name: watchtower
68+
restart: unless-stopped
69+
volumes:
70+
- /var/run/docker.sock:/var/run/docker.sock
71+
environment:
72+
- WATCHTOWER_CLEANUP=true
73+
- WATCHTOWER_POLL_INTERVAL=86400
74+
- WATCHTOWER_LABEL_ENABLE=true
75+
labels:
76+
- com.centurylinklabs.watchtower.enable=true

envExample

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# ──────────────────────────────────────────────────────────────────────────────
2+
# qbt-proton-qnap :: envExample (rename to .env and edit)
3+
# ──────────────────────────────────────────────────────────────────────────────
4+
# Paths on your QNAP (adapt if you don't use CACHEDEV3_DATA or SSD2TB)
5+
CONFIG_ROOT=/share/CACHEDEV3_DATA/SSD2TB/AppData/qbt-proton
6+
DOWNLOADS_ROOT=/share/CACHEDEV3_DATA/SSD2TB/Downloads
7+
8+
# Container user
9+
PUID=1000
10+
PGID=100
11+
TZ=Europe/Paris
12+
13+
# ProtonVPN (WireGuard) — get your private key from https://account.protonvpn.com/
14+
WIREGUARD_PRIVATE_KEY=REPLACE_WITH_YOUR_WG_PRIVATE_KEY
15+
# Proton WireGuard address and Proton DNS (fixed)
16+
WIREGUARD_ADDRESSES=10.2.0.2/32
17+
DNS_ADDRESS=10.2.0.1
18+
DOT=off
19+
20+
# Server selection (only port-forwarding servers are chosen)
21+
SERVER_COUNTRIES=France
22+
SERVER_CITIES=Paris
23+
24+
# qBittorrent
25+
# QNAP already binds 8080, so WebUI is published on host port 8081
26+
WEBUI_HOST_PORT=8081
27+
28+
# Gluetun Control Server auth — used by the port-forwarding mod (GSP) inside qB
29+
# Use a random 22+ chars string (letters+digits). You can generate:
30+
# docker run --rm qmcgaw/gluetun genkey
31+
GLUETUN_API_KEY=CHANGE_ME_TO_A_RANDOM_BASE58_KEY
32+
33+
# GSP Mod (Port Forwarding Sync) — must match the Gluetun API key above
34+
GSP_GTN_API_KEY=${GLUETUN_API_KEY}
35+
# Initial qBittorrent listening port (updated automatically by the mod)
36+
GSP_QBITTORRENT_PORT=53764
37+
GSP_MINIMAL_LOGS=false

qBittorrent/categories.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"Movies": {
3+
"save_path": "/Downloads/Movies",
4+
"download_path": "/Downloads/Movies"
5+
},
6+
"TV": {
7+
"save_path": "/Downloads/TV",
8+
"download_path": "/Downloads/TV"
9+
},
10+
"Music": {
11+
"save_path": "/Downloads/Music",
12+
"download_path": "/Downloads/Music"
13+
}
14+
}

qBittorrent/qBittorrent.conf

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
\
2+
[LegalNotice]
3+
Accepted=true
4+
5+
[Meta]
6+
MigrationVersion=5
7+
8+
[Network]
9+
PortForwardingEnabled=false
10+
11+
[Preferences]
12+
# Anonymous mode OFF, Encryption ALLOW (0)
13+
Session\AnonymousMode=false
14+
Session\Encryption=0
15+
16+
# Force tun0 interface
17+
Connection\Interface=tun0
18+
Connection\PortRangeMin=53764
19+
Connection\UPnP=false
20+
21+
# Save paths
22+
Downloads\SavePath=/Downloads/
23+
Session\DefaultSavePath=/Downloads
24+
Downloads\TempPath=/Downloads/Incomplete/
25+
Session\TempPath=/Downloads/Incomplete
26+
Session\TempPathEnabled=true
27+
28+
# Reasonable log size for NAS
29+
FileLogger\Enabled=true
30+
FileLogger\Path=/config/qbittorrent.log
31+
FileLogger\MaxSizeBytes=67108864
32+
FileLogger\Age=7
33+
34+
# Web UI permissive to avoid Unauthorized on first boot
35+
WebUI\Port=8080
36+
WebUI\Address=*
37+
WebUI\HostHeaderValidation=false
38+
WebUI\CSRFProtection=false
39+
WebUI\BypassLocalAuth=true
40+
WebUI\AuthSubnetWhitelistEnabled=true
41+
WebUI\AuthSubnetWhitelist=127.0.0.1/32, ::1/128
42+
WebUI\HTTPS\Enabled=false
43+
44+
# Performance (non-breaking)
45+
Session\ReannounceWhenAddressChanged=true
46+
Session\MaxConcurrentHTTPAnnounces=32
47+
DiskCache\Size=1024

qBittorrent/watched_folders.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"path": "/Downloads/Torrents",
4+
"enabled": true,
5+
"recursive": false
6+
}
7+
]

scripts/fix_after_login.sh

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
\
2+
#!/bin/sh
3+
# qbt-proton-qnap :: fix_after_login.sh
4+
# Goal:
5+
# - Stop containers cleanly
6+
# - Patch .fastresume paths (/downloads -> /Downloads)
7+
# - Ensure /Downloads subfolders & permissions
8+
# - Start Gluetun and wait healthy
9+
# - Start qBittorrent and wait for WebUI API ready
10+
# - Fetch forwarded port from Gluetun
11+
# - Apply WebUI & runtime prefs through API (bypass local, host header off, csrf off, whitelist localhost)
12+
# - Verify and restart qB to lock values
13+
set -eu
14+
15+
echo "[*] qbt-proton-qnap :: fix_after_login.sh"
16+
17+
# Load .env
18+
if [ -f ".env" ]; then
19+
# shellcheck disable=SC1091
20+
. ./.env
21+
else
22+
echo "[-] .env not found. Run from project root after creating .env from envExample."
23+
exit 1
24+
fi
25+
26+
: "${CONFIG_ROOT:?set in .env}"
27+
: "${DOWNLOADS_ROOT:?set in .env}"
28+
: "${PUID:?set}"
29+
: "${PGID:?set}"
30+
: "${WEBUI_HOST_PORT:=8081}"
31+
32+
CONF="${CONFIG_ROOT}/qBittorrent/qBittorrent.conf"
33+
BT_BACKUP="${CONFIG_ROOT}/qBittorrent/BT_backup"
34+
35+
echo "[i] Using config: ${CONF}"
36+
echo "[i] PUID=${PUID} PGID=${PGID}"
37+
38+
# Stop containers with patience
39+
echo "[*] Stopping qbittorrent"
40+
docker compose stop qbittorrent >/dev/null 2>&1 || true
41+
for i in $(seq 1 20); do
42+
state="$(docker inspect -f '{{.State.Running}}' qbittorrent 2>/dev/null || echo 'false')"
43+
[ "${state}" = "false" ] && break
44+
sleep 0.5
45+
done
46+
47+
echo "[*] Stopping gluetun"
48+
docker compose stop gluetun >/dev/null 2>&1 || true
49+
for i in $(seq 1 20); do
50+
state="$(docker inspect -f '{{.State.Running}}' gluetun 2>/dev/null || echo 'false')"
51+
[ "${state}" = "false" ] && break
52+
sleep 0.5
53+
done
54+
55+
# Patch /downloads to /Downloads in .fastresume (if any)
56+
if [ -d "${BT_BACKUP}" ]; then
57+
echo "[*] Patching /downloads -> /Downloads in .fastresume"
58+
# BusyBox compatible grep -r
59+
find "${BT_BACKUP}" -type f -name "*.fastresume" -print0 | xargs -0 -r sed -i 's#/downloads#/Downloads#g'
60+
fi
61+
62+
# Ensure folders and permissions
63+
echo "[*] Ensuring folders & permissions under: ${DOWNLOADS_ROOT}"
64+
mkdir -p "${DOWNLOADS_ROOT}/Incomplete" "${DOWNLOADS_ROOT}/Torrents"
65+
chown -R "${PUID}:${PGID}" "${DOWNLOADS_ROOT}"
66+
chmod -R u+rwX,g+rwX "${DOWNLOADS_ROOT}"
67+
68+
# Start Gluetun and wait healthy
69+
echo "[*] Starting gluetun"
70+
docker compose up -d gluetun >/dev/null
71+
# Wait for health=healthy
72+
for i in $(seq 1 60); do
73+
h="$(docker inspect -f '{{.State.Health.Status}}' gluetun 2>/dev/null || echo 'starting')"
74+
[ "${h}" = "healthy" ] && break
75+
sleep 2
76+
done
77+
docker inspect -f '{{.State.Health.Status}}' gluetun 2>/dev/null || true
78+
79+
# Start qB
80+
echo "[*] Starting qbittorrent"
81+
docker compose up -d qbittorrent >/dev/null
82+
83+
# Wait WebUI readiness (API version)
84+
echo "[*] Waiting for qBittorrent API inside container..."
85+
ok=false
86+
for i in $(seq 1 60); do
87+
if docker exec qbittorrent sh -lc "wget -qO- http://localhost:8080/api/v2/app/version >/dev/null 2>&1"; then
88+
ok=true
89+
break
90+
fi
91+
sleep 2
92+
done
93+
if [ "${ok}" != "true" ]; then
94+
echo "[-] WebUI API did not become ready"
95+
exit 1
96+
fi
97+
98+
# Get forwarded port from Gluetun
99+
FWD="$(docker exec gluetun sh -lc 'cat /tmp/gluetun/forwarded_port 2>/dev/null || curl -fsS http://localhost:8000/v1/openvpn/portforwarded' | sed -E 's/[^0-9]//g' || true)"
100+
FWD="${FWD:-0}"
101+
echo "[i] Forwarded port = ${FWD}"
102+
103+
# Temp WebUI password if LSIO reset it
104+
QBU="admin"
105+
QBP="$(docker logs qbittorrent 2>&1 | awk '/WebUI administrator username is/{f=1;next} f{print $NF; exit}')"
106+
107+
# Login & apply preferences via API (payload param 'json=...' per qB API spec)
108+
docker exec qbittorrent sh -lc "
109+
u='${QBU}'; p='${QBP}';
110+
curl -s -c /tmp/c.txt -X POST --data \"username=\$u&password=\$p\" http://localhost:8080/api/v2/auth/login >/dev/null && \
111+
curl -s -b /tmp/c.txt -H 'Referer: http://localhost:8080' \
112+
-d 'json={\"bypass_local_auth\":true,\"web_ui_host_header_validation\":false,\"web_ui_csrf_protection_enabled\":false,\"bypass_auth_subnet_whitelist_enabled\":true,\"web_ui_auth_subnet_whitelist\":\"127.0.0.1/32,::1/128\",\"web_ui_address\":\"*\",\"save_path\":\"/Downloads\",\"temp_path_enabled\":true,\"temp_path\":\"/Downloads/Incomplete\"%s}' \
113+
http://localhost:8080/api/v2/app/setPreferences >/dev/null
114+
" >/dev/null
115+
116+
# If forwarded port is valid, set it too
117+
if [ "${FWD}" -gt 0 ]; then
118+
docker exec qbittorrent sh -lc "
119+
u='${QBU}'; p='${QBP}';
120+
curl -s -c /tmp/c.txt -X POST --data \"username=\$u&password=\$p\" http://localhost:8080/api/v2/auth/login >/dev/null && \
121+
curl -s -b /tmp/c.txt -H 'Referer: http://localhost:8080' \
122+
-d 'json={\"listen_port\":${FWD}}' \
123+
http://localhost:8080/api/v2/app/setPreferences >/dev/null
124+
" >/dev/null
125+
fi
126+
127+
# Verify a subset
128+
docker exec qbittorrent sh -lc "
129+
u='${QBU}'; p='${QBP}';
130+
curl -s -c /tmp/c.txt -X POST --data \"username=\$u&password=\$p\" http://localhost:8080/api/v2/auth/login >/dev/null && \
131+
curl -s -b /tmp/c.txt http://localhost:8080/api/v2/app/preferences \
132+
| tr ',' '\n' | grep -E '\"listen_port\"|\"save_path\"|\"temp_path\"|\"bypass_local_auth\"|\"web_ui_host_header_validation\"|\"web_ui_csrf_protection_enabled\"|\"bypass_auth_subnet_whitelist_enabled\"' || true
133+
" || true
134+
135+
# Final restart to ensure the runtime applies cleanly
136+
docker restart qbittorrent >/dev/null
137+
138+
echo "[OK] Done. Open WebUI at your host:port. If some torrents error due to path updates, select them and 'Force recheck'."

0 commit comments

Comments
 (0)