arrstack: wire arrproxy v0.4.0 tier routing (fafflix/cheeztv)
The old bare sonarr/radarr Services are gone; arrproxy still ran the v0.3.x
default topology pointing at non-existent upstreams and dead sonarr/radarr
apikey Secrets. Cut 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.
- 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. arrproxy strips /<hash> and proxies /<app>/..., so each
upstream URL carries the /<hash> path segment to line up with the *arr's own
UrlBase (/3aa168/sonarr etc.).
- 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 the 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.
This commit is contained in:
@@ -7,7 +7,8 @@ metadata:
|
||||
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-apikey,radarr-apikey,prowlarr-apikey"
|
||||
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:
|
||||
@@ -33,7 +34,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: api
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-api:v0.3.1
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-api:v0.4.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -49,11 +50,18 @@ spec:
|
||||
# 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 this dir
|
||||
# (sourced from the existing <app>-apikey Secrets). The api injects
|
||||
# them server-side and redacts them from every proxied response.
|
||||
# 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:
|
||||
@@ -86,6 +94,9 @@ spec:
|
||||
- name: arr-keys
|
||||
mountPath: /etc/arrproxy/keys
|
||||
readOnly: true
|
||||
- name: tiers
|
||||
mountPath: /etc/arrproxy/tiers
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
livenessProbe:
|
||||
@@ -118,27 +129,37 @@ spec:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
volumes:
|
||||
# Real *arr API keys, one file per app named exactly <app> so the api
|
||||
# reads /etc/arrproxy/keys/{sonarr,radarr,prowlarr}. Reuses the same
|
||||
# <app>-apikey Secrets the *arr Deployments already consume.
|
||||
# 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-apikey
|
||||
name: sonarr-adult-apikey
|
||||
items:
|
||||
- key: apitoken
|
||||
path: sonarr
|
||||
path: adult/sonarr
|
||||
- secret:
|
||||
name: radarr-apikey
|
||||
name: radarr-adult-apikey
|
||||
items:
|
||||
- key: apitoken
|
||||
path: radarr
|
||||
path: adult/radarr
|
||||
- secret:
|
||||
name: prowlarr-apikey
|
||||
name: sonarr-kids-apikey
|
||||
items:
|
||||
- key: apitoken
|
||||
path: prowlarr
|
||||
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
|
||||
|
||||
@@ -8,6 +8,7 @@ resources:
|
||||
- migrations-configmap.yaml
|
||||
- migrate-job.yaml
|
||||
- vaultstaticsecret.yaml
|
||||
- tiers-configmap.yaml
|
||||
- oauth2-proxy-configmap.yaml
|
||||
- oauth2-proxy-deployment.yaml
|
||||
- api-deployment.yaml
|
||||
|
||||
@@ -21,9 +21,11 @@ data:
|
||||
# application (terraform-authentik PR #18).
|
||||
OAUTH2_PROXY_OIDC_ISSUER_URL: "https://identity.unkin.net/application/o/arrstack/"
|
||||
OAUTH2_PROXY_REDIRECT_URL: "https://arrstack.unkin.net/oauth2/callback"
|
||||
# Longest-prefix wins: /api and /<app> go to arrproxy-api, everything else
|
||||
# (the SPA + static assets) to arrproxy-ui.
|
||||
OAUTH2_PROXY_UPSTREAMS: "http://arrproxy-ui.arrstack.svc.cluster.local:8080/,http://arrproxy-api.arrstack.svc.cluster.local:8080/api/,http://arrproxy-api.arrstack.svc.cluster.local:8080/sonarr/,http://arrproxy-api.arrstack.svc.cluster.local:8080/radarr/,http://arrproxy-api.arrstack.svc.cluster.local:8080/prowlarr/"
|
||||
# Longest-prefix wins: /api and the per-tier hash routes (/3aa168, /3df803) go
|
||||
# to arrproxy-api, everything else (the SPA + static assets) to arrproxy-ui.
|
||||
# Tier routes carry /<hash>/<app>/...; the bare /sonarr /radarr /prowlarr
|
||||
# upstreams are retired (legacy routes off; prowlarr is served directly).
|
||||
OAUTH2_PROXY_UPSTREAMS: "http://arrproxy-ui.arrstack.svc.cluster.local:8080/,http://arrproxy-api.arrstack.svc.cluster.local:8080/api/,http://arrproxy-api.arrstack.svc.cluster.local:8080/3aa168/,http://arrproxy-api.arrstack.svc.cluster.local:8080/3df803/"
|
||||
OAUTH2_PROXY_SCOPE: "openid email profile ak_groups"
|
||||
# Populate session.Groups from the Authentik ak_groups claim; pass-user-headers
|
||||
# then emits it as a single comma-joined X-Forwarded-Groups header.
|
||||
@@ -33,14 +35,16 @@ data:
|
||||
# set-xauthrequest is intentionally NOT set -- it only populates auth_request
|
||||
# *response* headers, which never reach an --upstreams-proxied backend.
|
||||
OAUTH2_PROXY_PASS_USER_HEADERS: "true"
|
||||
# Bypass auth for the *arr proxy API (/<app>/api...) and the machine-mint
|
||||
# admin route (/api/admin/...). The first matches /sonarr/api; the second
|
||||
# matches /api/admin/ only -- both routed to the arrproxy-api upstream by the
|
||||
# catch-all /api/ prefix above. /api/admin/ is protected by arrproxy's OWN
|
||||
# ARRPROXY_ADMIN_TOKEN bearer (OpenBao on the VMs reaches it via the ingress),
|
||||
# so it is intentionally oauth-skipped. /api/tokens and /api/me are NOT
|
||||
# matched and stay oauth-authenticated.
|
||||
OAUTH2_PROXY_SKIP_AUTH_REGEX: "^/[^/]+/api,^/api/admin/"
|
||||
# Bypass auth for the tier *arr proxy API (/<hash>/<app>/api...) and the
|
||||
# machine-mint admin route (/api/admin/...). The first matches
|
||||
# /3aa168/sonarr/api (two path segments before /api, since tier routes are
|
||||
# hash-prefixed); the second matches /api/admin/ only -- both routed to the
|
||||
# arrproxy-api upstream by the tier and catch-all /api/ prefixes above.
|
||||
# /api/admin/ is protected by arrproxy's OWN ARRPROXY_ADMIN_TOKEN bearer
|
||||
# (OpenBao on the VMs reaches it via the ingress), so it is intentionally
|
||||
# oauth-skipped. /api/tokens and /api/me are NOT matched and stay
|
||||
# oauth-authenticated.
|
||||
OAUTH2_PROXY_SKIP_AUTH_REGEX: "^/[^/]+/[^/]+/api,^/api/admin/"
|
||||
OAUTH2_PROXY_EMAIL_DOMAINS: "*"
|
||||
# Authentik hardcodes email_verified=false in the id_token; without this
|
||||
# oauth2-proxy rejects the session ("email ... isn't verified") -> 500 on
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
# arrproxy v0.4.0 tier topology (ARRPROXY_TIERS_FILE). Two isolated instance
|
||||
# tiers, each with its own *arr backends, real-key subdir, and group grants:
|
||||
# fafflix (adult, /3aa168/*) and cheeztv (kids, /3df803/*).
|
||||
# arrproxy strips the /<hash> route prefix and proxies "/<app>/..." upstream, so
|
||||
# each app's upstream URL carries the "/<hash>" path segment: the joined upstream
|
||||
# path (/3aa168/sonarr/...) then matches the *arr's own UrlBase (/3aa168/sonarr).
|
||||
# Access: the adult group reaches BOTH tiers with any method; the kids group
|
||||
# reaches ONLY the cheeztv tier and only GET/HEAD. Groups arrive on the
|
||||
# hierarchical Authentik ak_groups claim, forwarded by oauth2-proxy as
|
||||
# X-Forwarded-Groups. Legacy unprefixed routes are retired (legacyRoutes:false);
|
||||
# the old bare sonarr/radarr Services no longer exist.
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: arrproxy-tiers
|
||||
namespace: arrstack
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
data:
|
||||
tiers.json: |
|
||||
{
|
||||
"legacyTier": "fafflix",
|
||||
"legacyRoutes": false,
|
||||
"tiers": [
|
||||
{
|
||||
"name": "fafflix",
|
||||
"hash": "3aa168",
|
||||
"keyDir": "adult",
|
||||
"grants": [
|
||||
{ "group": "akP-media-fafflix" }
|
||||
],
|
||||
"apps": [
|
||||
{ "name": "sonarr", "upstream": "http://sonarr-adult.arrstack.svc.cluster.local:8989/3aa168", "urlBase": "/3aa168/sonarr" },
|
||||
{ "name": "radarr", "upstream": "http://radarr-adult.arrstack.svc.cluster.local:7878/3aa168", "urlBase": "/3aa168/radarr" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cheeztv",
|
||||
"hash": "3df803",
|
||||
"keyDir": "kids",
|
||||
"readOnly": true,
|
||||
"grants": [
|
||||
{ "group": "akP-media-fafflix" },
|
||||
{ "group": "akP-media-cheeztv", "methods": ["GET", "HEAD"] }
|
||||
],
|
||||
"apps": [
|
||||
{ "name": "sonarr", "upstream": "http://sonarr-kids.arrstack.svc.cluster.local:8989/3df803", "urlBase": "/3df803/sonarr" },
|
||||
{ "name": "radarr", "upstream": "http://radarr-kids.arrstack.svc.cluster.local:7878/3df803", "urlBase": "/3df803/radarr" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -31,7 +31,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: ui
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-ui:v0.3.1
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-ui:v0.4.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
Reference in New Issue
Block a user