Compare commits
No commits in common. "d32600aaa262cb4be35a9a3af492c3ce6a2e60d6" and "bb90021adcf5f06ed5c12ff2a37f7bb985623cb4" have entirely different histories.
d32600aaa2
...
bb90021adc
2 changed files with 0 additions and 192 deletions
|
|
@ -1,87 +0,0 @@
|
||||||
# FileFlows ApiKey Guard
|
|
||||||
|
|
||||||
## Problem
|
|
||||||
|
|
||||||
Die FileFlows-API liefert `ApiKey`-Felder in Flow-Parts beim `GET` grundsätzlich
|
|
||||||
als Literal `[REDACTED]` aus. Wird ein Flow danach gespeichert — aus der Web-UI
|
|
||||||
oder über einen `GET → PUT`-Roundtrip (Scripts, Automationen) — landet dieser
|
|
||||||
Platzhalter in der Datenbank.
|
|
||||||
|
|
||||||
Folge: die Nodes `Sonarr - Trigger Manual Import` und
|
|
||||||
`Radarr - Trigger Manual Import` authentisieren sich mit `[REDACTED]`, die Arr
|
|
||||||
antwortet **401**, der Node bricht **still** ab.
|
|
||||||
|
|
||||||
Fehlerbild in der Kette:
|
|
||||||
|
|
||||||
```
|
|
||||||
SABnzbd (movetdarr.sh) → FileFlows → Arr Manual Import
|
|
||||||
↑ 401, still
|
|
||||||
```
|
|
||||||
|
|
||||||
- n8n Media-Handoff bleibt auf `processing` stehen
|
|
||||||
- `movetdarr.sh` pollt den Handoff-Lease weiter → SAB-Job hängt in der Queue
|
|
||||||
bis zum 24-h-Timeout
|
|
||||||
- Symptom beim Nutzer: "hängt seit Stunden in der Queue"
|
|
||||||
|
|
||||||
**Ein einziger UI-Save zerschießt alle vier Keys gleichzeitig**, weil der GET
|
|
||||||
alle vier als `[REDACTED]` ausliefert (verifiziert per Negativtest: 1 Key
|
|
||||||
sabotiert → 4 Keys in der DB defekt).
|
|
||||||
|
|
||||||
## Lösung
|
|
||||||
|
|
||||||
`ff-apikey-guard.sh` prüft alle Flows in der FileFlows-SQLite auf ApiKeys mit
|
|
||||||
Länge != 32 und schreibt die echten Keys via Butler-Proxy
|
|
||||||
(`PUT /fileflows/api/flow`) zurück.
|
|
||||||
|
|
||||||
Keys kommen aus einer lokalen Map, nie aus Git.
|
|
||||||
|
|
||||||
## Deployment (tdarr, 10.2.1.104)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo install -m 750 fileflows/guard/ff-apikey-guard.sh \
|
|
||||||
/app-config/ff-guard/ff-apikey-guard.sh
|
|
||||||
|
|
||||||
# Butler-Token
|
|
||||||
printf '%s' '<butler-token>' | sudo tee /app-config/ff-guard/butler.token
|
|
||||||
sudo chmod 600 /app-config/ff-guard/butler.token
|
|
||||||
|
|
||||||
# Key-Map: "<port> <apikey>" pro Zeile, Keys aus den Arr-config.xml auf 10.2.1.100
|
|
||||||
# 8989 sonarrUHD / 8990 sonarrFHD / 7878 radarrUHD / 7879 radarrFHD
|
|
||||||
sudo chmod 600 /app-config/ff-guard/arr-keys.map
|
|
||||||
```
|
|
||||||
|
|
||||||
Läuft als systemd-Timer alle 15 Minuten (`cron` ist auf tdarr inaktiv):
|
|
||||||
|
|
||||||
```
|
|
||||||
/etc/systemd/system/ff-apikey-guard.service Type=oneshot
|
|
||||||
/etc/systemd/system/ff-apikey-guard.timer OnUnitActiveSec=15min
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable --now ff-apikey-guard.timer
|
|
||||||
```
|
|
||||||
|
|
||||||
## Betrieb
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Log
|
|
||||||
sudo tail /app-config/ff-guard/guard.log
|
|
||||||
|
|
||||||
# Manuell prüfen/reparieren
|
|
||||||
sudo systemctl start ff-apikey-guard.service
|
|
||||||
|
|
||||||
# Timer-Status
|
|
||||||
systemctl list-timers ff-apikey-guard.timer
|
|
||||||
```
|
|
||||||
|
|
||||||
Logzeilen (Datum TT.MM.JJJJ):
|
|
||||||
|
|
||||||
- `OK: alle Flow-ApiKeys 32 Zeichen` — nichts zu tun
|
|
||||||
- `WARNUNG: defekte ApiKeys in Flow <uid> — repariere`
|
|
||||||
- `Flow <uid> repariert: N Key(s) zurueckgeschrieben (HTTP 200)`
|
|
||||||
|
|
||||||
## Nach einem UI-Save immer prüfen
|
|
||||||
|
|
||||||
Wer einen Flow in der FileFlows-UI speichert, sollte danach den Guard anstoßen
|
|
||||||
oder maximal 15 Minuten auf den Timer warten. Vorher schlagen alle
|
|
||||||
Arr-Importe still fehl.
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# ff-apikey-guard.sh — repariert kaputte Arr-ApiKeys in FileFlows-Flows
|
|
||||||
#
|
|
||||||
# URSACHE: Die FileFlows-API liefert ApiKey-Felder im GET als "[REDACTED]".
|
|
||||||
# Wird ein Flow aus der Web-UI (oder per GET->PUT-Roundtrip) gespeichert,
|
|
||||||
# landet dieser Platzhalter in der DB. Die "Trigger Manual Import"-Nodes
|
|
||||||
# schlagen dann still fehl (Arr antwortet 401), der n8n-Handoff bleibt auf
|
|
||||||
# "processing" und der SAB-Job haengt bis zum 24h-Timeout in der Queue.
|
|
||||||
#
|
|
||||||
# Dieses Skript erkennt ApiKeys != 32 Zeichen und schreibt die echten Keys
|
|
||||||
# aus /app-config/ff-guard/arr-keys.map zurueck (Format: "<port> <key>").
|
|
||||||
set -u
|
|
||||||
|
|
||||||
BUTLER="${BUTLER:-http://10.5.85.2:8888}"
|
|
||||||
GUARD_DIR="${GUARD_DIR:-/app-config/ff-guard}"
|
|
||||||
TOKEN_FILE="$GUARD_DIR/butler.token"
|
|
||||||
KEYMAP="$GUARD_DIR/arr-keys.map"
|
|
||||||
LOG="$GUARD_DIR/guard.log"
|
|
||||||
DB="${DB:-/app-config/fileflows-data/Data/FileFlows.sqlite}"
|
|
||||||
|
|
||||||
mkdir -p "$GUARD_DIR"
|
|
||||||
log() { echo "$(date '+%d.%m.%Y %H:%M:%S'): $1" >> "$LOG"; }
|
|
||||||
|
|
||||||
for f in "$TOKEN_FILE" "$KEYMAP"; do
|
|
||||||
[ -r "$f" ] || { log "FEHLER: Datei fehlt oder nicht lesbar: $f"; exit 1; }
|
|
||||||
done
|
|
||||||
[ -r "$DB" ] || { log "FEHLER: DB nicht lesbar: $DB"; exit 1; }
|
|
||||||
|
|
||||||
TOKEN="$(tr -d '\n\r' < "$TOKEN_FILE")"
|
|
||||||
|
|
||||||
# Flows mit defekten Keys ermitteln
|
|
||||||
FLOWUIDS="$(python3 - "$DB" <<'PY'
|
|
||||||
import sqlite3, json, sys
|
|
||||||
con = sqlite3.connect("file:%s?mode=ro" % sys.argv[1], uri=True)
|
|
||||||
for uid, name, typ, d in con.execute(
|
|
||||||
"select Uid,Name,Type,Data from DbObject where Type like '%Flow%'"):
|
|
||||||
try:
|
|
||||||
o = json.loads(d)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
for p in o.get("Parts", []):
|
|
||||||
k = (p.get("Model") or {}).get("ApiKey")
|
|
||||||
if k is not None and len(str(k)) != 32:
|
|
||||||
print(uid)
|
|
||||||
break
|
|
||||||
PY
|
|
||||||
)"
|
|
||||||
|
|
||||||
if [ -z "$FLOWUIDS" ]; then
|
|
||||||
log "OK: alle Flow-ApiKeys 32 Zeichen"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
RC=0
|
|
||||||
for uid in $FLOWUIDS; do
|
|
||||||
log "WARNUNG: defekte ApiKeys in Flow $uid — repariere"
|
|
||||||
OUT="$(mktemp)"; PATCHED="$(mktemp)"
|
|
||||||
|
|
||||||
if ! curl -sf -m 60 -H "Authorization: Bearer $TOKEN" \
|
|
||||||
"$BUTLER/fileflows/api/flow/$uid" -o "$OUT"; then
|
|
||||||
log "FEHLER: GET flow $uid fehlgeschlagen"
|
|
||||||
rm -f "$OUT" "$PATCHED"; RC=1; continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
CHANGED="$(python3 - "$OUT" "$KEYMAP" "$PATCHED" <<'PY'
|
|
||||||
import json, sys
|
|
||||||
flow = json.load(open(sys.argv[1]))
|
|
||||||
keys = {}
|
|
||||||
for line in open(sys.argv[2]):
|
|
||||||
parts = line.split()
|
|
||||||
if len(parts) == 2 and len(parts[1]) == 32:
|
|
||||||
keys[parts[0]] = parts[1]
|
|
||||||
n = 0
|
|
||||||
for p in flow.get("Parts", []):
|
|
||||||
mod = p.get("Model") or {}
|
|
||||||
if "ApiKey" not in mod:
|
|
||||||
continue
|
|
||||||
url = str(mod.get("URL") or mod.get("ApiUrl") or "")
|
|
||||||
port = url.rstrip("/").rsplit(":", 1)[-1]
|
|
||||||
if port in keys and str(mod["ApiKey"]) != keys[port]:
|
|
||||||
mod["ApiKey"] = keys[port]
|
|
||||||
n += 1
|
|
||||||
json.dump(flow, open(sys.argv[3], "w"))
|
|
||||||
print(n)
|
|
||||||
PY
|
|
||||||
)"
|
|
||||||
|
|
||||||
if [ "${CHANGED:-0}" = "0" ]; then
|
|
||||||
log "WARNUNG: Flow $uid — kein Key konnte gemappt werden (Ports pruefen)"
|
|
||||||
rm -f "$OUT" "$PATCHED"; RC=1; continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
HTTP="$(curl -s -m 60 -o /dev/null -w '%{http_code}' -X PUT \
|
|
||||||
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
|
|
||||||
"$BUTLER/fileflows/api/flow" --data @"$PATCHED")"
|
|
||||||
|
|
||||||
if [ "$HTTP" = "200" ]; then
|
|
||||||
log "Flow $uid repariert: $CHANGED Key(s) zurueckgeschrieben (HTTP 200)"
|
|
||||||
else
|
|
||||||
log "FEHLER: PUT flow $uid -> HTTP $HTTP"
|
|
||||||
RC=1
|
|
||||||
fi
|
|
||||||
rm -f "$OUT" "$PATCHED"
|
|
||||||
done
|
|
||||||
exit $RC
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue