83 lines
5.7 KiB
YAML
83 lines
5.7 KiB
YAML
# ServiceDefinition: jackett
|
|
# Source of truth: spec docs/13-service-catalog.md row 4; docs/03-networking-isolation-and-egress.md;
|
|
# docs/15-identity-and-access.md; decisions/adr-0015 (VPN egress steering).
|
|
# internal indexer proxy | egress VPN + kill-switch | config on Longhorn | auth forward-auth
|
|
# (bare web UI, no native OIDC).
|
|
#
|
|
# VPN-class (docs/13 row 4; docs/03 "VPN-class set"): Jackett scrapes public torrent indexers/trackers;
|
|
# that egress MUST leave via the node PIA tunnel, never the WAN. `egress: vpn` renders the
|
|
# homelab.keithjackson.dev/egress-class=vpn pod label the shared CiliumEgressGatewayPolicy
|
|
# (platform/vpn-egress/egress-steering.yaml) selects on to steer + kill-switch the traffic OUTSIDE the
|
|
# workload (R-NET-05/06/07). No independent egress path; fail-closed if tun0 is down.
|
|
#
|
|
# Inbound reachability (← sonarr, ← radarr) is an EAST-WEST allow rendered on the arr side when those
|
|
# services are onboarded. This service initiates nothing east-west, so dependencies is empty (reaches
|
|
# only DNS + VPN internet). Validates against schemas/servicedefinition.schema.json.
|
|
name: jackett
|
|
kind: container
|
|
source:
|
|
image: lscr.io/linuxserver/jackett:v0.24.3030-ls30
|
|
exposure: internal # LAN-only web UI at jackett.${BASE_DOMAIN} (docs/13 row 4; R-DNS/R-NET)
|
|
egress: vpn # indexer scraping via node PIA tunnel + kill-switch (docs/03; ADR-0015; R-NET-05/06)
|
|
storage:
|
|
- name: config
|
|
size: 2Gi
|
|
class: longhorn # indexer definitions/API keys on 3-replica Longhorn (docs/04; R-STATE-02)
|
|
mountPath: /config # THE APP WRITES /config, NOT the renderer default /data/config.
|
|
# LSIO images run with `-data=/config` (Jackett/qBittorrent likewise use
|
|
# /config), so without this the Longhorn PVC mounted at /data/config sat
|
|
# holding nothing but lost+found while the app wrote its entire
|
|
# configuration to the container's EPHEMERAL writable layer — destroyed on
|
|
# every pod recreation: restart, reschedule, image bump. Measured in-cluster
|
|
# 2026-09-12: /data/config was dev=2080 with [lost+found], /config was
|
|
# dev=2053 on the node's own disk with [Sentry asp config.xml logs].
|
|
# This is the SAME failure catalog/plex.yaml:54 already records fixing.
|
|
# The live /config contents were copied onto each PVC before this landed,
|
|
# so API keys and credentials other apps reference were preserved.
|
|
dependencies: [] # default-deny: reaches only DNS; arr reach it north of its own egress (docs/03; R-NET-01/02)
|
|
ingressRoutes:
|
|
- host: jackett.${BASE_DOMAIN}
|
|
port: 9117 # Jackett WebUI; protected at the Gateway by forward-auth (docs/09, docs/15)
|
|
auth:
|
|
mode: forward-auth # oauth2-proxy -> Keycloak; bare UI, no usable native OIDC (docs/15; R-IDP-02)
|
|
groups:
|
|
- media-users # authenticated AND in an authorized group (R-IDP-06)
|
|
# platform-admins is a SUPERSET, not a widening. --allowed-group is repeatable and OR-ed, so
|
|
# listing both means "media consumers OR platform operators". Without it the media UIs are
|
|
# reachable by NOBODY the moment media-users is empty: on prod the realm's only user is the
|
|
# operator, who is in platform-admins only, so jackett/qbittorrent/radarr/sonarr answered an
|
|
# immediate 403 at the oauth2-proxy callback (no login prompt, because the Keycloak SSO session
|
|
# already existed) while pihole/crafty — which already gate on platform-admins — worked.
|
|
# This grants no real privilege: platform-admins already maps to cluster-admin via
|
|
# k8s-oidc-rbac.yaml (oidc:platform-admins), so a member can already exec into these pods.
|
|
- platform-admins # operators of these services must not be locked out of them
|
|
# Answer Jackett's OWN API with 401 instead of a 302 to Keycloak (schema 1.9.0; renders
|
|
# --api-route, singular and repeatable, onto the oauth2-proxy sidecar).
|
|
#
|
|
# MEASURED on this pod's access log, 2026-09-30: on /api/ paths, 33 x 302 against 2 x 401 —
|
|
# and 4 of 6 POSTs were answered BY THE PROXY and never reached Jackett (access-log upstream
|
|
# field `-`, not `/`), including both `/api/v2.0/server/update` calls. Jackett's jQuery does
|
|
# not send the exact `Accept: application/json` oauth2-proxy's AJAX guard requires, so the
|
|
# 302 was followed invisibly, satisfied silently by the live Keycloak SSO session, and
|
|
# resolved 200 carrying the dashboard's HTML where the JavaScript expected JSON. A browser
|
|
# following a 302 also re-issues a POST as a GET and DROPS THE BODY, which is why "adding an
|
|
# indexer silently does nothing" was the user-visible symptom and nothing logged a failure.
|
|
#
|
|
# SCOPE: `/api/` only. Jackett serves its UI at /UI/ and its static assets elsewhere, and a
|
|
# browser never NAVIGATES to /api/ — so a plain page load is still 302'd into a silent SSO
|
|
# sign-in, which is the behaviour that must not change.
|
|
apiRoutes:
|
|
- ^/api/
|
|
update:
|
|
channel: stable # R-UPD-01
|
|
range: ">=0.22.0" # floor only, no ceiling (0.24.x runs as of 2026-10-30; Jackett has no
|
|
# major line to hold back from, so renovate.json5 mirrors no cap)
|
|
cadence: "0 4 * * *"
|
|
autoRollbackOnFailedHealth: true # R-UPD-03
|
|
observability:
|
|
metrics: true
|
|
slis:
|
|
- up # indexer proxy down -> alert (docs/10; R-OBS-01)
|
|
backup:
|
|
policy: daily-offcluster # indexer config/API keys backed up off-cluster (docs/04; R-STATE-03)
|