168 lines
6.2 KiB
Nix
168 lines
6.2 KiB
Nix
{ config, lib, pkgs, homeyConfig, ... }:
|
|
|
|
# Jellyfin — media server.
|
|
#
|
|
# Auth model: Jellyfin-native (no Authelia SSO — Jellyfin intercepts the
|
|
# forward_auth flow in ways that break SSO). LDAP plugin can be installed
|
|
# manually via the UI; the XML config is pre-seeded on first start so the
|
|
# plugin settings are pre-populated when the user opens the config page.
|
|
#
|
|
# Manual step after first deploy:
|
|
# 1. Dashboard → Plugins → Catalog → search "LDAP Authentication" → Install
|
|
# 2. Restart Jellyfin (Dashboard → Administration → Restart)
|
|
# 3. Dashboard → Plugins → LDAP Authentication → verify fields → Save
|
|
#
|
|
# Volume layout:
|
|
# <dataDir>/jellyfin/config/ → /config
|
|
# <dataDir>/media/movies/ → /data/movies
|
|
# <dataDir>/media/tvshows/ → /data/tvshows
|
|
# <dataDir>/media/general/ → /data/general
|
|
# <dataDir>/media/complete/ → /data/complete
|
|
|
|
let
|
|
cfg = config.homey.jellyfin;
|
|
dataDir = config.homey.storage.mountPoint;
|
|
domain = homeyConfig.domain;
|
|
|
|
ldapBaseDn = lib.concatStringsSep ","
|
|
(map (p: "dc=${p}") (lib.splitString "." domain));
|
|
|
|
ldapConfigDir = "${dataDir}/jellyfin/config/plugins/configurations";
|
|
ldapConfigXml = "${ldapConfigDir}/LDAP-Auth.xml";
|
|
in
|
|
{
|
|
options.homey.jellyfin = {
|
|
enable = lib.mkEnableOption "Jellyfin media server";
|
|
|
|
image = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "docker.io/jellyfin/jellyfin:latest";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 8096;
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# -----------------------------------------------------------------------
|
|
# Secrets
|
|
# -----------------------------------------------------------------------
|
|
sops.secrets."openldap/ro_password" = { owner = "root"; };
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Container
|
|
# -----------------------------------------------------------------------
|
|
virtualisation.oci-containers.containers.jellyfin = {
|
|
image = cfg.image;
|
|
ports = [ "127.0.0.1:${toString cfg.port}:8096" ];
|
|
|
|
environment = {
|
|
JELLYFIN_PublishedServerUrl = "https://media.${domain}";
|
|
PUID = "1000";
|
|
PGID = "1000";
|
|
};
|
|
|
|
volumes = [
|
|
"${dataDir}/jellyfin/config:/config"
|
|
"${dataDir}/media/movies:/data/movies:ro"
|
|
"${dataDir}/media/tvshows:/data/tvshows:ro"
|
|
"${dataDir}/media/general:/data/general:ro"
|
|
"${dataDir}/media/complete:/data/complete:ro"
|
|
];
|
|
|
|
extraOptions = [ "--network=homey" ];
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# ExecStartPre: pre-seed LDAP plugin config on first start only
|
|
# -----------------------------------------------------------------------
|
|
systemd.services."podman-jellyfin" = {
|
|
serviceConfig.ExecStartPre = [
|
|
(pkgs.writeShellScript "jellyfin-seed-ldap-config" ''
|
|
set -euo pipefail
|
|
# Only write on first start; preserve any UI edits on subsequent starts.
|
|
if [ -f "${ldapConfigXml}" ]; then
|
|
exit 0
|
|
fi
|
|
mkdir -p "${ldapConfigDir}"
|
|
RO_PASSWORD=$(cat ${config.sops.secrets."openldap/ro_password".path})
|
|
cat > "${ldapConfigXml}" <<EOF
|
|
<?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>openldap</LdapServer>
|
|
<LdapPort>389</LdapPort>
|
|
<UseSsl>false</UseSsl>
|
|
<UseStartTls>false</UseStartTls>
|
|
<SkipSslVerify>false</SkipSslVerify>
|
|
<LdapBindUser>cn=readonly,${ldapBaseDn}</LdapBindUser>
|
|
<LdapBindPassword>$RO_PASSWORD</LdapBindPassword>
|
|
<LdapBaseDn>ou=users,${ldapBaseDn}</LdapBaseDn>
|
|
<LdapSearchFilter></LdapSearchFilter>
|
|
<LdapSearchAttributes>uid,cn,mail,displayName</LdapSearchAttributes>
|
|
<LdapUsernameAttribute>uid</LdapUsernameAttribute>
|
|
<LdapPasswordAttribute>userPassword</LdapPasswordAttribute>
|
|
<EnableAllFolders>true</EnableAllFolders>
|
|
<EnabledFolders />
|
|
<AdminBaseDn>cn=admins,ou=groups,${ldapBaseDn}</AdminBaseDn>
|
|
<AdminFilter></AdminFilter>
|
|
<AllowPassChange>false</AllowPassChange>
|
|
<CreateUsersFromLdap>true</CreateUsersFromLdap>
|
|
<EnableCaseSensitiveUsername>false</EnableCaseSensitiveUsername>
|
|
<LdapClientCertPath></LdapClientCertPath>
|
|
<LdapClientKeyPath></LdapClientKeyPath>
|
|
<LdapRootCaPath></LdapRootCaPath>
|
|
<PasswordResetUrl></PasswordResetUrl>
|
|
</PluginConfiguration>
|
|
EOF
|
|
chmod 600 "${ldapConfigXml}"
|
|
'')
|
|
];
|
|
after = lib.mkAfter [ "mnt-data.mount" "podman-homey-network.service" ];
|
|
requires = lib.mkAfter [ "mnt-data.mount" "podman-homey-network.service" ];
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Authelia access control — bypass; Jellyfin has its own login UI and
|
|
# does not work correctly behind SSO forward_auth.
|
|
# -----------------------------------------------------------------------
|
|
homey.authelia.accessControlRules = [{
|
|
priority = 60;
|
|
domain = [ "media.${domain}" ];
|
|
policy = "bypass";
|
|
}];
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Caddy virtual host — no forward_auth; Jellyfin manages its own auth
|
|
# -----------------------------------------------------------------------
|
|
homey.caddy.virtualHosts = [{
|
|
subdomain = "media";
|
|
port = cfg.port;
|
|
auth = false;
|
|
}];
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Storage directories
|
|
# -----------------------------------------------------------------------
|
|
homey.storage.extraDirs = [
|
|
{ path = "jellyfin"; }
|
|
{ path = "jellyfin/config"; }
|
|
];
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Backup
|
|
# -----------------------------------------------------------------------
|
|
homey.backup.extraPaths = [ "${dataDir}/jellyfin" ];
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Uptime Kuma monitor
|
|
# -----------------------------------------------------------------------
|
|
homey.monitoring.monitors = [{
|
|
name = "Jellyfin";
|
|
url = "https://media.${domain}";
|
|
interval = 60;
|
|
}];
|
|
};
|
|
}
|