diff --git a/2026/2026-09-25--qbittorrent-bt-net.md b/2026/2026-09-25--qbittorrent-bt-net.md new file mode 100644 index 0000000..a7c31fb --- /dev/null +++ b/2026/2026-09-25--qbittorrent-bt-net.md @@ -0,0 +1,39 @@ +# 2026-09-25 — qBittorrent BT net (flame / firewalled) + +## Goal +Inbound BT listen **6881** tcp+udp reachable so WebUI connection status leaves the firewalled (flame) state. + +## Live on koopa +- CTR `koopa-prime-qbittorrent` (linuxserver): host **6881** tcp+udp, WebUI **9023**→8080 → `bt.hacktivism.ch` +- Conf: `Session\Port=6881`, `PortForwardingEnabled=true`, `Connection\UPnP=true` +- Temp: `/downloads/incomplete/` (abc / host uid 100999) +- Durable apply: `scripts/prime/apply-qbittorrent-bt-net.sh` (+ wrapper `koopa-bt-fix.sh`) +- Clone on host: `~/koopa-admin-log/scripts/prime/` + +## firewalld +- Doc: `configs/firewalld/public-ports.md` — **6881/tcp+udp** +- Agent cannot list without sudo/polkit; human opens with: + ```bash + sudo firewall-cmd --permanent --add-port=6881/tcp + sudo firewall-cmd --permanent --add-port=6881/udp + sudo firewall-cmd --reload + ``` +- WAN probe success (below) implies the path through host firewall is open. + +## VeciGate (2026-09-25) +- wan `dstnat` tcp+udp **6881** → `192.168.100.95:6881` +- hairpin LAN `192.168.100.0/24` and LAN2 `192.168.88.0/24` → `212.51.151.254:6881` tcp+udp +- Comments: `koopa: qBittorrent BT …` / `hairpin: LAN[2] qBittorrent BT …` +- Detail: vecigate-admin-log `2026/2026-09-25--qbittorrent-bt-dnat.md` + +## Verify (2026-09-25, from magikoopa) +| Probe | Result | +|-------|--------| +| `nc -vz 192.168.100.95 6881` | LAN_TCP_OK | +| `nc -vz 212.51.151.254 6881` | WAN_TCP_OK | +| `nc -zu 212.51.151.254 6881` | WAN_UDP_OK | + +WebUI API (`/api/v2/…`) still auth-gated from agent — human glance at connection icon on `bt.hacktivism.ch`. + +## Freigabe +Session Freigabe covered podman conf/restart + VeciGate NAT. No git push of admin-log unless asked. diff --git a/configs/firewalld/public-ports.md b/configs/firewalld/public-ports.md index 2ae7378..e9ce9de 100644 --- a/configs/firewalld/public-ports.md +++ b/configs/firewalld/public-ports.md @@ -18,6 +18,7 @@ sudo firewall-cmd --list-ports | 8080/tcp | Tor OR | — | | 23235/tcp | (legacy list; host SSH is :22, WAN via VeciGate 23235→22) | — | | **9200/tcp** | **Forgejo git-SSH** (podman rootlessport) | **OK** after add+reload | +| **6881/tcp+udp** | qBittorrent BT listen (`koopa-prime-qbittorrent`) | apply: `scripts/prime/apply-qbittorrent-bt-net.sh` | ### Not opened on purpose @@ -61,6 +62,7 @@ Forgejo advertises `SSH_PORT=9200`. Traffic hits **host:9200** (not Caddy). With |------|--------| | 2026-07-09 | 443/tcp re-added for IPv6 | | 2026-07-10 | **9200/tcp** for Forgejo git-SSH (verified LAN + hairpin + SSH auth) | +| 2026-09-25 | **6881/tcp+udp** for qBittorrent BT (`scripts/prime/apply-qbittorrent-bt-net.sh`); VeciGate wan+hairpin DNAT live; WAN/LAN `nc` OK | ## Nym (`koopa-nym`, nym.com) diff --git a/configs/ports.md b/configs/ports.md index 70288db..d8febbe 100644 --- a/configs/ports.md +++ b/configs/ports.md @@ -14,7 +14,8 @@ | 9020 | `koopa-castopod` → `castopod.hacktivism.ch` | | 9021 | `koopa-bonfire` → `bonfire.hacktivism.ch` | | 9022 | `koopa-prime` (Jellyfin) → `prime.hacktivism.ch` | -| 9023 | qBittorrent → `bt.hacktivism.ch` | +| 9023 | qBittorrent WebUI → `bt.hacktivism.ch` | +| **6881** | qBittorrent BT listen (tcp+udp; firewalld + VeciGate DNAT) | | **9024** | `koopa-forgejo` (HTTP) → **`git.hacktivism.ch`** | | **9025** | `koopa-paivana` → `paivana.hacktivism.ch` | | **9026** | `koopa-lemmy-proxy` → `lemmy.hacktivism.ch` | diff --git a/configs/prime/README.md b/configs/prime/README.md index 2b0c287..5a81f79 100644 --- a/configs/prime/README.md +++ b/configs/prime/README.md @@ -6,6 +6,7 @@ | Compose | `compose.yml` (this dir; **no secret values**) | | Jellyfin | **9022** → Caddy `prime.hacktivism.ch` | | qBittorrent | **9023** → Caddy `bt.hacktivism.ch` | +| BT listen | **6881** tcp+udp (firewalld + VeciGate DNAT); apply `scripts/prime/apply-qbittorrent-bt-net.sh` | | Topic | `2026/2026-07-09--koopa-prime-jellyfin.md` | `WEBUI_PASSWORD` is `${WEBUI_PASSWORD}` in the mirror — live value only in diff --git a/scripts/README.md b/scripts/README.md index 659df57..18f1b66 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -17,6 +17,7 @@ | `taler-wallet-cli/` | thin wrappers; **benchmarks live in** `../benchmarks/` | | `castopod/` | host `hernani` podman-compose `~/koopa-castopod` — see `castopod/README.md` | | `lemmy/` | host `hernani` podman-compose `~/koopa-lemmy` — see `configs/lemmy/README.md` | +| `prime/` | qBittorrent BT net apply (`apply-qbittorrent-bt-net.sh`; wrapper `koopa-bt-fix.sh`) — incomplete dir, UPnP/PF, firewalld **6881** | **Secrets:** never in this tree — sibling **`../koopa-admin-secrets`** (`koopa/host-root//` ↔ `/root/` on host; `containers/…/secrets/` for in-container). diff --git a/scripts/prime/apply-qbittorrent-bt-net.sh b/scripts/prime/apply-qbittorrent-bt-net.sh new file mode 100755 index 0000000..fab7fdb --- /dev/null +++ b/scripts/prime/apply-qbittorrent-bt-net.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# Apply qBittorrent BT net fixes on koopa (flame / firewalled inbound). +# +# Supersedes session one-shot /tmp/koopa-bt-fix.sh (2026-09-25). +# +# On koopa (hernani; firewalld needs sudo): +# bash /path/to/koopa-admin-log/scripts/prime/apply-qbittorrent-bt-net.sh +# # or after clone: +# bash ~/koopa-admin-log/scripts/prime/apply-qbittorrent-bt-net.sh +# +# What it does: +# 1) /downloads/incomplete/ as container user abc (host uid 100999) +# 2) PortForwardingEnabled + Connection\UPnP = true (edit inside container) +# 3) firewalld public: 6881/tcp + 6881/udp (sudo) +# 4) podman restart koopa-prime-qbittorrent + STATUS= lines +# +# WAN inbound: VeciGate DNAT 6881 tcp+udp → koopa:6881 (+ hairpin LAN/LAN2) +# applied 2026-09-25 (comment~qBittorrent). Re-check with script footer STATUS. +# +# Freigabe: podman + firewalld (session Freigabe required for live apply). +set -euo pipefail + +CTR=koopa-prime-qbittorrent +CONF_IN=/config/qBittorrent/qBittorrent.conf +BT_PORT="${BT_PORT:-6881}" +STAMP="$(date +%Y%m%d-%H%M%S)" + +die() { echo "ERROR: $*" >&2; exit 1; } + +command -v podman >/dev/null || die "podman missing" +podman container exists "$CTR" || die "container $CTR missing" + +echo "===1_INCOMPLETE===" +podman exec -u abc "$CTR" sh -c ' + set -e + mkdir -p /downloads/incomplete + ls -lad /downloads /downloads/incomplete + touch /downloads/incomplete/.writeok && rm /downloads/incomplete/.writeok + echo incomplete_write=OK +' + +echo "===2_CONF_UPNP_PF===" +# Host hernani cannot write config (owned by mapped abc/100999). Edit as abc. +podman exec -u abc "$CTR" sh -c " + set -e + CONF='$CONF_IN' + test -f \"\$CONF\" || { echo missing_conf; exit 1; } + cp -a \"\$CONF\" \"\${CONF}.bak-btfix-$STAMP\" + echo bak=\${CONF}.bak-btfix-$STAMP + python3 - \"\$CONF\" <<'PY' +import sys +from pathlib import Path +p = Path(sys.argv[1]) +text = p.read_text() +orig = text +repls = [ + ('PortForwardingEnabled=false', 'PortForwardingEnabled=true'), + ('Connection\\\\UPnP=false', 'Connection\\\\UPnP=true'), +] +changed = False +for a, b in repls: + if a in text: + text = text.replace(a, b) + changed = True +if not changed: + # Already true, or unexpected keys — report and continue if already OK + keys = [ln for ln in text.splitlines() if 'UPnP' in ln or 'PortForward' in ln] + if any('=true' in ln for ln in keys) and not any('=false' in ln for ln in keys if 'UPnP' in ln or 'PortForward' in ln): + print('already_ok') + for ln in keys: + print(ln) + raise SystemExit(0) + print('no_keys_replaced') + for ln in keys: + print(ln) + raise SystemExit(2) +p.write_text(text) +print('replaced_ok') +for ln in text.splitlines(): + if 'UPnP' in ln or 'PortForward' in ln: + print(ln) +PY +" + +echo "===3_FIREWALLD===" +fw_ok=0 +if command -v firewall-cmd >/dev/null 2>&1; then + if sudo -n true 2>/dev/null; then + SUDO=(sudo -n) + elif [[ "$(id -u)" -eq 0 ]]; then + SUDO=() + else + # Interactive sudo (Freigabe path) + SUDO=(sudo) + fi + if "${SUDO[@]}" firewall-cmd --permanent --add-port="${BT_PORT}/tcp" \ + && "${SUDO[@]}" firewall-cmd --permanent --add-port="${BT_PORT}/udp" \ + && "${SUDO[@]}" firewall-cmd --reload; then + echo "firewalld_ports=$("${SUDO[@]}" firewall-cmd --list-ports 2>/dev/null || true)" + echo "firewalld=OK" + fw_ok=1 + else + echo "firewalld=FAIL" + fi +else + echo "firewalld=MISSING_CMD" +fi +if [[ "$fw_ok" -ne 1 ]]; then + echo "HINT: sudo firewall-cmd --permanent --add-port=${BT_PORT}/tcp" + echo "HINT: sudo firewall-cmd --permanent --add-port=${BT_PORT}/udp" + echo "HINT: sudo firewall-cmd --reload" +fi + +echo "===4_RESTART===" +podman restart "$CTR" +sleep 4 +podman ps --filter "name=$CTR" --format '{{.Names}} {{.Status}} {{.Ports}}' + +echo "===5_POST===" +podman exec -u abc "$CTR" sh -c " + grep -E 'UPnP|PortForward' $CONF_IN || true + ls -lad /downloads/incomplete +" +ss -lntup 2>/dev/null | grep -E "${BT_PORT}|9023" || true +ss -lnup 2>/dev/null | grep "${BT_PORT}" || true + +echo "STATUS=apply_done ctr=$CTR bt_port=$BT_PORT firewalld_ok=$fw_ok stamp=$STAMP" +# VeciGate DNAT live 2026-09-25: wan dstnat tcp+udp 6881 → 192.168.100.95:6881 +# + hairpin LAN 192.168.100.0/24 and LAN2 192.168.88.0/24 to 212.51.151.254:6881 +echo "STATUS=vecigate_dnat live wan+hairpin LAN/LAN2 → 192.168.100.95:$BT_PORT (comment~qBittorrent)" +echo "STATUS=verify WAN: nc -vz 212.51.151.254 $BT_PORT ; LAN: nc -vz 192.168.100.95 $BT_PORT" +echo "STATUS=verify WebUI connection icon (flame=firewalled) at bt.hacktivism.ch" diff --git a/scripts/prime/koopa-bt-fix.sh b/scripts/prime/koopa-bt-fix.sh new file mode 100755 index 0000000..18891b0 --- /dev/null +++ b/scripts/prime/koopa-bt-fix.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Compat wrapper: session name koopa-bt-fix.sh → durable apply. +exec "$(cd "$(dirname "$0")" && pwd)/apply-qbittorrent-bt-net.sh" "$@"