2
0
Fork 0

initial rework

This commit is contained in:
Luca Bilke 2023-11-08 15:31:49 +01:00
parent 8363538baf
commit 4119c959c0
3 changed files with 125 additions and 268 deletions

View File

@ -16,10 +16,10 @@ RUN apt-get update && \
FROM ubuntu:jammy FROM ubuntu:jammy
LABEL org.opencontainers.image.source="https://github.com/soxfor/qbittorrent-natmap" LABEL org.opencontainers.image.source="https://github.com/ssnailed/qbittorrent-natpmp"
LABEL org.opencontainers.image.base.name="ubuntu:jammy" LABEL org.opencontainers.image.base.name="ubuntu:jammy"
LABEL description="Map port via NAT-PMP and update qBittorrent configuration" LABEL description="Map port via NAT-PMP and update qBittorrent configuration"
LABEL version="1.1.0" LABEL version="2.0.0"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
@ -47,13 +47,8 @@ RUN sed -i "/${LANG}/s/^# //g" /etc/locale.gen && \
ENV QBITTORRENT_SERVER=localhost ENV QBITTORRENT_SERVER=localhost
ENV QBITTORRENT_PORT=8080 ENV QBITTORRENT_PORT=8080
ENV QBITTORRENT_USER=admin ENV CHECK_INTERVAL=45
ENV QBITTORRENT_PASS=adminadmin ENV NAT_LEASE_LIFETIME=60
ENV VPN_GATEWAY=
ENV VPN_CT_NAME=gluetun
ENV VPN_IF_NAME=tun0
ENV CHECK_INTERVAL=300
ENV NAT_LEASE_LIFETIME=300
COPY --from=docker-cli /usr/bin/docker /usr/bin/docker COPY --from=docker-cli /usr/bin/docker /usr/bin/docker
COPY data/start.sh /start.sh COPY data/start.sh /start.sh

View File

