d387301d6b
## Why The sonarr/radarr/prowlarr images self-generate an API key into /config/config.xml on first boot, so the key is unmanaged and differs per volume reset. This makes Vault the source of truth for those keys (override bootstrap, chosen by Ben): the key is minted in Vault and enforced into config.xml before each app starts. ## Changes - Add a `VaultAuth` `default` in the `arrstack` namespace (kubernetes auth, mount `k8s/au/syd1`, role `default`, SA `default`), mirroring jellyfin. - Add a per-app `VaultStaticSecret` that syncs `kv/kubernetes/namespace/arrstack/default/<app>` (key `apitoken`) into the `<app>-apikey` Secret. The `default` k8s role's templated policy already grants read on that path for the `arrstack/default` SA, so no terraform-vault change is needed. - Add an `apikey-init` initContainer to each of the three deployments that reads `API_KEY` from the VSO-created Secret, fails closed on a missing or non-hex value, and writes/updates only the `<ApiKey>` element in `/config/config.xml` (then fixes ownership 1000:1000, mode 600). Image is a pinned busybox via artifactapi to keep this PR atomic (no new image dependency). - Wire the new manifests into the base and per-app kustomizations. ## Notes - Keys already seeded in Vault at `kv/kubernetes/namespace/arrstack/default/<app>`. - nzbget is out of scope: it has no config.xml `<ApiKey>` (uses ControlPassword), a separate follow-up. - Downstream consumers (proxy, terraform) currently read `kv/service/media-apps/<app>`; the authoritative key now lives at the path above. Reconciliation is deferred. --------- Co-authored-by: unkin-agent <unkin-agent@git.unkin.net> Reviewed-on: #369 Co-authored-by: Unkin Agent <unkin-agent@unkin.net> Co-committed-by: Unkin Agent <unkin-agent@unkin.net>
116 lines
3.4 KiB
YAML
116 lines
3.4 KiB
YAML
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: prowlarr
|
|
namespace: arrstack
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
# RWO config PVC + single stateful SQLite DB: never run two pods at once.
|
|
type: Recreate
|
|
selector:
|
|
matchLabels:
|
|
app: prowlarr
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: prowlarr
|
|
spec:
|
|
securityContext:
|
|
fsGroup: 1000
|
|
fsGroupChangePolicy: OnRootMismatch
|
|
initContainers:
|
|
# Enforce the Vault-sourced API key in /config/config.xml before the app
|
|
# starts. Vault is source of truth (override bootstrap): the key is minted
|
|
# in Vault, synced by VSO into the prowlarr-apikey Secret, and written here.
|
|
# Runs as root to fix ownership; touches only the <ApiKey> element.
|
|
- name: apikey-init
|
|
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0
|
|
imagePullPolicy: IfNotPresent
|
|
securityContext:
|
|
runAsUser: 0
|
|
env:
|
|
- name: API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: prowlarr-apikey
|
|
key: apitoken
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
set -eu
|
|
case "$API_KEY" in
|
|
"" | *[!0-9a-fA-F]*)
|
|
echo "apikey-init: API_KEY missing or not hex; refusing" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
CFG=/config/config.xml
|
|
if [ ! -f "$CFG" ]; then
|
|
printf '<Config>\n <ApiKey>%s</ApiKey>\n</Config>\n' "$API_KEY" > "$CFG"
|
|
elif grep -q '<ApiKey>' "$CFG"; then
|
|
sed -i "s|<ApiKey>[^<]*</ApiKey>|<ApiKey>${API_KEY}</ApiKey>|" "$CFG"
|
|
else
|
|
sed -i "s|<Config>|<Config>\n <ApiKey>${API_KEY}</ApiKey>|" "$CFG"
|
|
fi
|
|
chown 1000:1000 "$CFG"
|
|
chmod 600 "$CFG"
|
|
echo "apikey-init: <ApiKey> enforced from Vault"
|
|
resources:
|
|
requests:
|
|
cpu: 50m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 200m
|
|
memory: 64Mi
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /config
|
|
containers:
|
|
- name: prowlarr
|
|
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/linuxserver/prowlarr:2.5.2
|
|
imagePullPolicy: IfNotPresent
|
|
ports:
|
|
- name: http
|
|
containerPort: 9696
|
|
protocol: TCP
|
|
env:
|
|
- name: PUID
|
|
value: "1000"
|
|
- name: PGID
|
|
value: "1000"
|
|
- name: TZ
|
|
value: Australia/Sydney
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /ping
|
|
port: http
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /ping
|
|
port: http
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: "1"
|
|
memory: 512Mi
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /config
|
|
volumes:
|
|
- name: config
|
|
persistentVolumeClaim:
|
|
claimName: prowlarr-config
|