fafflix,cheeztv: declarative SSO + LDAP plugin auth config
Wire phase-1 plugin-based auth into both Jellyfin instances so browser users sign in via Authentik OIDC (jellyfin-plugin-sso) and native/app-password clients authenticate through the Authentik LDAP outpost (jellyfin-plugin-ldapauth), JIT-provisioning users. Both instances share the single Authentik "jellyfin" OAuth2 client, so the SSO config is identical; each reads the shared client_secret from its own namespace-scoped Vault path via a per-namespace VaultStaticSecret. - plugin-configmap.yaml: SSO-Auth.xml (OID provider "authentik", issuer identity.k8s.syd1.au.unkin.net/application/o/jellyfin/, EnableAllFolders, groups-claim role mapping akP-jellyfin-admin->admin / akP-jellyfin-user->user), LDAP-Auth.xml (server authentik-ldap.authentik.svc:6636 LDAPS, CreateUsersFromLdap), and branding.xml with a "Sign in with SSO" login link to /sso/OID/start/authentik. - statefulset.yaml: inject-plugin-config initContainer renders the XMLs into /config/plugins/configurations/, substituting client_secret / ldap_bind_password from the VSO-synced Secret (never committed) before Jellyfin starts. - vaultstaticsecret.yaml: per-namespace oauth-credentials VSS. Image tag is intentionally left at v0.1.3; the plugin DLLs land via a separate jellyfin-ha release and a follow-up pin bump.
This commit is contained in:
@@ -18,6 +18,7 @@ resources:
|
||||
- pvc-media-tv.yaml
|
||||
- pvc-media-movies.yaml
|
||||
- statefulset.yaml
|
||||
- plugin-configmap.yaml
|
||||
- pdb.yaml
|
||||
- service.yaml
|
||||
- valkey.yaml
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
# Declarative config for the browser-auth plugins bundled in the jellyfin-ha
|
||||
# image (jellyfin-plugin-sso, jellyfin-plugin-ldapauth). Rendered into
|
||||
# /config/plugins/configurations/ by the inject-plugin-config initContainer,
|
||||
# which substitutes the OidSecret / LdapBindPassword placeholders from the
|
||||
# VSO-synced oauth-credentials Secret so no secret is committed here. The SSO
|
||||
# provider key "authentik" must match the redirect path segment registered on
|
||||
# the shared Authentik "jellyfin" OAuth2 client. Roles/AdminRoles are matched
|
||||
# against the hierarchical Authentik groups claim (akP-jellyfin-user grants
|
||||
# login, akP-jellyfin-admin grants Jellyfin admin; global admins inherit the
|
||||
# latter via akR-global-admin).
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cheeztv-plugin-config
|
||||
namespace: cheeztv
|
||||
data:
|
||||
SSO-Auth.xml: |
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<SamlConfigs />
|
||||
<OidConfigs>
|
||||
<item>
|
||||
<key>
|
||||
<string>authentik</string>
|
||||
</key>
|
||||
<value>
|
||||
<PluginConfiguration>
|
||||
<OidEndpoint>https://identity.k8s.syd1.au.unkin.net/application/o/jellyfin/</OidEndpoint>
|
||||
<OidClientId>jellyfin</OidClientId>
|
||||
<OidSecret>@@CLIENT_SECRET@@</OidSecret>
|
||||
<Enabled>true</Enabled>
|
||||
<EnableAuthorization>true</EnableAuthorization>
|
||||
<EnableAllFolders>true</EnableAllFolders>
|
||||
<EnabledFolders />
|
||||
<AdminRoles>
|
||||
<string>akP-jellyfin-admin</string>
|
||||
</AdminRoles>
|
||||
<Roles>
|
||||
<string>akP-jellyfin-user</string>
|
||||
<string>akP-jellyfin-admin</string>
|
||||
</Roles>
|
||||
<EnableFolderRoles>false</EnableFolderRoles>
|
||||
<EnableLiveTvRoles>false</EnableLiveTvRoles>
|
||||
<EnableLiveTv>false</EnableLiveTv>
|
||||
<EnableLiveTvManagement>false</EnableLiveTvManagement>
|
||||
<LiveTvRoles />
|
||||
<LiveTvManagementRoles />
|
||||
<FolderRoleMappings />
|
||||
<RoleClaim>groups</RoleClaim>
|
||||
<OidScopes>
|
||||
<string>openid</string>
|
||||
<string>profile</string>
|
||||
<string>email</string>
|
||||
</OidScopes>
|
||||
<CanonicalLinks></CanonicalLinks>
|
||||
<DisableHttps>false</DisableHttps>
|
||||
<DoNotValidateEndpoints>false</DoNotValidateEndpoints>
|
||||
<DoNotValidateIssuerName>false</DoNotValidateIssuerName>
|
||||
<SchemeOverride>https</SchemeOverride>
|
||||
</PluginConfiguration>
|
||||
</value>
|
||||
</item>
|
||||
</OidConfigs>
|
||||
</PluginConfiguration>
|
||||
LDAP-Auth.xml: |
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<LdapServer>authentik-ldap.authentik.svc.cluster.local</LdapServer>
|
||||
<LdapPort>6636</LdapPort>
|
||||
<UseSsl>true</UseSsl>
|
||||
<UseStartTls>false</UseStartTls>
|
||||
<SkipSslVerify>true</SkipSslVerify>
|
||||
<LdapBindUser>cn=jellyfin-ldap,ou=users,DC=ldap,DC=goauthentik,DC=io</LdapBindUser>
|
||||
<LdapBindPassword>@@LDAP_BIND_PASSWORD@@</LdapBindPassword>
|
||||
<LdapBaseDn>ou=users,DC=ldap,DC=goauthentik,DC=io</LdapBaseDn>
|
||||
<LdapSearchFilter>(objectClass=user)</LdapSearchFilter>
|
||||
<LdapAdminBaseDn>ou=users,DC=ldap,DC=goauthentik,DC=io</LdapAdminBaseDn>
|
||||
<LdapAdminFilter>(memberOf=cn=akP-jellyfin-admin,ou=groups,DC=ldap,DC=goauthentik,DC=io)</LdapAdminFilter>
|
||||
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
||||
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
||||
<CreateUsersFromLdap>true</CreateUsersFromLdap>
|
||||
<AllowPassChange>false</AllowPassChange>
|
||||
<LdapUidAttribute>cn</LdapUidAttribute>
|
||||
<LdapUsernameAttribute>cn</LdapUsernameAttribute>
|
||||
<LdapPasswordAttribute>userPassword</LdapPasswordAttribute>
|
||||
<EnableAllFolders>true</EnableAllFolders>
|
||||
<EnabledFolders />
|
||||
</PluginConfiguration>
|
||||
branding.xml: |
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BrandingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<LoginDisclaimer><p style="text-align:center"><a href="/sso/OID/start/authentik">Sign in with SSO</a></p></LoginDisclaimer>
|
||||
<CustomCss></CustomCss>
|
||||
<SplashscreenEnabled>false</SplashscreenEnabled>
|
||||
</BrandingOptions>
|
||||
@@ -109,6 +109,57 @@ spec:
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
# Render the SSO/LDAP plugin configs into the shared config volume,
|
||||
# substituting the client secret and LDAP bind password from the
|
||||
# VSO-synced oauth-credentials Secret (never committed). Plugin configs
|
||||
# are fully managed here so they are overwritten every start; the login
|
||||
# button branding is written only when absent so admin edits survive.
|
||||
- name: inject-plugin-config
|
||||
image: busybox:1.37.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
mkdir -p /config/plugins/configurations /config/config
|
||||
chown 1000:1000 /config/plugins /config/plugins/configurations /config/config
|
||||
esc() { printf '%s' "$1" | sed -e 's/[&|\\]/\\&/g'; }
|
||||
cs=$(esc "${CLIENT_SECRET}")
|
||||
lp=$(esc "${LDAP_BIND_PASSWORD}")
|
||||
sed "s|@@CLIENT_SECRET@@|${cs}|" /templates/SSO-Auth.xml > /config/plugins/configurations/SSO-Auth.xml
|
||||
sed "s|@@LDAP_BIND_PASSWORD@@|${lp}|" /templates/LDAP-Auth.xml > /config/plugins/configurations/LDAP-Auth.xml
|
||||
chown 1000:1000 /config/plugins/configurations/SSO-Auth.xml /config/plugins/configurations/LDAP-Auth.xml
|
||||
chmod 600 /config/plugins/configurations/SSO-Auth.xml /config/plugins/configurations/LDAP-Auth.xml
|
||||
if [ ! -e /config/config/branding.xml ]; then
|
||||
cp /templates/branding.xml /config/config/branding.xml
|
||||
chown 1000:1000 /config/config/branding.xml
|
||||
chmod 664 /config/config/branding.xml
|
||||
fi
|
||||
env:
|
||||
- name: CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth-credentials
|
||||
key: client_secret
|
||||
optional: true
|
||||
- name: LDAP_BIND_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth-credentials
|
||||
key: ldap_bind_password
|
||||
optional: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
- name: plugin-config
|
||||
mountPath: /templates
|
||||
readOnly: true
|
||||
containers:
|
||||
- name: cheeztv
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/jellyfin-ha:v0.1.3
|
||||
@@ -222,6 +273,9 @@ spec:
|
||||
subPath: kids
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: plugin-config
|
||||
configMap:
|
||||
name: cheeztv-plugin-config
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: cheeztv-config
|
||||
|
||||
@@ -22,3 +22,27 @@ spec:
|
||||
refreshAfter: 5m
|
||||
type: kv-v2
|
||||
vaultAuthRef: default
|
||||
---
|
||||
# Shared Authentik "jellyfin" OAuth2 client secret (key: client_secret) plus the
|
||||
# LDAP outpost bind password (key: ldap_bind_password) for the auth plugins.
|
||||
# The default k8s role's templated policy is namespace-scoped
|
||||
# (kv/data/kubernetes/namespace/{{sa_namespace}}/{{sa_name}}/*), so each instance
|
||||
# reads its own namespace path; the SAME shared values must be seeded at both
|
||||
# fafflix and cheeztv paths. VSO syncs into the oauth-credentials Secret, whose
|
||||
# keys the inject-plugin-config initContainer substitutes into the plugin XML.
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: oauth-credentials
|
||||
namespace: cheeztv
|
||||
spec:
|
||||
destination:
|
||||
create: true
|
||||
name: oauth-credentials
|
||||
overwrite: true
|
||||
hmacSecretData: true
|
||||
mount: kv
|
||||
path: kubernetes/namespace/cheeztv/default/oauth-credentials
|
||||
refreshAfter: 5m
|
||||
type: kv-v2
|
||||
vaultAuthRef: default
|
||||
|
||||
@@ -18,6 +18,7 @@ resources:
|
||||
- pvc-media-tv.yaml
|
||||
- pvc-media-movies.yaml
|
||||
- statefulset.yaml
|
||||
- plugin-configmap.yaml
|
||||
- pdb.yaml
|
||||
- service.yaml
|
||||
- valkey.yaml
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
# Declarative config for the browser-auth plugins bundled in the jellyfin-ha
|
||||
# image (jellyfin-plugin-sso, jellyfin-plugin-ldapauth). Rendered into
|
||||
# /config/plugins/configurations/ by the inject-plugin-config initContainer,
|
||||
# which substitutes the OidSecret / LdapBindPassword placeholders from the
|
||||
# VSO-synced oauth-credentials Secret so no secret is committed here. The SSO
|
||||
# provider key "authentik" must match the redirect path segment registered on
|
||||
# the shared Authentik "jellyfin" OAuth2 client. Roles/AdminRoles are matched
|
||||
# against the hierarchical Authentik groups claim (akP-jellyfin-user grants
|
||||
# login, akP-jellyfin-admin grants Jellyfin admin; global admins inherit the
|
||||
# latter via akR-global-admin).
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: fafflix-plugin-config
|
||||
namespace: fafflix
|
||||
data:
|
||||
SSO-Auth.xml: |
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<SamlConfigs />
|
||||
<OidConfigs>
|
||||
<item>
|
||||
<key>
|
||||
<string>authentik</string>
|
||||
</key>
|
||||
<value>
|
||||
<PluginConfiguration>
|
||||
<OidEndpoint>https://identity.k8s.syd1.au.unkin.net/application/o/jellyfin/</OidEndpoint>
|
||||
<OidClientId>jellyfin</OidClientId>
|
||||
<OidSecret>@@CLIENT_SECRET@@</OidSecret>
|
||||
<Enabled>true</Enabled>
|
||||
<EnableAuthorization>true</EnableAuthorization>
|
||||
<EnableAllFolders>true</EnableAllFolders>
|
||||
<EnabledFolders />
|
||||
<AdminRoles>
|
||||
<string>akP-jellyfin-admin</string>
|
||||
</AdminRoles>
|
||||
<Roles>
|
||||
<string>akP-jellyfin-user</string>
|
||||
<string>akP-jellyfin-admin</string>
|
||||
</Roles>
|
||||
<EnableFolderRoles>false</EnableFolderRoles>
|
||||
<EnableLiveTvRoles>false</EnableLiveTvRoles>
|
||||
<EnableLiveTv>false</EnableLiveTv>
|
||||
<EnableLiveTvManagement>false</EnableLiveTvManagement>
|
||||
<LiveTvRoles />
|
||||
<LiveTvManagementRoles />
|
||||
<FolderRoleMappings />
|
||||
<RoleClaim>groups</RoleClaim>
|
||||
<OidScopes>
|
||||
<string>openid</string>
|
||||
<string>profile</string>
|
||||
<string>email</string>
|
||||
</OidScopes>
|
||||
<CanonicalLinks></CanonicalLinks>
|
||||
<DisableHttps>false</DisableHttps>
|
||||
<DoNotValidateEndpoints>false</DoNotValidateEndpoints>
|
||||
<DoNotValidateIssuerName>false</DoNotValidateIssuerName>
|
||||
<SchemeOverride>https</SchemeOverride>
|
||||
</PluginConfiguration>
|
||||
</value>
|
||||
</item>
|
||||
</OidConfigs>
|
||||
</PluginConfiguration>
|
||||
LDAP-Auth.xml: |
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<LdapServer>authentik-ldap.authentik.svc.cluster.local</LdapServer>
|
||||
<LdapPort>6636</LdapPort>
|
||||
<UseSsl>true</UseSsl>
|
||||
<UseStartTls>false</UseStartTls>
|
||||
<SkipSslVerify>true</SkipSslVerify>
|
||||
<LdapBindUser>cn=jellyfin-ldap,ou=users,DC=ldap,DC=goauthentik,DC=io</LdapBindUser>
|
||||
<LdapBindPassword>@@LDAP_BIND_PASSWORD@@</LdapBindPassword>
|
||||
<LdapBaseDn>ou=users,DC=ldap,DC=goauthentik,DC=io</LdapBaseDn>
|
||||
<LdapSearchFilter>(objectClass=user)</LdapSearchFilter>
|
||||
<LdapAdminBaseDn>ou=users,DC=ldap,DC=goauthentik,DC=io</LdapAdminBaseDn>
|
||||
<LdapAdminFilter>(memberOf=cn=akP-jellyfin-admin,ou=groups,DC=ldap,DC=goauthentik,DC=io)</LdapAdminFilter>
|
||||
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
||||
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
||||
<CreateUsersFromLdap>true</CreateUsersFromLdap>
|
||||
<AllowPassChange>false</AllowPassChange>
|
||||
<LdapUidAttribute>cn</LdapUidAttribute>
|
||||
<LdapUsernameAttribute>cn</LdapUsernameAttribute>
|
||||
<LdapPasswordAttribute>userPassword</LdapPasswordAttribute>
|
||||
<EnableAllFolders>true</EnableAllFolders>
|
||||
<EnabledFolders />
|
||||
</PluginConfiguration>
|
||||
branding.xml: |
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BrandingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<LoginDisclaimer><p style="text-align:center"><a href="/sso/OID/start/authentik">Sign in with SSO</a></p></LoginDisclaimer>
|
||||
<CustomCss></CustomCss>
|
||||
<SplashscreenEnabled>false</SplashscreenEnabled>
|
||||
</BrandingOptions>
|
||||
@@ -109,6 +109,57 @@ spec:
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
# Render the SSO/LDAP plugin configs into the shared config volume,
|
||||
# substituting the client secret and LDAP bind password from the
|
||||
# VSO-synced oauth-credentials Secret (never committed). Plugin configs
|
||||
# are fully managed here so they are overwritten every start; the login
|
||||
# button branding is written only when absent so admin edits survive.
|
||||
- name: inject-plugin-config
|
||||
image: busybox:1.37.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
mkdir -p /config/plugins/configurations /config/config
|
||||
chown 1000:1000 /config/plugins /config/plugins/configurations /config/config
|
||||
esc() { printf '%s' "$1" | sed -e 's/[&|\\]/\\&/g'; }
|
||||
cs=$(esc "${CLIENT_SECRET}")
|
||||
lp=$(esc "${LDAP_BIND_PASSWORD}")
|
||||
sed "s|@@CLIENT_SECRET@@|${cs}|" /templates/SSO-Auth.xml > /config/plugins/configurations/SSO-Auth.xml
|
||||
sed "s|@@LDAP_BIND_PASSWORD@@|${lp}|" /templates/LDAP-Auth.xml > /config/plugins/configurations/LDAP-Auth.xml
|
||||
chown 1000:1000 /config/plugins/configurations/SSO-Auth.xml /config/plugins/configurations/LDAP-Auth.xml
|
||||
chmod 600 /config/plugins/configurations/SSO-Auth.xml /config/plugins/configurations/LDAP-Auth.xml
|
||||
if [ ! -e /config/config/branding.xml ]; then
|
||||
cp /templates/branding.xml /config/config/branding.xml
|
||||
chown 1000:1000 /config/config/branding.xml
|
||||
chmod 664 /config/config/branding.xml
|
||||
fi
|
||||
env:
|
||||
- name: CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth-credentials
|
||||
key: client_secret
|
||||
optional: true
|
||||
- name: LDAP_BIND_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth-credentials
|
||||
key: ldap_bind_password
|
||||
optional: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
- name: plugin-config
|
||||
mountPath: /templates
|
||||
readOnly: true
|
||||
containers:
|
||||
- name: fafflix
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/jellyfin-ha:v0.1.3
|
||||
@@ -236,6 +287,9 @@ spec:
|
||||
subPath: kids
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: plugin-config
|
||||
configMap:
|
||||
name: fafflix-plugin-config
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: fafflix-config
|
||||
|
||||
@@ -22,3 +22,27 @@ spec:
|
||||
refreshAfter: 5m
|
||||
type: kv-v2
|
||||
vaultAuthRef: default
|
||||
---
|
||||
# Shared Authentik "jellyfin" OAuth2 client secret (key: client_secret) plus the
|
||||
# LDAP outpost bind password (key: ldap_bind_password) for the auth plugins.
|
||||
# The default k8s role's templated policy is namespace-scoped
|
||||
# (kv/data/kubernetes/namespace/{{sa_namespace}}/{{sa_name}}/*), so each instance
|
||||
# reads its own namespace path; the SAME shared values must be seeded at both
|
||||
# fafflix and cheeztv paths. VSO syncs into the oauth-credentials Secret, whose
|
||||
# keys the inject-plugin-config initContainer substitutes into the plugin XML.
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: oauth-credentials
|
||||
namespace: fafflix
|
||||
spec:
|
||||
destination:
|
||||
create: true
|
||||
name: oauth-credentials
|
||||
overwrite: true
|
||||
hmacSecretData: true
|
||||
mount: kv
|
||||
path: kubernetes/namespace/fafflix/default/oauth-credentials
|
||||
refreshAfter: 5m
|
||||
type: kv-v2
|
||||
vaultAuthRef: default
|
||||
|
||||
Reference in New Issue
Block a user