@ -1,197 +1,150 @@
#!/usr/bin/env bash #!/usr/bin/env bash
timestamp() { timestamp() {
date '+%Y-%m-%d %H:%M:%S' date '+%Y-%m-%d %H:%M:%S'
}
get_vpn_if_gw() {
local vpn_if_hex_addr=''
local vpn_if_dec_addr=''
local vpn_if_addr=''
local try_ip=''
local vpn_if_gw=''
vpn_if_hex_addr=$(grep "${VPN_IF_NAME}" /proc/net/route | awk '$2 == "00000000" { print $3 }')
if [ -n "${vpn_if_hex_addr}" ]; then
#shellcheck disable=SC2046
vpn_if_dec_addr=$(printf "%d." $(echo "${vpn_if_hex_addr}" | sed 's/../0x& /g' | tr ' ' '\n' | tac) | sed 's/\.$/\n/')
fi
if [ -z "${vpn_if_dec_addr}" ]; then
vpn_if_addr=$(ip addr show dev "${VPN_IF_NAME}" | grep -oP '([0-9]{1,3}[\.]){3}[0-9]{1,3}')
for n in {1..254}; do
try_ip="$(echo "${vpn_if_addr}" | cut -d'.' -f1-3).${n}"
if [ "${try_ip}" != "${vpn_if_addr}" ]; then
if nc -4 -vw1 "${try_ip}" 1 &>/dev/null 2>&1; then
vpn_if_gw=${try_ip}
break
fi
fi
done
if [ -n "${vpn_if_gw}" ]; then
echo "${vpn_if_gw}"
else
return 1
fi
else
echo "${vpn_if_dec_addr}"
fi
} }
getpublicip() { getpublicip() {
# shellcheck disable=SC2086 # shellcheck disable=SC2086
natpmpc -g ${VPN_GATEWAY} | grep -oP '(?<=Public.IP.address.:.).*' natpmpc -g ${VPN_GATEWAY} | grep -oP '(?<=Public.IP.address.:.).*'
} }
findconfiguredport() { findconfiguredport() {
curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "$1" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/preferences" | grep -oP '(?<=\"listen_port\"\:)(\d{1,5})' curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "$1" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/preferences" | grep -oP '(?<=\"listen_port\"\:)(\d{1,5})'
} }
findactiveport() { findactiveport() {
# shellcheck disable=SC2086 # shellcheck disable=SC2086
natpmpc -g ${VPN_GATEWAY} -a 0 0 udp ${NAT_LEASE_LIFETIME} >/dev/null 2>&1 natpmpc -g ${VPN_GATEWAY} -a 0 0 udp ${NAT_LEASE_LIFETIME} >/dev/null 2>&1
# shellcheck disable=SC2086 # shellcheck disable=SC2086
natpmpc -g ${VPN_GATEWAY} -a 0 0 tcp ${NAT_LEASE_LIFETIME} | grep -oP '(?<=Mapped public port.).*(?=.protocol.*)' natpmpc -g ${VPN_GATEWAY} -a 0 0 tcp ${NAT_LEASE_LIFETIME} | grep -oP '(?<=Mapped public port.).*(?=.protocol.*)'
} }
qbt_login() { qbt_login() {
qbt_sid=$(curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --data "username=${QBITTORRENT_USER}" --data-urlencode "password=${QBITTORRENT_PASS}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/auth/login" | grep -oP '(?!set-cookie:.)SID=.*(?=\;.HttpOnly\;)') qbt_sid=$(curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --data "username=${QBITTORRENT_USER}" --data-urlencode "password=${QBITTORRENT_PASS}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/auth/login" | grep -oP '(?!set-cookie:.)SID=.*(?=\;.HttpOnly\;)')
return $? return $?
} }
qbt_changeport(){ qbt_changeport() {
curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "$1" --data-urlencode "json={\"listen_port\":$2,\"random_port\":false,\"upnp\":false}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/setPreferences" >/dev/null 2>&1 curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "$1" --data-urlencode "json={\"listen_port\":$2,\"random_port\":false,\"upnp\":false}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/setPreferences" >/dev/null 2>&1
return $? return $?
} }
qbt_checksid(){ qbt_checksid() {
if curl -s --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "${qbt_sid}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/version" | grep -qi forbidden; then if curl -s --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "${qbt_sid}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/version" | grep -qi forbidden; then
return 1 return 1
else else
return 0 return 0
fi fi
} }
qbt_isreachable(){ qbt_isreachable() {
# shellcheck disable=SC2086 # shellcheck disable=SC2086
nc -4 -vw 5 ${QBITTORRENT_SERVER} ${QBITTORRENT_PORT} &>/dev/null 2>&1 nc -4 -vw 5 ${QBITTORRENT_SERVER} ${QBITTORRENT_PORT} &>/dev/null 2>&1
} }
fw_delrule(){ fw_delrule() {
if (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${configured_port}.*"); then if (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${configured_port}.*"); then
# shellcheck disable=SC2086 # shellcheck disable=SC2086
docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${configured_port} -j ACCEPT docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${configured_port} -j ACCEPT
# shellcheck disable=SC2086 # shellcheck disable=SC2086
docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p udp --dport ${configured_port} -j ACCEPT docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p udp --dport ${configured_port} -j ACCEPT
fi fi
} }
fw_addrule(){ fw_addrule() {
if ! (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${active_port}.*"); then if ! (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${active_port}.*"); then
# shellcheck disable=SC2086 # shellcheck disable=SC2086
docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${active_port} -j ACCEPT docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${active_port} -j ACCEPT
# shellcheck disable=SC2086 # shellcheck disable=SC2086
docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p udp --dport ${active_port} -j ACCEPT docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p udp --dport ${active_port} -j ACCEPT
return 0 return 0
else else
return 1 return 1
fi fi
} }
get_portmap() { get_portmap() {
res=0 res=0
public_ip=$(getpublicip) public_ip=$(getpublicip)
if ! qbt_checksid; then if ! qbt_checksid; then
echo "$(timestamp) | qBittorrent Cookie invalid, getting new SessionID" echo "$(timestamp) | qBittorrent Cookie invalid, getting new SessionID"
if ! qbt_login; then if ! qbt_login; then
echo "$(timestamp) | Failed getting new SessionID from qBittorrent" echo "$(timestamp) | Failed getting new SessionID from qBittorrent"
return 1 return 1
fi fi
else else
echo "$(timestamp) | qBittorrent SessionID Ok!" echo "$(timestamp) | qBittorrent SessionID Ok!"
fi fi
configured_port=$(findconfiguredport "${qbt_sid}") configured_port=$(findconfiguredport "${qbt_sid}")
active_port=$(findactiveport) active_port=$(findactiveport)
echo "$(timestamp) | Public IP: ${public_ip}" echo "$(timestamp) | Public IP: ${public_ip}"
echo "$(timestamp) | Configured Port: ${configured_port}" echo "$(timestamp) | Configured Port: ${configured_port}"
echo "$(timestamp) | Active Port: ${active_port}" echo "$(timestamp) | Active Port: ${active_port}"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if [ ${configured_port} != ${active_port} ]; then if [ ${configured_port} != ${active_port} ]; then
if qbt_changeport "${qbt_sid}" ${active_port}; then if qbt_changeport "${qbt_sid}" ${active_port}; then
if fw_delrule; then if fw_delrule; then
echo "$(timestamp) | IPTables rule deleted for port ${configured_port} on ${VPN_CT_NAME} container" echo "$(timestamp) | IPTables rule deleted for port ${configured_port} on ${VPN_CT_NAME} container"
fi fi
echo "$(timestamp) | Port Changed to: $(findconfiguredport ${qbt_sid})" echo "$(timestamp) | Port Changed to: $(findconfiguredport ${qbt_sid})"
else else
echo "$(timestamp) | Port Change failed." echo "$(timestamp) | Port Change failed."
res=1 res=1
fi fi
else else
echo "$(timestamp) | Port OK (Act: ${active_port} Cfg: ${configured_port})" echo "$(timestamp) | Port OK (Act: ${active_port} Cfg: ${configured_port})"
fi fi
if fw_addrule; then if fw_addrule; then
echo "$(timestamp) | IPTables rule added for port ${active_port} on ${VPN_CT_NAME} container" echo "$(timestamp) | IPTables rule added for port ${active_port} on ${VPN_CT_NAME} container"
fi fi
return $res return $res
}
check_vpn_ct_health() {
while true;
do
if ! docker inspect "${VPN_CT_NAME}" --format='{{json .State.Health.Status}}' | grep -q '"healthy"'; then
echo "$(timestamp) | Waiting for ${VPN_CT_NAME} healthy state.."
sleep 3
else
echo "$(timestamp) | VPN container ${VPN_CT_NAME} in healthy state!"
break
fi
done
} }
pre_reqs() { pre_reqs() {
if [ -z "${VPN_GATEWAY}" ]; then while read -r var; do
VPN_GATEWAY=$(get_vpn_if_gw || echo '') if [ -z "${!var}" ]; then
fi echo "$(timestamp) | ${var} is empty or not set."
while read -r var; do [ "$var" != "QBITTORRENT_USER" ] && [ "$var" != "QBITTORRENT_PASS" ] && exit 1
[ -z "${!var}" ] && { echo "$(timestamp) | ${var} is empty or not set."; exit 1; } fi
done << EOF done <<EOF
QBITTORRENT_SERVER QBITTORRENT_SERVER
QBITTORRENT_PORT QBITTORRENT_PORT
QBITTORRENT_USER QBITTORRENT_USER
QBITTORRENT_PASS QBITTORRENT_PASS
VPN_GATEWAY VPN_GATEWAY
VPN_CT_NAME
VPN_IF_NAME
CHECK_INTERVAL CHECK_INTERVAL
NAT_LEASE_LIFETIME NAT_LEASE_LIFETIME
EOF EOF
[ ! -S /var/run/docker.sock ] && { echo "$(timestamp) | Docker socket doesn't exist or is inaccessible"; exit 2; } [ ! -S /var/run/docker.sock ] && {
echo "$(timestamp) | Docker socket doesn't exist or is inaccessible"
exit 2
}
return 0 return 0
} }
load_vals(){ load_vals() {
public_ip=$(getpublicip) public_ip=$(getpublicip)
if qbt_isreachable; then if qbt_isreachable; then
if qbt_login; then if qbt_login; then
configured_port=$(findconfiguredport "${qbt_sid}") configured_port=$(findconfiguredport "${qbt_sid}")
else else
echo "$(timestamp) | Unable to login to qBittorrent at ${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" echo "$(timestamp) | Unable to login to qBittorrent at ${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}"
exit 7 exit 7
fi fi
else else
echo "$(timestamp) | Unable to reach qBittorrent at ${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" echo "$(timestamp) | Unable to reach qBittorrent at ${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}"
exit 6 exit 6
fi fi
active_port='' active_port=''
} }
public_ip= public_ip=
@ -199,28 +152,33 @@ configured_port=
active_port= active_port=
qbt_sid= qbt_sid=
# Wait for a healthy state on the VPN container pre_reqs && load_vals
check_vpn_ct_health
if pre_reqs; then load_vals; fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
[ -z ${public_ip} ] && { echo "$(timestamp) | Unable to grab VPN Public IP. Please check configuration"; exit 3; } [ -z ${public_ip} ] && {
echo "$(timestamp) | Unable to grab VPN Public IP. Please check configuration"
exit 3
}
# shellcheck disable=SC2086 # shellcheck disable=SC2086
[ -z ${configured_port} ] && { echo "$(timestamp) | qBittorrent configured port value is empty(?). Please check configuration"; exit 4; } [ -z ${configured_port} ] && {
[ -z "${qbt_sid}" ] && { echo "$(timestamp) | Unable to grab qBittorrent SessionID. Please check configuration"; exit 5; } echo "$(timestamp) | qBittorrent configured port value is empty(?). Please check configuration"
exit 4
}
[ -z "${qbt_sid}" ] && {
echo "$(timestamp) | Unable to grab qBittorrent SessionID. Please check configuration"
exit 5
}
while true; while true; do
do if get_portmap; then
if get_portmap; then echo "$(timestamp) | NAT-PMP/UPnP Ok!"
echo "$(timestamp) | NAT-PMP/UPnP Ok!" else
else echo "$(timestamp) | NAT-PMP/UPnP Failed"
echo "$(timestamp) | NAT-PMP/UPnP Failed" fi
fi # shellcheck disable=SC2086
# shellcheck disable=SC2086 echo "$(timestamp) | Sleeping for $(echo ${CHECK_INTERVAL}/60 | bc) minutes"
echo "$(timestamp) | Sleeping for $(echo ${CHECK_INTERVAL}/60 | bc) minutes" # shellcheck disable=SC2086
# shellcheck disable=SC2086 sleep ${CHECK_INTERVAL}
sleep ${CHECK_INTERVAL}
done done
exit $? exit $?

