INCOMPLETE — preserved by the PM after the authoring agent hit the session
rate limit mid-task. Do not merge without finishing and gating it.
Problem: qBittorrent 5.1.2 has no WebUI\Username or WebUI\Password_PBKDF2 in
its config, so it mints a NEW temporary admin password on every process start
and announces it only on stdout. Two consequences, both real: the Sonarr and
Radarr download clients configured earlier will silently stop working at the
next pod restart, and a human operator has no stable login either.
Adds a SOPS-encrypted qbittorrent-bootstrap Secret (verified encrypted: both
values are ENC[AES256_GCM]) plus the catalog/rendered wiring to consume it.
WHAT REMAINS:
- The agent's last note: the postStart hook works, but its output never
reaches kubectl logs, so it was switching to the marker-file logging
pattern gitea's hook already uses. That change is NOT here.
- The Secret exists on prod as kubectl-client-side-apply, i.e. live-only;
merging adopts it into git.
- Sonarr's and Radarr's stored download-client passwords were to be updated
to the durable value — NOT done, so the chain may break on restart until
it is.
- The required proof is unfinished: restart the pod, then show the WebUI
accepts the credential AND both arrs still report isValid: true.
Co-Authored-By: Claude Opus 5 <[email protected]>
61 lines
3.7 KiB
YAML
61 lines
3.7 KiB
YAML
# qBittorrent DURABLE WebUI credential (WS-APPS). SOPS-encrypted (age), decrypted in-cluster by Flux.
|
|
#
|
|
# WHY THIS FILE EXISTS (defect, measured on prod 2026-09-13):
|
|
# qBittorrent.conf on the config PVC carried NEITHER `WebUI\Username` NOR
|
|
# `WebUI\Password_PBKDF2`. qBittorrent 5.x treats that as "no credential set" and generates a NEW
|
|
# random `admin` password on EVERY process start, announcing it ONLY on stdout ("A temporary
|
|
# password is provided for this session: ..."). Consequences, both real: (a) Sonarr's and Radarr's
|
|
# saved download-client credentials stop working at the next qbittorrent pod restart, silently
|
|
# breaking Completed Download Handling; (b) a human operator has no stable login at all and must
|
|
# read the pod log after every restart.
|
|
#
|
|
# HOW IT IS APPLIED: catalog/qbittorrent.yaml's app container reads BOTH keys below as env
|
|
# (QBT_WEBUI_USERNAME / QBT_WEBUI_PASSWORD) and its `lifecycle.postStart` hook — the same idiom as
|
|
# gitea's break-glass admin bootstrap — DERIVES the PBKDF2-HMAC-SHA512 digest qBittorrent stores
|
|
# and asserts both keys into /config/qBittorrent/qBittorrent.conf on EVERY container start, then
|
|
# verifies the WebUI actually accepts them. Level-triggered: a pod restart, a config-PVC restore
|
|
# from an older backup, and a rebuild-from-git onto an EMPTY PVC all end with this SAME
|
|
# credential, because the hook re-asserts it on every start rather than once.
|
|
#
|
|
# webui-username: the WebUI account name. qBittorrent has exactly ONE local account, and `admin` is
|
|
# the name its own temporary-password path uses, so keeping it avoids inventing a second identity.
|
|
# webui-password: the durable plaintext. It is ALSO what Sonarr's and Radarr's download-client
|
|
# entries store in their own databases (app state on their config PVCs, NOT in git — see
|
|
# DECISIONS-LOG), so rotating it here means re-pointing both arrs.
|
|
# Deliberately alphanumeric: it travels through a urlencoded WebAPI login body and two arr JSON
|
|
# payloads, and a shell/URL-special character in any of those is a bug waiting to happen.
|
|
#
|
|
# NEVER put a value in a comment: SOPS does NOT encrypt comments.
|
|
# Recover with: infra/devcontainer/dev.sh run sops -d apps/wave-0/qbittorrent-bootstrap-secret.sops.yaml
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: qbittorrent-bootstrap
|
|
namespace: qbittorrent
|
|
labels:
|
|
app.kubernetes.io/name: qbittorrent
|
|
type: Opaque
|
|
stringData:
|
|
webui-username: ENC[AES256_GCM,data:P022nG0=,iv:MXcCXsD5hs7MAJ+OAYHkZ3pXep7XGPqeAExChoqna08=,tag:WE34mxtfQRgiwPRaDA12pQ==,type:str]
|
|
webui-password: ENC[AES256_GCM,data:+Lx0Ny7SBZeF/CzQYm8umntw27ewyXS7unWObpl0fCg=,iv:JkAtublBMS7SJUutAFip1XjdmsKnGe+S2E9QDT4rCJk=,tag:MYptPVgoEUEpXe5kmg1xIg==,type:str]
|
|
sops:
|
|
kms: []
|
|
gcp_kms: []
|
|
azure_kv: []
|
|
hc_vault: []
|
|
age:
|
|
- recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg
|
|
enc: |
|
|
-----BEGIN AGE ENCRYPTED FILE-----
|
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1aWpUY04wZHoyWVdPbGU2
|
|
TC9vaU5oYmhWSVh3YWVTdzkvU3Axdlh6QVFZCkV4MldaZXNyam5CejQyZStyczJ3
|
|
UStFd0hRQmRsNGZLVEVTRnVjWXp6SVkKLS0tICtkWlRqVGN5OWYvVnZUaHYvbEZv
|
|
VWtHMVdDU3hmdXo1TndLb05zQzJ0S28KfqlDBTocO0ZAKtH6ad/00EeVRqZ7WMwv
|
|
FP2evd5vyn95QQICWhY/LH6EKbwVVlcIlL7i9l8K3kK4Ebdk54mAyw==
|
|
-----END AGE ENCRYPTED FILE-----
|
|
lastmodified: "2026-09-13T05:23:23Z"
|
|
mac: ENC[AES256_GCM,data:hQ7jIgFqpNcFY5H4AfMxuslC2R/2wGmGWtghemOmTUUipSdoGdbNjWGQhxzk40lFjw6Zj2b+buOpXpg70s4xr+4feusqa2TJ3+bxAuRWmuZpTQAU2OaO5v0SdDEs3gwKm2mA3JFo/e6POm/P/kf2GIECcyU1AciZQt6ws7UJmQc=,iv:6SF0ZAuzHQThD/FGbWhFfa4dXU8hyW8jWFiDmwgfFU8=,tag:31i8HM2s/mGeS4zvFxO53Q==,type:str]
|
|
pgp: []
|
|
encrypted_regex: ^(data|stringData)$
|
|
version: 3.9.4
|