homelab/manifests/media/jellyfin-header-debug.yaml
Nik Afiq 7a64863ead
Some checks failed
validate / lint (push) Failing after 1s
fix: rewrite Filebar's malformed auth header to unblock login (temporary)
Filebar sends its auth only on the legacy X-Emby-Authorization header,
with unquoted values (Client=Filebar instead of Client="Filebar"), and
never sends the Authorization header Jellyfin 12.0 requires to resolve
request.App -- causing every login to 400. Switches the debug proxy to
OpenResty and adds a Lua shim that rewrites just Filebar's requests into
the header format Jellyfin expects (matching SenPlayer's working
requests); everything else passes through unchanged.

This is a workaround for a Filebar bug, not a permanent fix -- see the
TODO in jellyfin-header-debug.yaml to remove it once Filebar ships a
corrected release.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016EQrGkfXFae4PZG9QfPRf4
2026-09-14 16:05:22 +09:00

118 lines
4.3 KiB
YAML

# Apply: kubectl apply -f manifests/media/jellyfin-header-debug.yaml
# Delete: kubectl delete -f manifests/media/jellyfin-header-debug.yaml
# Description: TEMPORARY compat shim + diagnostic proxy in front of the real
# jellyfin Service. The Filebar iOS app (<=1.4.13) sends its auth header only
# on the legacy X-Emby-Authorization header, unquoted (e.g. Client=Filebar
# instead of Client="Filebar"), and never sends the modern Authorization
# header Jellyfin 12.0 requires to identify the client -- so every login
# gets a 400 (ArgumentNullException on request.App). This proxy rewrites
# just Filebar's requests into the header format Jellyfin expects (matching
# what SenPlayer already sends correctly) and forwards everything else
# unchanged. All requests are also logged to stdout for visibility.
#
# TODO: DELETE this file and revert the jellyfin Ingress backend to
# `jellyfin` once Filebar ships a fixed release that sends a correct
# Authorization header on its own -- this is a workaround for their bug,
# not something to keep around permanently.
apiVersion: v1
kind: ConfigMap
metadata:
name: jellyfin-debug-proxy-conf
namespace: jellyfin
data:
default.conf: |
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
log_format jf_debug '$time_iso8601 client=$remote_addr method=$request_method uri="$request_uri" '
'status=$status '
'user_agent="$http_user_agent" '
'authorization="$http_authorization" '
'x_emby_authorization="$http_x_emby_authorization" '
'x_emby_token="$http_x_emby_token" '
'x_mediabrowser_token="$http_x_mediabrowser_token"';
server {
listen 8080;
access_log /dev/stdout jf_debug;
# TEMPORARY Filebar compat shim -- see TODO in this file's header.
# Only touches requests from Filebar's broken auth header; everything
# else (SenPlayer, web UI, etc.) passes through untouched.
access_by_lua_block {
local ua = ngx.var.http_user_agent or ""
local authorization = ngx.var.http_authorization
local legacy = ngx.var.http_x_emby_authorization
if ua:find("^Filebar") and (not authorization or authorization == "") and legacy then
local client = legacy:match("Client=([^,]+)")
local device = legacy:match("Device=([^,]+)")
local deviceid = legacy:match("DeviceId=([^,]+)")
local version = legacy:match("Version=([^,]+)")
local token = legacy:match("Token=([^,]+)")
if client then
local fixed = string.format('MediaBrowser Client="%s", Device="%s", DeviceId="%s", Version="%s"',
client, device or "", deviceid or "", version or "")
if token and token ~= "" then
fixed = fixed .. string.format(', Token="%s"', token)
end
ngx.req.set_header("Authorization", fixed)
ngx.req.set_header("X-Emby-Authorization", fixed)
end
end
}
location / {
proxy_pass http://jellyfin.jellyfin.svc.cluster.local:80;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jellyfin-debug-proxy
namespace: jellyfin
spec:
replicas: 1
selector:
matchLabels:
app: jellyfin-debug-proxy
template:
metadata:
labels:
app: jellyfin-debug-proxy
spec:
containers:
- name: nginx
image: openresty/openresty:1.31.1.1-alpine
ports:
- containerPort: 8080
volumeMounts:
- name: conf
mountPath: /etc/nginx/conf.d
volumes:
- name: conf
configMap:
name: jellyfin-debug-proxy-conf
---
apiVersion: v1
kind: Service
metadata:
name: jellyfin-debug-proxy
namespace: jellyfin
spec:
selector:
app: jellyfin-debug-proxy
ports:
- port: 80
targetPort: 8080