From 1a257c06fe112817ed22dc58381480feb59ab1db Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 1 Sep 2026 14:22:30 -0700 Subject: [PATCH] frankbook: propagate skip-state before restart, PBKDF2 qB seed The frankbook handler ran port verification even for services the credential gate skipped, so a parked qbittorrent failed the deploy and aborted the five arrs. Gate now lives Mac-side (test -s gluetun/env inside the VM) before restart+verify. The qB first-boot seed wrote a plaintext WebUI\Password, which qB >= 4.2 ignores (PBKDF2 @ByteArray only), and omitted HostHeaderValidation=false, which would 401 the LAN forwarder path. Seed now generates the PBKDF2 hash in the VM (python3 hashlib) and sets ServerDomains=* + HostHeaderValidation=false. Arr categories (lidarr/radarr/tv-sonarr) pre-created with save_path=/Volumes/COMPLETE/Finished Torrents so any grab lands in the review dir regardless of wiring details. --- scripts/deploy-remote.sh | 86 ++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/scripts/deploy-remote.sh b/scripts/deploy-remote.sh index fbbe325..608ee40 100755 --- a/scripts/deploy-remote.sh +++ b/scripts/deploy-remote.sh @@ -841,64 +841,80 @@ deploy_frankbook_stack() { done local tmp tmp="$(mktemp /tmp/frankbook-vm.XXXXXX)" - cat > "$tmp" <<'VM_SETUP' + cat > "$tmp" <<'VM_SEED' set -euo pipefail -SVC="${1:?service required}" -# qBittorrent first-boot seed: conf + categories + machine-local password. +# qBittorrent first-boot seed: conf + categories + machine-local PBKDF2 +# password. qB >= 4.2 ignores plaintext passwords, so the conf carries the +# @ByteArray(salt:hash) form (PBKDF2-HMAC-SHA512, 100k iterations, 64 bytes). if [ ! -e /home/core/Containers/qbittorrent/qbit-seeded ]; then mkdir -p /Volumes/COMPLETE/qbittorrent-data/qBittorrent - QBIT_PW="$(openssl rand -base64 18 | tr -d '/+=')" + QBIT_PW="$(openssl rand -base64 18 | tr -d '/+=' | head -c 20)" + QBIT_HASH="$(python3 - "$QBIT_PW" <<'PYH' +import hashlib, base64, os, sys +pw = sys.argv[1].encode() +salt = os.urandom(16) +h = hashlib.pbkdf2_hmac("sha512", pw, salt, 100000, dklen=64) +print("@ByteArray(%s:%s)" % (base64.b64encode(salt).decode(), base64.b64encode(h).decode())) +PYH +)" umask 077 printf 'QBIT_WEBUI_PASSWORD=%s\n' "$QBIT_PW" > /home/core/Containers/qbittorrent/env umask 022 - { - cat <<'QCONF' -[AutoRun] - + cat > /Volumes/COMPLETE/qbittorrent-data/qBittorrent/qBittorrent.conf < /Volumes/COMPLETE/qbittorrent-data/qBittorrent/qBittorrent.conf cat > /Volumes/COMPLETE/qbittorrent-data/qBittorrent/categories.json <<'QCAT' { - "Finished Torrents": { - "save_path": "/Volumes/COMPLETE/Finished Torrents" - }, - "lidarr": {"save_path": ""}, - "radarr": {"save_path": ""}, - "tv-sonarr": {"save_path": ""} + "Finished Torrents": {"save_path": "/Volumes/COMPLETE/Finished Torrents"}, + "lidarr": {"save_path": "/Volumes/COMPLETE/Finished Torrents"}, + "radarr": {"save_path": "/Volumes/COMPLETE/Finished Torrents"}, + "tv-sonarr": {"save_path": "/Volumes/COMPLETE/Finished Torrents"} } QCAT touch /home/core/Containers/qbittorrent/qbit-seeded echo " seeded qBittorrent config (WebUI password: /home/core/Containers/qbittorrent/env inside the VM)" fi -if [ "$SVC" = gluetun ] && [ ! -s /home/core/Containers/gluetun/env ]; then - echo "SKIP: gluetun has no credentials yet." - echo " Write the ProtonVPN WireGuard keys into /home/core/Containers/gluetun/env inside the VM" - echo " (podman machine ssh), then re-run: ./scripts/deploy.sh frankbook-pro --service gluetun" - exit 0 -fi -if [ "$SVC" = qbittorrent ] && [ ! -s /home/core/Containers/gluetun/env ]; then - echo "SKIP: qbittorrent waits for gluetun credentials." - exit 0 -fi +VM_SEED + podman machine ssh bash -s < "$tmp" + rm -f "$tmp" + # Credential gate: without a WireGuard env the tunnel stays parked, and + # qBittorrent (which downloads through it) waits with it. The arrs run + # without the tunnel, so only these two services are gated. + case "$service" in + gluetun|qbittorrent) + if ! podman machine ssh '[ -s /home/core/Containers/gluetun/env ]' >/dev/null 2>&1; then + echo "SKIP: $service waits for gluetun credentials." + echo " Write the WireGuard env into /home/core/Containers/gluetun/env inside the VM" + echo " (podman machine ssh), then re-run: ./scripts/deploy.sh frankbook-pro --service gluetun --service qbittorrent" + return 0 + fi + ;; + esac + tmp="$(mktemp /tmp/frankbook-vm.XXXXXX)" + cat > "$tmp" <<'VM_START' +set -euo pipefail +SVC="${1:?service required}" systemctl --user daemon-reload unit="$(systemctl --user list-units --all --no-legend "${SVC}.service" "container-${SVC}.service" 2>/dev/null | head -n1 | awk '{print $1}')" [ -n "$unit" ] || { echo "FAIL: no generated systemd unit for $SVC" >&2; exit 1; } @@ -910,7 +926,7 @@ if [ "$state" != active ] && [ "$state" != activating ]; then systemctl --user status "$unit" --no-pager -l || true exit 1 fi -VM_SETUP +VM_START podman machine ssh bash -s "$service" < "$tmp" rm -f "$tmp" local port path