1ecba543aa
## Why
The old bare `sonarr`/`radarr` Services are gone, but arrproxy still ran the v0.3.x built-in default topology pointing at non-existent `sonarr`/`radarr` upstreams and mounted dead `sonarr-apikey`/`radarr-apikey` Secrets. This cuts the front door over to arrproxy v0.4.0 tier routing so the adult (fafflix, `/3aa168`) and kids (cheeztv, `/3df803`) *arr instances are reachable again, with per-tier group authorization.
## Changes
- Bump `arrproxy-api` and `arrproxy-ui` images to `v0.4.0`.
- Add `arrproxy-tiers` ConfigMap (`ARRPROXY_TIERS_FILE`): `fafflix` + `cheeztv` tiers, each with sonarr/radarr upstreams, route hash, key subdir and group grants. Adults (`akP-media-fafflix`) reach both tiers all methods; kids (`akP-media-cheeztv`) reach only cheeztv, GET/HEAD. `legacyRoutes:false` retires the unprefixed routes.
- Because arrproxy strips `/<hash>` and proxies `/<app>/...`, each upstream URL carries the `/<hash>` path segment so the joined upstream path (`/3aa168/sonarr/...`) matches the *arr's own UrlBase.
- Mount the tiers file and rework the projected keys volume to the real per-instance `{sonarr,radarr}-{adult,kids}-apikey` Secrets under `<keyDir>/<app>` (`adult/*`, `kids/*`); refresh reloader annotations.
- oauth2-proxy: route `/3aa168` and `/3df803` to arrproxy-api (drop dead `/sonarr` `/radarr` `/prowlarr`; prowlarr is served directly), and widen `SKIP_AUTH_REGEX` to `^/[^/]+/[^/]+/api` for the hash-prefixed tier api paths.
Group headers keep flowing via the existing Authentik `ak_groups` claim -> oauth2-proxy `X-Forwarded-Groups` mechanism. Validated with `kustomize build` + repo kubeconform (88/88 valid). No new Vault seeds required — the per-instance apikey Secrets already exist.
Reviewed-on: #416
Co-authored-by: unkin-agent <unkin-agent@unkin.net>
Co-committed-by: unkin-agent <unkin-agent@unkin.net>
167 lines
6.1 KiB
YAML
167 lines
6.1 KiB
YAML
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: arrproxy-api
|
|
namespace: arrstack
|
|
annotations:
|
|
# Wave 2: serve only after the wave-1 migrate Job completes.
|
|
argocd.argoproj.io/sync-wave: "2"
|
|
secret.reloader.stakater.com/reload: "arrproxy-pepper,arrproxy-admin-token,arrproxy-db-app,sonarr-adult-apikey,radarr-adult-apikey,sonarr-kids-apikey,radarr-kids-apikey"
|
|
configmap.reloader.stakater.com/reload: "arrproxy-tiers"
|
|
spec:
|
|
replicas: 2
|
|
selector:
|
|
matchLabels:
|
|
app: arrproxy-api
|
|
strategy:
|
|
rollingUpdate:
|
|
maxUnavailable: 1
|
|
type: RollingUpdate
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: arrproxy-api
|
|
spec:
|
|
serviceAccountName: default
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 65532
|
|
runAsGroup: 65532
|
|
fsGroup: 65532
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
containers:
|
|
- name: api
|
|
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-api:v0.4.0
|
|
imagePullPolicy: IfNotPresent
|
|
ports:
|
|
- containerPort: 8080
|
|
name: http
|
|
protocol: TCP
|
|
env:
|
|
- name: ARRPROXY_ADDR
|
|
value: ":8080"
|
|
# oauth2-proxy --pass-user-headers forwards identity to the upstream as
|
|
# X-Forwarded-{User,Email,Groups} (a single comma-joined Groups value).
|
|
# Email/User already match the api defaults; override the groups header
|
|
# (default X-Auth-Request-Groups is auth_request-response-only and never
|
|
# reaches this upstream) so group-based authorization works.
|
|
- name: ARRPROXY_GROUPS_HEADER
|
|
value: X-Forwarded-Groups
|
|
# Real per-app *arr keys, projected one file per app under a per-tier
|
|
# subdir (<keyDir>/<app>) matching the tiers file: adult/{sonarr,radarr}
|
|
# and kids/{sonarr,radarr}. Sourced from the same <instance>-apikey
|
|
# Secrets the *arr Deployments consume. The api injects them server-side
|
|
# and redacts them from every proxied response.
|
|
- name: ARRPROXY_KEYS_DIR
|
|
value: /etc/arrproxy/keys
|
|
# Tier topology (fafflix/cheeztv): upstreams, route hashes, key subdirs
|
|
# and group grants. Legacy unprefixed routes are retired in the file
|
|
# (legacyRoutes:false); see arrproxy-tiers ConfigMap.
|
|
- name: ARRPROXY_TIERS_FILE
|
|
value: /etc/arrproxy/tiers/tiers.json
|
|
- name: ARRPROXY_PEPPER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: arrproxy-pepper
|
|
key: pepper
|
|
# Machine-mint admin bearer, synced from Vault KV via the
|
|
# arrproxy-admin-token VSO. Gates the /api/admin/ route that
|
|
# oauth2-proxy intentionally skip-auths so OpenBao on the VMs can
|
|
# mint tokens against arrproxy's own bearer.
|
|
- name: ARRPROXY_ADMIN_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: arrproxy-admin-token
|
|
key: token
|
|
# DSN assembled from the CNPG-generated arrproxy-db-app Secret;
|
|
# $(VAR) expansion resolves the two env entries defined above it.
|
|
- name: ARRPROXY_DB_USER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: arrproxy-db-app
|
|
key: username
|
|
- name: ARRPROXY_DB_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: arrproxy-db-app
|
|
key: password
|
|
- name: DATABASE_URL
|
|
value: "postgres://$(ARRPROXY_DB_USER):$(ARRPROXY_DB_PASSWORD)@arrproxy-db-rw.arrstack.svc.cluster.local:5432/arrproxy?sslmode=require"
|
|
volumeMounts:
|
|
- name: arr-keys
|
|
mountPath: /etc/arrproxy/keys
|
|
readOnly: true
|
|
- name: tiers
|
|
mountPath: /etc/arrproxy/tiers
|
|
readOnly: true
|
|
- name: tmp
|
|
mountPath: /tmp
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /livez
|
|
port: http
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /readyz
|
|
port: http
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
limits:
|
|
cpu: "1"
|
|
memory: 512Mi
|
|
volumes:
|
|
# Real per-tier *arr API keys, projected as <keyDir>/<app> so the api
|
|
# reads /etc/arrproxy/keys/{adult,kids}/{sonarr,radarr} (matching the
|
|
# tiers file keyDir). Reuses the same <instance>-apikey Secrets the *arr
|
|
# Deployments already consume (seeded by their VaultStaticSecrets).
|
|
- name: arr-keys
|
|
projected:
|
|
sources:
|
|
- secret:
|
|
name: sonarr-adult-apikey
|
|
items:
|
|
- key: apitoken
|
|
path: adult/sonarr
|
|
- secret:
|
|
name: radarr-adult-apikey
|
|
items:
|
|
- key: apitoken
|
|
path: adult/radarr
|
|
- secret:
|
|
name: sonarr-kids-apikey
|
|
items:
|
|
- key: apitoken
|
|
path: kids/sonarr
|
|
- secret:
|
|
name: radarr-kids-apikey
|
|
items:
|
|
- key: apitoken
|
|
path: kids/radarr
|
|
# Tier topology JSON (ARRPROXY_TIERS_FILE).
|
|
- name: tiers
|
|
configMap:
|
|
name: arrproxy-tiers
|
|
- name: tmp
|
|
emptyDir:
|
|
sizeLimit: 64Mi
|
|
restartPolicy: Always
|