arrstack: set *arr AuthenticationMethod=External (proxy-trusted) (#382)

## Why

The *arr UIs reached through **arrproxy** were prompting **"Authentication Required"**. Sonarr v4 (and Radarr/Prowlarr on the same Servarr auth code) refuses remote access when `AuthenticationMethod=None`, so the web UI kept demanding a login even though **arrproxy + oauth2-proxy already authenticate every user at the front door**. That is a double prompt with no purpose.

## Change

Extend the existing `apikey-init` container for **sonarr / radarr / prowlarr** to also idempotently enforce, in `/config/config.xml`:

- `<AuthenticationMethod>External</AuthenticationMethod>`
- `<AuthenticationRequired>Enabled</AuthenticationRequired>`

Create-or-replace both elements (same idempotent sed/printf pattern already used for `<ApiKey>`/`<UrlBase>`), without disturbing ApiKey or UrlBase. Pods use `Recreate`, so they roll and re-run the init container on apply.

## Why this is the correct, header-less fix

In the Servarr v4 source, `AddExternal()` registers the **identical `NoAuthenticationHandler` as `AddNone()`** — `External` requires **no** username header (no `X-Forwarded-User` / `Remote-User`). It differs from `None` only in that it is **exempt from the None remote-access block**, so the UI stops prompting while remote access is permitted.

This matters because arrproxy's `trustBoundary` deliberately **strips all inbound identity headers** and forwards only the real `X-Api-Key` to the upstream *arr — it forwards no username header. Because `External` needs none, that stripping is irrelevant and **no arrproxy change is required**. The API path is unaffected (arrproxy injects the real key; *arr API auth is key-based regardless of AuthenticationMethod).

## Validation

- `kustomize build --enable-helm apps/overlays/au-syd1/arrstack` → OK (rendered init carries the auth logic for all 3 apps)
- pre-commit (yamllint + all hooks) → Passed

Versions in scope: sonarr 4.0.19, radarr 6.3.0, prowlarr 2.5.2 (all share the Servarr v4 auth handler).

---------

Co-authored-by: unkin-agent <unkin-agent@git.unkin.net>
Reviewed-on: #382
Co-authored-by: Unkin Agent <unkin-agent@unkin.net>
Co-committed-by: Unkin Agent <unkin-agent@unkin.net>
This commit was merged in pull request #382.
This commit is contained in:
2026-08-18 20:03:18 +10:00
committed by BenVincent
parent c22fbe435a
commit 108d1cb213
3 changed files with 54 additions and 9 deletions
+18 -3
View File
@@ -26,7 +26,10 @@ spec:
# (override bootstrap): the key is minted in Vault, synced by VSO into the
# prowlarr-apikey Secret, and written here. UrlBase=/prowlarr lets arrproxy
# forward arrstack.unkin.net/prowlarr/... with the prefix preserved (no 307).
# Runs as root to fix ownership; touches only <ApiKey> and <UrlBase>.
# Runs as root to fix ownership; touches only <ApiKey>, <UrlBase>, and
# <AuthenticationMethod>=External + <AuthenticationRequired>. External makes
# the *arr defer UI login to arrproxy/oauth2-proxy (same no-auth request
# handler as None, but permits remote access without prompting).
- name: apikey-init
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0
imagePullPolicy: IfNotPresent
@@ -50,9 +53,11 @@ spec:
;;
esac
URL_BASE=/prowlarr
AUTH_METHOD=External
AUTH_REQUIRED=Enabled
CFG=/config/config.xml
if [ ! -f "$CFG" ]; then
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n</Config>\n' "$API_KEY" "$URL_BASE" > "$CFG"
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n <AuthenticationMethod>%s</AuthenticationMethod>\n <AuthenticationRequired>%s</AuthenticationRequired>\n</Config>\n' "$API_KEY" "$URL_BASE" "$AUTH_METHOD" "$AUTH_REQUIRED" > "$CFG"
else
if grep -q '<ApiKey>' "$CFG"; then
sed -i "s|<ApiKey>[^<]*</ApiKey>|<ApiKey>${API_KEY}</ApiKey>|" "$CFG"
@@ -64,10 +69,20 @@ spec:
else
sed -i "s|<Config>|<Config>\n <UrlBase>${URL_BASE}</UrlBase>|" "$CFG"
fi
if grep -q '<AuthenticationMethod>' "$CFG"; then
sed -i "s|<AuthenticationMethod>[^<]*</AuthenticationMethod>|<AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
else
sed -i "s|<Config>|<Config>\n <AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
fi
if grep -q '<AuthenticationRequired>' "$CFG"; then
sed -i "s|<AuthenticationRequired>[^<]*</AuthenticationRequired>|<AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
else
sed -i "s|<Config>|<Config>\n <AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
fi
fi
chown 1000:1000 "$CFG"
chmod 600 "$CFG"
echo "config-init: <ApiKey> and <UrlBase>=${URL_BASE} enforced from Vault"
echo "config-init: <ApiKey>, <UrlBase>=${URL_BASE}, <AuthenticationMethod>=${AUTH_METHOD}, <AuthenticationRequired>=${AUTH_REQUIRED} enforced from Vault"
resources:
requests:
cpu: 50m
+18 -3
View File
@@ -26,7 +26,10 @@ spec:
# (override bootstrap): the key is minted in Vault, synced by VSO into the
# radarr-apikey Secret, and written here. UrlBase=/radarr lets arrproxy
# forward arrstack.unkin.net/radarr/... with the prefix preserved (no 307).
# Runs as root to fix ownership; touches only <ApiKey> and <UrlBase>.
# Runs as root to fix ownership; touches only <ApiKey>, <UrlBase>, and
# <AuthenticationMethod>=External + <AuthenticationRequired>. External makes
# the *arr defer UI login to arrproxy/oauth2-proxy (same no-auth request
# handler as None, but permits remote access without prompting).
- name: apikey-init
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0
imagePullPolicy: IfNotPresent
@@ -50,9 +53,11 @@ spec:
;;
esac
URL_BASE=/radarr
AUTH_METHOD=External
AUTH_REQUIRED=Enabled
CFG=/config/config.xml
if [ ! -f "$CFG" ]; then
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n</Config>\n' "$API_KEY" "$URL_BASE" > "$CFG"
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n <AuthenticationMethod>%s</AuthenticationMethod>\n <AuthenticationRequired>%s</AuthenticationRequired>\n</Config>\n' "$API_KEY" "$URL_BASE" "$AUTH_METHOD" "$AUTH_REQUIRED" > "$CFG"
else
if grep -q '<ApiKey>' "$CFG"; then
sed -i "s|<ApiKey>[^<]*</ApiKey>|<ApiKey>${API_KEY}</ApiKey>|" "$CFG"
@@ -64,10 +69,20 @@ spec:
else
sed -i "s|<Config>|<Config>\n <UrlBase>${URL_BASE}</UrlBase>|" "$CFG"
fi
if grep -q '<AuthenticationMethod>' "$CFG"; then
sed -i "s|<AuthenticationMethod>[^<]*</AuthenticationMethod>|<AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
else
sed -i "s|<Config>|<Config>\n <AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
fi
if grep -q '<AuthenticationRequired>' "$CFG"; then
sed -i "s|<AuthenticationRequired>[^<]*</AuthenticationRequired>|<AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
else
sed -i "s|<Config>|<Config>\n <AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
fi
fi
chown 1000:1000 "$CFG"
chmod 600 "$CFG"
echo "config-init: <ApiKey> and <UrlBase>=${URL_BASE} enforced from Vault"
echo "config-init: <ApiKey>, <UrlBase>=${URL_BASE}, <AuthenticationMethod>=${AUTH_METHOD}, <AuthenticationRequired>=${AUTH_REQUIRED} enforced from Vault"
resources:
requests:
cpu: 50m
+18 -3
View File
@@ -29,7 +29,10 @@ spec:
# (override bootstrap): the key is minted in Vault, synced by VSO into the
# sonarr-apikey Secret, and written here. UrlBase=/sonarr lets arrproxy
# forward arrstack.unkin.net/sonarr/... with the prefix preserved (no 307).
# Runs as root to fix ownership; touches only <ApiKey> and <UrlBase>.
# Runs as root to fix ownership; touches only <ApiKey>, <UrlBase>, and
# <AuthenticationMethod>=External + <AuthenticationRequired>. External makes
# the *arr defer UI login to arrproxy/oauth2-proxy (same no-auth request
# handler as None, but permits remote access without prompting).
- name: apikey-init
image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0
imagePullPolicy: IfNotPresent
@@ -53,9 +56,11 @@ spec:
;;
esac
URL_BASE=/sonarr
AUTH_METHOD=External
AUTH_REQUIRED=Enabled
CFG=/config/config.xml
if [ ! -f "$CFG" ]; then
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n</Config>\n' "$API_KEY" "$URL_BASE" > "$CFG"
printf '<Config>\n <ApiKey>%s</ApiKey>\n <UrlBase>%s</UrlBase>\n <AuthenticationMethod>%s</AuthenticationMethod>\n <AuthenticationRequired>%s</AuthenticationRequired>\n</Config>\n' "$API_KEY" "$URL_BASE" "$AUTH_METHOD" "$AUTH_REQUIRED" > "$CFG"
else
if grep -q '<ApiKey>' "$CFG"; then
sed -i "s|<ApiKey>[^<]*</ApiKey>|<ApiKey>${API_KEY}</ApiKey>|" "$CFG"
@@ -67,10 +72,20 @@ spec:
else
sed -i "s|<Config>|<Config>\n <UrlBase>${URL_BASE}</UrlBase>|" "$CFG"
fi
if grep -q '<AuthenticationMethod>' "$CFG"; then
sed -i "s|<AuthenticationMethod>[^<]*</AuthenticationMethod>|<AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
else
sed -i "s|<Config>|<Config>\n <AuthenticationMethod>${AUTH_METHOD}</AuthenticationMethod>|" "$CFG"
fi
if grep -q '<AuthenticationRequired>' "$CFG"; then
sed -i "s|<AuthenticationRequired>[^<]*</AuthenticationRequired>|<AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
else
sed -i "s|<Config>|<Config>\n <AuthenticationRequired>${AUTH_REQUIRED}</AuthenticationRequired>|" "$CFG"
fi
fi
chown 1000:1000 "$CFG"
chmod 600 "$CFG"
echo "config-init: <ApiKey> and <UrlBase>=${URL_BASE} enforced from Vault"
echo "config-init: <ApiKey>, <UrlBase>=${URL_BASE}, <AuthenticationMethod>=${AUTH_METHOD}, <AuthenticationRequired>=${AUTH_REQUIRED} enforced from Vault"
resources:
requests:
cpu: 50m