From 4119c959c07242df98a7e3ece177bcd4c01d1591 Mon Sep 17 00:00:00 2001 From: Luca Bilke Date: Wed, 8 Nov 2023 15:31:49 +0100 Subject: [PATCH] initial rework --- Dockerfile | 13 +-- data/start.sh | 284 +++++++++++++++++++-------------------------- docker-compose.yml | 96 --------------- 3 files changed, 125 insertions(+), 268 deletions(-) delete mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index becef21..7b8f0b9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,10 +16,10 @@ RUN apt-get update && \ 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 description="Map port via NAT-PMP and update qBittorrent configuration" -LABEL version="1.1.0" +LABEL version="2.0.0" ARG DEBIAN_FRONTEND=noninteractive @@ -47,13 +47,8 @@ RUN sed -i "/${LANG}/s/^# //g" /etc/locale.gen && \ ENV QBITTORRENT_SERVER=localhost ENV QBITTORRENT_PORT=8080 -ENV QBITTORRENT_USER=admin -ENV QBITTORRENT_PASS=adminadmin -ENV VPN_GATEWAY= -ENV VPN_CT_NAME=gluetun -ENV VPN_IF_NAME=tun0 -ENV CHECK_INTERVAL=300 -ENV NAT_LEASE_LIFETIME=300 +ENV CHECK_INTERVAL=45 +ENV NAT_LEASE_LIFETIME=60 COPY --from=docker-cli /usr/bin/docker /usr/bin/docker COPY data/start.sh /start.sh diff --git a/data/start.sh b/data/start.sh index db10008..1933a32 100755 --- a/data/start.sh +++ b/data/start.sh @@ -1,197 +1,150 @@ #!/usr/bin/env bash timestamp() { - 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 + date '+%Y-%m-%d %H:%M:%S' } getpublicip() { - # shellcheck disable=SC2086 - natpmpc -g ${VPN_GATEWAY} | grep -oP '(?<=Public.IP.address.:.).*' + # shellcheck disable=SC2086 + natpmpc -g ${VPN_GATEWAY} | grep -oP '(?<=Public.IP.address.:.).*' } 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() { - # shellcheck disable=SC2086 - natpmpc -g ${VPN_GATEWAY} -a 0 0 udp ${NAT_LEASE_LIFETIME} >/dev/null 2>&1 - # shellcheck disable=SC2086 - natpmpc -g ${VPN_GATEWAY} -a 0 0 tcp ${NAT_LEASE_LIFETIME} | grep -oP '(?<=Mapped public port.).*(?=.protocol.*)' + # shellcheck disable=SC2086 + natpmpc -g ${VPN_GATEWAY} -a 0 0 udp ${NAT_LEASE_LIFETIME} >/dev/null 2>&1 + # shellcheck disable=SC2086 + natpmpc -g ${VPN_GATEWAY} -a 0 0 tcp ${NAT_LEASE_LIFETIME} | grep -oP '(?<=Mapped public port.).*(?=.protocol.*)' } 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\;)') - return $? + 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 $? } -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 - return $? +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 + return $? } -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 - return 1 - else - return 0 - fi +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 + return 1 + else + return 0 + fi } -qbt_isreachable(){ - # shellcheck disable=SC2086 - nc -4 -vw 5 ${QBITTORRENT_SERVER} ${QBITTORRENT_PORT} &>/dev/null 2>&1 +qbt_isreachable() { + # shellcheck disable=SC2086 + nc -4 -vw 5 ${QBITTORRENT_SERVER} ${QBITTORRENT_PORT} &>/dev/null 2>&1 } -fw_delrule(){ - if (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${configured_port}.*"); then - # shellcheck disable=SC2086 - docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${configured_port} -j ACCEPT - # shellcheck disable=SC2086 - docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p udp --dport ${configured_port} -j ACCEPT - fi +fw_delrule() { + if (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${configured_port}.*"); then + # shellcheck disable=SC2086 + docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${configured_port} -j ACCEPT + # shellcheck disable=SC2086 + docker exec "${VPN_CT_NAME}" /sbin/iptables -D INPUT -i "${VPN_IF_NAME}" -p udp --dport ${configured_port} -j ACCEPT + fi } -fw_addrule(){ - if ! (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${active_port}.*"); then - # shellcheck disable=SC2086 - docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${active_port} -j ACCEPT - # shellcheck disable=SC2086 - docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p udp --dport ${active_port} -j ACCEPT - return 0 - else - return 1 - fi +fw_addrule() { + if ! (docker exec "${VPN_CT_NAME}" /sbin/iptables -L INPUT -n | grep -qP "^ACCEPT.*${active_port}.*"); then + # shellcheck disable=SC2086 + docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p tcp --dport ${active_port} -j ACCEPT + # shellcheck disable=SC2086 + docker exec "${VPN_CT_NAME}" /sbin/iptables -A INPUT -i "${VPN_IF_NAME}" -p udp --dport ${active_port} -j ACCEPT + return 0 + else + return 1 + fi } get_portmap() { - res=0 - public_ip=$(getpublicip) + res=0 + public_ip=$(getpublicip) - if ! qbt_checksid; then - echo "$(timestamp) | qBittorrent Cookie invalid, getting new SessionID" - if ! qbt_login; then - echo "$(timestamp) | Failed getting new SessionID from qBittorrent" - return 1 - fi - else - echo "$(timestamp) | qBittorrent SessionID Ok!" - fi + if ! qbt_checksid; then + echo "$(timestamp) | qBittorrent Cookie invalid, getting new SessionID" + if ! qbt_login; then + echo "$(timestamp) | Failed getting new SessionID from qBittorrent" + return 1 + fi + else + echo "$(timestamp) | qBittorrent SessionID Ok!" + fi - configured_port=$(findconfiguredport "${qbt_sid}") - active_port=$(findactiveport) + configured_port=$(findconfiguredport "${qbt_sid}") + active_port=$(findactiveport) - echo "$(timestamp) | Public IP: ${public_ip}" - echo "$(timestamp) | Configured Port: ${configured_port}" - echo "$(timestamp) | Active Port: ${active_port}" + echo "$(timestamp) | Public IP: ${public_ip}" + echo "$(timestamp) | Configured Port: ${configured_port}" + echo "$(timestamp) | Active Port: ${active_port}" - # shellcheck disable=SC2086 - if [ ${configured_port} != ${active_port} ]; then - if qbt_changeport "${qbt_sid}" ${active_port}; then - if fw_delrule; then - echo "$(timestamp) | IPTables rule deleted for port ${configured_port} on ${VPN_CT_NAME} container" - fi - echo "$(timestamp) | Port Changed to: $(findconfiguredport ${qbt_sid})" - else - echo "$(timestamp) | Port Change failed." - res=1 - fi - else - echo "$(timestamp) | Port OK (Act: ${active_port} Cfg: ${configured_port})" - fi + # shellcheck disable=SC2086 + if [ ${configured_port} != ${active_port} ]; then + if qbt_changeport "${qbt_sid}" ${active_port}; then + if fw_delrule; then + echo "$(timestamp) | IPTables rule deleted for port ${configured_port} on ${VPN_CT_NAME} container" + fi + echo "$(timestamp) | Port Changed to: $(findconfiguredport ${qbt_sid})" + else + echo "$(timestamp) | Port Change failed." + res=1 + fi + else + echo "$(timestamp) | Port OK (Act: ${active_port} Cfg: ${configured_port})" + fi - if fw_addrule; then - echo "$(timestamp) | IPTables rule added for port ${active_port} on ${VPN_CT_NAME} container" - fi + if fw_addrule; then + echo "$(timestamp) | IPTables rule added for port ${active_port} on ${VPN_CT_NAME} container" + fi - 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 + return $res } pre_reqs() { - if [ -z "${VPN_GATEWAY}" ]; then - VPN_GATEWAY=$(get_vpn_if_gw || echo '') - fi -while read -r var; do - [ -z "${!var}" ] && { echo "$(timestamp) | ${var} is empty or not set."; exit 1; } -done << EOF + while read -r var; do + if [ -z "${!var}" ]; then + echo "$(timestamp) | ${var} is empty or not set." + [ "$var" != "QBITTORRENT_USER" ] && [ "$var" != "QBITTORRENT_PASS" ] && exit 1 + fi + done <:/gluetun - # If using ProtonVPN with OpenVPN, this path needs to be set to the downloaded .ovpn file - # - //.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=+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: - - //config:/config - - //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 \ No newline at end of file