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:
1 parent
fe34f04454
commit
1a257c06fe
1 file changed
+51
-35
+51
-35
@@ -841,64 +841,80 @@ deploy_frankbook_stack() {
|
|||||||
done
|
done
|
||||||
local tmp
|
local tmp
|
||||||
tmp="$(mktemp /tmp/frankbook-vm.XXXXXX)"
|
tmp="$(mktemp /tmp/frankbook-vm.XXXXXX)"
|
||||||
cat > "$tmp" <<'VM_SETUP'
|
cat > "$tmp" <<'VM_SEED'
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SVC="${1:?service required}"
|
# qBittorrent first-boot seed: conf + categories + machine-local PBKDF2
|
||||||
# qBittorrent first-boot seed: conf + categories + machine-local password.
|
# 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
|
if [ ! -e /home/core/Containers/qbittorrent/qbit-seeded ]; then
|
||||||
mkdir -p /Volumes/COMPLETE/qbittorrent-data/qBittorrent
|
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
|
umask 077
|
||||||
printf 'QBIT_WEBUI_PASSWORD=%s\n' "$QBIT_PW" > /home/core/Containers/qbittorrent/env
|
printf 'QBIT_WEBUI_PASSWORD=%s\n' "$QBIT_PW" > /home/core/Containers/qbittorrent/env
|
||||||
umask 022
|
umask 022
|
||||||
{
|
cat > /Volumes/COMPLETE/qbittorrent-data/qBittorrent/qBittorrent.conf <<QCONF
|
||||||
cat <<'QCONF'
|
|
||||||
[AutoRun]
|
|
||||||
|
|
||||||
[BitTorrent]
|
[BitTorrent]
|
||||||
Session\DefaultSavePath=/Volumes/COMPLETE
|
Session\\DefaultSavePath=/Volumes/COMPLETE
|
||||||
Session\Port=6881
|
Session\\Port=6881
|
||||||
Session\QueueingSystemEnabled=true
|
Session\\QueueingSystemEnabled=true
|
||||||
Session\TempPath=/Volumes/INCOMPLETE/incomplete
|
Session\\TempPath=/Volumes/INCOMPLETE/incomplete
|
||||||
Session\TempPathEnabled=true
|
Session\\TempPathEnabled=true
|
||||||
|
|
||||||
[LegalNotice]
|
[LegalNotice]
|
||||||
|
Accepted=true
|
||||||
|
|
||||||
[Meta]
|
[Meta]
|
||||||
MigrationVersion=8
|
MigrationVersion=8
|
||||||
|
|
||||||
[Network]
|
[Network]
|
||||||
Cookies\Enabled=true
|
Cookies\\Enabled=true
|
||||||
|
|
||||||
[Preferences]
|
[Preferences]
|
||||||
WebUI\Address=*
|
WebUI\\Address=*
|
||||||
WebUI\Username=admin
|
WebUI\\ServerDomains=*
|
||||||
|
WebUI\\HostHeaderValidation=false
|
||||||
|
WebUI\\Username=admin
|
||||||
|
WebUI\\Password=$QBIT_HASH
|
||||||
QCONF
|
QCONF
|
||||||
printf 'WebUI\\Password=%s\n' "$QBIT_PW"
|
|
||||||
} > /Volumes/COMPLETE/qbittorrent-data/qBittorrent/qBittorrent.conf
|
|
||||||
cat > /Volumes/COMPLETE/qbittorrent-data/qBittorrent/categories.json <<'QCAT'
|
cat > /Volumes/COMPLETE/qbittorrent-data/qBittorrent/categories.json <<'QCAT'
|
||||||
{
|
{
|
||||||
"Finished Torrents": {
|
"Finished Torrents": {"save_path": "/Volumes/COMPLETE/Finished Torrents"},
|
||||||
"save_path": "/Volumes/COMPLETE/Finished Torrents"
|
"lidarr": {"save_path": "/Volumes/COMPLETE/Finished Torrents"},
|
||||||
},
|
"radarr": {"save_path": "/Volumes/COMPLETE/Finished Torrents"},
|
||||||
"lidarr": {"save_path": ""},
|
"tv-sonarr": {"save_path": "/Volumes/COMPLETE/Finished Torrents"}
|
||||||
"radarr": {"save_path": ""},
|
|
||||||
"tv-sonarr": {"save_path": ""}
|
|
||||||
}
|
}
|
||||||
QCAT
|
QCAT
|
||||||
touch /home/core/Containers/qbittorrent/qbit-seeded
|
touch /home/core/Containers/qbittorrent/qbit-seeded
|
||||||
echo " seeded qBittorrent config (WebUI password: /home/core/Containers/qbittorrent/env inside the VM)"
|
echo " seeded qBittorrent config (WebUI password: /home/core/Containers/qbittorrent/env inside the VM)"
|
||||||
fi
|
fi
|
||||||
if [ "$SVC" = gluetun ] && [ ! -s /home/core/Containers/gluetun/env ]; then
|
VM_SEED
|
||||||
echo "SKIP: gluetun has no credentials yet."
|
podman machine ssh bash -s < "$tmp"
|
||||||
echo " Write the ProtonVPN WireGuard keys into /home/core/Containers/gluetun/env inside the VM"
|
rm -f "$tmp"
|
||||||
echo " (podman machine ssh), then re-run: ./scripts/deploy.sh frankbook-pro --service gluetun"
|
# Credential gate: without a WireGuard env the tunnel stays parked, and
|
||||||
exit 0
|
# qBittorrent (which downloads through it) waits with it. The arrs run
|
||||||
fi
|
# without the tunnel, so only these two services are gated.
|
||||||
if [ "$SVC" = qbittorrent ] && [ ! -s /home/core/Containers/gluetun/env ]; then
|
case "$service" in
|
||||||
echo "SKIP: qbittorrent waits for gluetun credentials."
|
gluetun|qbittorrent)
|
||||||
exit 0
|
if ! podman machine ssh '[ -s /home/core/Containers/gluetun/env ]' >/dev/null 2>&1; then
|
||||||
fi
|
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
|
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}')"
|
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; }
|
[ -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
|
systemctl --user status "$unit" --no-pager -l || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
VM_SETUP
|
VM_START
|
||||||
podman machine ssh bash -s "$service" < "$tmp"
|
podman machine ssh bash -s "$service" < "$tmp"
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
local port path
|
local port path
|
||||||
|
|||||||
Reference in new issue
Block a user