feat(freesailarr)!: eight image updates and a trustedNetworks value for the *arr apps

Collapses the eight open Renovate PRs for this stack into one change, and
answers what two of them turned out to carry.

The eight tags: qbittorrent 5.2.3 -> 5.2.4, bazarr 1.6.0 -> 1.6.2,
sonarr 4.0.19 -> 4.0.20, flaresolverr v3.5.0 -> v3.5.2,
recyclarr 8.7.1 -> 8.7.3, prowlarr 2.5.2 -> 2.6.5, radarr 6.3.0 -> 6.4.4,
seerr v3.4.1 -> v3.5.0. gluetun, the busybox init image and the
digest-pinned mediathekarr have no update pending and are untouched.

Major rather than patch, because prowlarr 2.6.3 and radarr 6.4.3 are not
plain bumps: they removed the hard-coded trusted proxy networks
(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7, fe80::/10, read in
Startup.cs at the previously pinned tags) and replaced them with a
TrustedNetworks setting whose own default is string.Empty -
ForwardedHeadersConfigurator returns before registering a single
KnownNetwork while it is empty. Behind an in-cluster ingress controller
that means the forwarded headers are ignored: the client address the app
reports is the controller's pod IP, the scheme reads as http behind TLS
termination, and under "Disabled for Local Addresses" a proxied request
stops counting as local.

So a <app>.trustedNetworks value is added for prowlarr, radarr AND sonarr,
rendered as <APP>__SERVER__TRUSTEDNETWORKS and only when non-empty - an
empty variable cannot be told apart from an unset one by the app, but looks
handled to a reader of the manifest. The env form was verified per app
rather than inferred: all three bind ServerOptions to <App>:Server next to
a plain AddEnvironmentVariables(). A helper suppresses the chart's entry
when the app's own env block already carries the variable, for the reason
the UrlBase helper states - two identically named variables in one
container are not a defined state.

sonarr is included because it is already in the empty state: 4.0.19, the
version this chart shipped before, has no hard-coded networks either. It
arrived in what the convention classes as a plain app-version bump, so
nothing reviewed or documented it. Repairing two and knowingly leaving the
third would be the same silent pattern.

The default is empty, and this therefore does NOT restore the behaviour
from before the upgrade. That is deliberate and follows upstream's reason -
trusting X-Forwarded-For from any private network is a spoofing vector -
and it is made visible instead of reinstated: NOTES.txt warns in exactly
the combination that costs something, the app published through an Ingress
or an HTTPRoute and the value empty, and stays quiet otherwise.

AllowedHosts, added by the same upstream releases, is deliberately not
exposed. Its default is string.Empty as well and the app then accepts any
host, so there is nothing to repair; the trap is documented instead,
because if it is ever set the app appends only localhost, 127.0.0.1, [::1]
and its own hostname - neither the pod IP nor the Service DNS name - and
the kubelet probes the pod by IP, which an incomplete list answers with
HTTP 400.

At default values the rendered manifest changes in exactly eight lines, the
image tags; the new value renders nothing until it is set.
This commit is contained in:
jklein committed 2026-10-12 22:00:23 +00:00
1 parent f202fdc3fc
commit 7003f3674c
6 files changed
+176 -9

No files matched your search

+39
View File
@@ -52,6 +52,45 @@ SET gluetun.vpn.firewallOutboundSubnets — IT IS EMPTY RIGHT NOW
fail, in a way that looks like an application bug. Set it to the cluster pod
and service CIDRs, e.g. "10.42.0.0/16,10.43.0.0/16" on k3s.
{{- end }}
{{- /* The servarr apps that are published through a routing object AND have no
trusted proxy network set - the one combination in which the empty
default actually costs something. Gated on freesailarr.published plus
membership in freesailarr.ingressApps, the same pair the ACCESS section
below uses, so it holds in Gateway mode too: reading ingress.enabled
directly would silence this warning in exactly the mode whose routes
still publish the apps. */}}
{{- $noTrust := list }}
{{- if $published }}
{{- range $a := $ingressApps }}
{{- if and (has $a.name (list "prowlarr" "radarr" "sonarr")) (not (index $.Values $a.name).trustedNetworks) }}
{{- $noTrust = append $noTrust $a.name }}
{{- end }}
{{- end }}
{{- end }}
{{- if $noTrust }}
FORWARDED HEADERS ARE NOT TRUSTED — {{ join ", " $noTrust }}
These apps are published through a routing object and their trustedNetworks
value is empty, which is the chart's default. They therefore trust no proxy:
the client address each one logs and displays is your ingress controller's
pod IP rather than the real client, and each sees the scheme as http even
where TLS is terminated in front of it. If an app's authentication is set to
"Disabled for Local Addresses", a proxied request no longer counts as local
and is asked to authenticate.
This is not a regression introduced by your values. Upstream removed the
hard-coded private networks (prowlarr 2.6.3, radarr 6.4.3; sonarr before
4.0.19) because trusting X-Forwarded-For from any private network is a
spoofing vector, and this chart follows that default rather than reinstating
it behind your back.
To honour the headers, set the network your ingress controller's pods sit in
— the chart cannot know it, as it is a property of the cluster:
prowlarr.trustedNetworks: "10.42.0.0/16" # e.g. the k3s pod CIDR
Leaving it empty is a valid choice; it is only not a silent one.
{{- end }}
ACCESS
{{- if and $published $ingressApps }}