Persist TB-006 batch 2 media SSO and batch 1 auth fixes to the repo.
Media sites get Caddy forward_auth; Authentik scripts cover all proxy providers. BookStack OIDC and Plane admin routing fixes included, plus ops scripts for external auth on valhalla.
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
#!/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) ==="
|
||||
@@ -0,0 +1,26 @@
|
||||
from pathlib import Path
|
||||
|
||||
path = Path("/lsiopy/lib/python3.12/site-packages/deluge/ui/web/auth.py")
|
||||
text = path.read_text()
|
||||
if "homelab external auth" in text:
|
||||
print("already patched")
|
||||
raise SystemExit(0)
|
||||
|
||||
text = text.replace(
|
||||
" return s.hexdigest() == config['pwd_sha1']",
|
||||
" return True # homelab external auth",
|
||||
1,
|
||||
)
|
||||
old_login = """ if self.check_password(password):
|
||||
log.info('Login success (ClientIP %s)', __request__.getClientIP())
|
||||
return self._create_session(__request__)
|
||||
else:
|
||||
log.error('Login failed (ClientIP %s)', __request__.getClientIP())
|
||||
return False"""
|
||||
new_login = """ log.info('Login success (ClientIP %s) [homelab external auth]', __request__.getClientIP())
|
||||
return self._create_session(__request__)"""
|
||||
if old_login not in text:
|
||||
raise SystemExit("login() pattern not found")
|
||||
text = text.replace(old_login, new_login, 1)
|
||||
path.write_text(text)
|
||||
print("patched auth.py")
|
||||
Reference in New Issue
Block a user