#!/usr/bin/env bash set -euo pipefail # TB-006 batch 2: disable local auth; Servarr -> External; skip tautulli. # Run on valhalla as ginnoir. set_servarr_external() { local app="$1" local cfg="/config/${app}/config.xml" if [[ ! -f "$cfg" ]]; then echo "SKIP ${app}: no ${cfg}" return 0 fi echo "=== ${app}: set External ===" docker stop "$app" >/dev/null python3 - "$cfg" <<'PY' import sys import xml.etree.ElementTree as ET path = sys.argv[1] tree = ET.parse(path) root = tree.getroot() auth_required = None for el in list(root): if el.tag == "AuthenticationRequired" and el.text: auth_required = el.text.strip() break for el in list(root): if el.tag in ("AuthenticationMethod", "AuthenticationRequired"): root.remove(el) ext = ET.SubElement(root, "AuthenticationMethod") ext.text = "External" req = ET.SubElement(root, "AuthenticationRequired") req.text = auth_required or "Enabled" tree.write(path, encoding="UTF-8", xml_declaration=True) print(f" wrote External (AuthenticationRequired={req.text})") PY docker start "$app" >/dev/null grep Authentication "$cfg" } for app in sonarr radarr prowlarr whisparr; do set_servarr_external "$app" done echo "=== bazarr: verify auth disabled ===" python3 - <<'PY' import yaml path = "/config/bazarr/config/config.yaml" with open(path) as f: d = yaml.safe_load(f) auth = d.setdefault("auth", {}) changed = False if auth.get("type") is not None: auth["type"] = None changed = True if auth.get("username"): auth["username"] = "" changed = True if auth.get("password"): auth["password"] = "" changed = True if changed: with open(path, "w") as f: yaml.safe_dump(d, f, default_flow_style=False, sort_keys=False) print(" updated bazarr auth -> type null") else: print(" already type null / no credentials") PY docker restart bazarr >/dev/null echo "=== qbittorrent: disable WebUI auth (keep API key for *arr) ===" QCONF="/config/qbittorrent/qBittorrent/qBittorrent.conf" docker stop qbittorrent >/dev/null python3 - "$QCONF" <<'PY' from pathlib import Path import sys path = Path(sys.argv[1]) lines = path.read_text().splitlines() out = [] added = False for line in lines: if line.startswith("WebUI\\AuthenticationEnabled="): out.append("WebUI\\AuthenticationEnabled=false") added = True continue out.append(line) if not added: final = [] for line in out: final.append(line) if line.strip() == "[Preferences]": final.append("WebUI\\AuthenticationEnabled=false") added = True out = final path.write_text("\n".join(out) + "\n") print(" WebUI\\AuthenticationEnabled=false") PY grep 'WebUI\\AuthenticationEnabled' "$QCONF" || true docker start qbittorrent >/dev/null echo "=== nzbget: disable form auth (API creds unchanged) ===" NZB="/config/nzbget/nzbget.conf" sed -i 's/^FormAuth=.*/FormAuth=no/' "$NZB" sed -i 's/^SecureControl=.*/SecureControl=no/' "$NZB" grep -E '^(FormAuth|SecureControl)=' "$NZB" docker restart nzbget >/dev/null echo "=== stash: external authwall (Authentik + internal_only) ===" python3 - <<'PY' from pathlib import Path import re path = Path("/config/stash/config.yml") text = path.read_text() text = re.sub(r'^dangerous_allow_public_without_auth:.*$', 'dangerous_allow_public_without_auth: "true"', text, flags=re.M) text = re.sub(r'^username:.*$', 'username: ""', text, flags=re.M) text = re.sub(r'^password:.*$', 'password: ""', text, flags=re.M) text = re.sub(r'^security_tripwire_accessed_from_public_internet:.*\n', '', text, flags=re.M) path.write_text(text) print(" dangerous_allow_public_without_auth=true, cleared username/password") PY docker restart stash >/dev/null echo "=== deluge: bypass web login (Authentik at edge; patch re-applies on recreate) ===" if docker exec deluge grep -q 'homelab external auth' /lsiopy/lib/python3.12/site-packages/deluge/ui/web/auth.py 2>/dev/null; then echo " already patched" else docker cp /tmp/patch-deluge-auth.py deluge:/tmp/patch-deluge-auth.py docker exec deluge python3 /tmp/patch-deluge-auth.py docker restart deluge >/dev/null echo " patched and restarted" fi echo "=== done (tautulli untouched) ==="