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.
This commit is contained in:
Frank
2026-09-01 14:22:30 -07:00
parent fe34f04454
commit 1a257c06fe
+51 -35
View File
@@ -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 <<QCONF
[BitTorrent]
Session\DefaultSavePath=/Volumes/COMPLETE
Session\Port=6881
Session\QueueingSystemEnabled=true
Session\TempPath=/Volumes/INCOMPLETE/incomplete
Session\TempPathEnabled=true
Session\\DefaultSavePath=/Volumes/COMPLETE
Session\\Port=6881
Session\\QueueingSystemEnabled=true
Session\\TempPath=/Volumes/INCOMPLETE/incomplete
Session\\TempPathEnabled=true
[LegalNotice]
Accepted=true
[Meta]
MigrationVersion=8
[Network]
Cookies\Enabled=true
Cookies\\Enabled=true
[Preferences]
WebUI\Address=*
WebUI\Username=admin
WebUI\\Address=*
WebUI\\ServerDomains=*
WebUI\\HostHeaderValidation=false
WebUI\\Username=admin
WebUI\\Password=$QBIT_HASH
QCONF
printf 'WebUI\\Password=%s\n' "$QBIT_PW"
} > /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