View File

@ -1,96 +0,0 @@
---
services:
gluetun:
# https://github.com/qdm12/gluetun
image: ghcr.io/qdm12/gluetun:latest
container_name: gluetun
# line above must be uncommented to allow external containers to connect. See https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun#external-container-to-gluetun
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- /<yourpath>:/gluetun
# If using ProtonVPN with OpenVPN, this path needs to be set to the downloaded .ovpn file
# - /<yourpath>/<ovpn_config>.udp.ovpn:/gluetun/custom.conf:ro
environment:
# See https://github.com/qdm12/gluetun/wiki
## ProtonVPN Wireguard
- VPN_SERVICE_PROVIDER=custom
- VPN_TYPE=wireguard
- VPN_ENDPOINT_IP=
- VPN_ENDPOINT_PORT=
- WIREGUARD_PUBLIC_KEY=
- WIREGUARD_PRIVATE_KEY=
- WIREGUARD_ADDRESSES=
## ProtonVPN OpenVPN
# - VPN_SERVICE_PROVIDER=custom
# - VPN_TYPE=openvpn
# - OPENVPN_CUSTOM_CONFIG=/gluetun/custom.conf
# See https://protonvpn.com/support/port-forwarding-manual-setup/
# - OPENVPN_USER=<username>+pmp
# - OPENVPN_PASSWORD=
# Timezone for accurate log times
- TZ=Etc/UTC
# Server list updater. See https://github.com/qdm12/gluetun/wiki/Updating-Servers#periodic-update
- UPDATER_PERIOD=
- UPDATER_VPN_SERVICE_PROVIDERS=
# If QBITTORRENT_SERVER address is not related to VPN_IF_NAME (default: tun0) you'll need to set the variable below
# - FIREWALL_OUTBOUND_SUBNETS=172.16.0.0/24
ports:
# - 8888:8888/tcp # HTTP proxy
# - 8388:8388/tcp # Shadowsocks
# - 8388:8388/udp # Shadowsocks
- 8080:8080/tcp # qBittorrent
# networks:
# gluetun-network:
# ipv4_address: 172.16.0.10
qbittorrent:
# https://docs.linuxserver.io/images/docker-qbittorrent
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
volumes:
- /<yourpath>/config:/config
- /<yourpath>/downloads:/downloads
environment:
- TZ=Etc/UTC
network_mode: "service:gluetun"
depends_on:
gluetun:
condition: service_healthy
qbittorrent-natmap:
# https://github.com/soxfor/qbittorrent-natmap
image: ghcr.io/soxfor/qbittorrent-natmap:latest
container_name: qbittorrent-natmap
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- TZ=Etc/UTC
# - QBITTORRENT_SERVER=
# - QBITTORRENT_PORT=8080
# - QBITTORRENT_USER=admin
# - QBITTORRENT_PASS=adminadmin
# - VPN_GATEWAY=
# - VPN_CT_NAME=gluetun
# - VPN_IF_NAME=tun0
# - CHECK_INTERVAL=300
# - NAT_LEASE_LIFETIME=300
network_mode: "service:gluetun"
depends_on:
qbittorrent:
condition: service_started
gluetun:
condition: service_healthy
#networks:
# gluetun-network:
# driver: bridge
# ipam:
# config:
# - subnet: 172.16.0.0/24
# gateway: 172.16.0.254