diff --git a/services/jellyfin/deployment.yaml b/services/jellyfin/deployment.yaml
index fdd244a..14e9686 100644
--- a/services/jellyfin/deployment.yaml
+++ b/services/jellyfin/deployment.yaml
@@ -21,6 +21,31 @@ spec:
labels:
app: jellyfin
spec:
+ # Clean up any lingering OIDC artifacts and strip the injected script tag
+ initContainers:
+ - name: strip-oidc
+ image: docker.io/jellyfin/jellyfin:10.11.5
+ securityContext:
+ runAsUser: 0
+ runAsGroup: 0
+ command:
+ - /bin/sh
+ - -c
+ - |
+ set -euxo pipefail
+ cp -a /jellyfin/jellyfin-web/. /web-root
+ # remove injected OIDC script tags everywhere just in case
+ for f in $(find /web-root -type f -name 'index.html'); do
+ sed -i '/oidc\/inject/d' "$f"
+ printf '%s\n' "$f"
+ done
+ # clean any lingering OIDC plugin artifacts on the config volume
+ rm -rf "/config/plugins/OIDC Authentication_"* /config/plugins/configurations/JellyfinOIDCPlugin.v2.xml || true
+ volumeMounts:
+ - name: web-root
+ mountPath: /web-root
+ - name: config
+ mountPath: /config
nodeSelector:
jellyfin: "true"
securityContext:
@@ -28,142 +53,6 @@ spec:
fsGroup: 65532
fsGroupChangePolicy: OnRootMismatch
runAsGroup: 65532
- initContainers:
- - name: fetch-oidc-plugin
- image: alpine:3.20
- securityContext:
- runAsUser: 0
- env:
- - name: OIDC_PLUGIN_REPO
- value: "registry.bstein.dev/streaming/oidc-plugin"
- - name: OIDC_PLUGIN_TAG
- value: "10.11.5"
- - name: ORAS_USERNAME
- valueFrom:
- secretKeyRef:
- name: harbor-robot
- key: username
- optional: true
- - name: ORAS_PASSWORD
- valueFrom:
- secretKeyRef:
- name: harbor-robot
- key: password
- optional: true
- volumeMounts:
- - name: oidc-plugin
- mountPath: /plugin-src
- command: ["/bin/sh", "-c"]
- args:
- - |
- set -euo pipefail
- apk add --no-cache curl tar
- ORAS_VERSION=1.2.0
- curl -sSL "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz" | tar -xz -C /usr/local/bin oras
- ref="${OIDC_PLUGIN_REPO}:${OIDC_PLUGIN_TAG}"
- cd /plugin-src
- if [ -n "${ORAS_USERNAME:-}" ] && [ -n "${ORAS_PASSWORD:-}" ]; then
- oras login "$(echo "${OIDC_PLUGIN_REPO}" | cut -d/ -f1)" -u "${ORAS_USERNAME}" -p "${ORAS_PASSWORD}"
- fi
- oras pull --allow-path-traversal "${ref}"
- ls -lh /plugin-src
- - name: install-oidc-plugin
- image: alpine:3.20
- securityContext:
- runAsUser: 0
- env:
- - name: OIDC_PLUGIN_VERSION
- value: "1.0.2.0"
- - name: OIDC_ISSUER
- value: "https://sso.bstein.dev/realms/atlas"
- - name: OIDC_REDIRECT_URI
- value: "https://stream.bstein.dev/api/oidc/callback"
- - name: OIDC_LOGOUT_URI
- value: "https://sso.bstein.dev/realms/atlas/protocol/openid-connect/logout?redirect_uri=https://stream.bstein.dev/"
- - name: OIDC_SCOPES
- value: "openid,profile,email"
- - name: OIDC_ROLE_CLAIM
- value: "groups"
- - name: OIDC_CLIENT_ID
- valueFrom:
- secretKeyRef:
- name: jellyfin-oidc
- key: client-id
- - name: OIDC_CLIENT_SECRET
- valueFrom:
- secretKeyRef:
- name: jellyfin-oidc
- key: client-secret
- volumeMounts:
- - name: config
- mountPath: /config
- - name: oidc-plugin
- mountPath: /plugin-src
- command: ["/bin/sh", "-c"]
- args:
- - |
- set -euo pipefail
- if [ -z "${OIDC_CLIENT_ID:-}" ] || [ -z "${OIDC_CLIENT_SECRET:-}" ]; then
- echo "OIDC_CLIENT_ID or OIDC_CLIENT_SECRET missing; create secret jellyfin-oidc" >&2
- exit 1
- fi
- rm -rf "/config/plugins/LDAP Authentication_20.0.0.0"
- apk add --no-cache unzip
- plugin_dir="/config/plugins/OIDC Authentication_${OIDC_PLUGIN_VERSION}"
- config_dir="/config/plugins/configurations"
- plugin_zip="/plugin-src/OIDC_Authentication_${OIDC_PLUGIN_VERSION}-net9.zip"
- if [ ! -s "${plugin_zip}" ]; then
- echo "Plugin zip missing at ${plugin_zip}" >&2
- echo "Contents of /plugin-src:" >&2
- ls -lah /plugin-src >&2 || true
- exit 1
- fi
- rm -rf "${plugin_dir}"
- mkdir -p "${plugin_dir}" "${config_dir}"
- unzip -o "${plugin_zip}" -d "${plugin_dir}"
- rm -f "${plugin_dir}"/Microsoft.Extensions.*.dll
- cat >"${plugin_dir}/meta.json" <<'EOF'
- {
- "category": "Authentication",
- "changelog": "OIDC SSO authentication plugin; auto user creation and role mapping",
- "description": "OpenID Connect (OIDC) authentication provider for Jellyfin with SSO support.",
- "guid": "a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6",
- "name": "OIDC Authentication",
- "overview": "Enable Single Sign-On (SSO) for Jellyfin using an OpenID Connect provider.",
- "owner": "lolerskatez",
- "targetAbi": "10.11.5.0",
- "timestamp": "2025-12-17T04:00:00Z",
- "version": "1.0.2.0",
- "status": "Active",
- "autoUpdate": false,
- "imagePath": "",
- "assemblies": []
- }
- EOF
- scope_lines=""
- for s in $(echo "${OIDC_SCOPES}" | tr ',' ' '); do
- trimmed="$(echo "${s}" | xargs)"
- [ -z "${trimmed}" ] && continue
- scope_lines="${scope_lines} ${trimmed}\n"
- done
- config_file="${config_dir}/JellyfinOIDCPlugin.v2.xml"
- cat >"${config_file}" <
-
- ${OIDC_ISSUER}
- ${OIDC_CLIENT_ID}
- ${OIDC_CLIENT_SECRET}
-
- $(printf "%b" "${scope_lines}")
- ${OIDC_ROLE_CLAIM}
- ${OIDC_REDIRECT_URI}
- ${OIDC_LOGOUT_URI}
-
- true
- false
-
- EOF
- chown -R 1000:65532 "${plugin_dir}" "${config_file}"
runtimeClassName: nvidia
containers:
- name: jellyfin
@@ -183,22 +72,6 @@ spec:
value: "65532"
- name: UMASK
value: "002"
- lifecycle:
- postStart:
- exec:
- command:
- - /bin/sh
- - -c
- - |
- set -e
- target="/jellyfin/jellyfin-web/index.html"
- marker='api/oidc/inject'
- if grep -q "${marker}" "${target}"; then
- exit 0
- fi
- tmp="$(mktemp)"
- awk -v marker="${marker}" 'BEGIN{inserted=0} /<\/head>/ && !inserted {print " "; inserted=1} {print}' "${target}" > "${tmp}"
- cp "${tmp}" "${target}"
resources:
limits:
nvidia.com/gpu: 1
@@ -211,16 +84,35 @@ spec:
volumeMounts:
- name: config
mountPath: /config
+ # Override LDAP plugin configuration from a secret to avoid embedding credentials in the PVC.
+ - name: ldap-config
+ mountPath: /config/plugins/configurations/LDAP-Auth.xml
+ subPath: ldap-config.xml
- name: cache
mountPath: /cache
- name: media
mountPath: /media
+ - name: web-root
+ mountPath: /jellyfin/jellyfin-web
+ lifecycle:
+ postStart:
+ exec:
+ command:
+ - /bin/sh
+ - -c
+ - |
+ set -eux
+ for f in $(find /jellyfin/jellyfin-web -type f -name 'index.html'); do
+ sed -i '/oidc\/inject/d' "$f" || true
+ done
securityContext:
runAsUser: 0
runAsGroup: 0
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
volumes:
+ - name: web-root
+ emptyDir: {}
- name: config
persistentVolumeClaim:
claimName: jellyfin-config-astreae
@@ -230,5 +122,9 @@ spec:
- name: media
persistentVolumeClaim:
claimName: jellyfin-media-asteria-new
- - name: oidc-plugin
- emptyDir: {}
+ - name: ldap-config
+ secret:
+ secretName: jellyfin-ldap-config
+ items:
+ - key: ldap-config.xml
+ path: ldap-config.xml