From ae6bf01a89754995d1b12b4f80cd5fb4974ef772 Mon Sep 17 00:00:00 2001 From: ibizaman Date: Sat, 5 Aug 2023 12:46:14 -0700 Subject: [PATCH] jellyfin SSO config declarative --- README.md | 2 +- modules/jellyfin.nix | 140 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a4d5949..42c7ffa 100644 --- a/README.md +++ b/README.md @@ -37,5 +37,5 @@ services. Also, the design will be extendable to allow users to add services not - [X] Jellyfin - [ ] Export metrics to Prometheus. - [X] LDAP auth through `jellyfin_user` and `jellyfin_admin` LDAP groups. - - [ ] SSO auth. + - [X] SSO auth. - [X] Backup support. diff --git a/modules/jellyfin.nix b/modules/jellyfin.nix index 66f1823..7df1929 100644 --- a/modules/jellyfin.nix +++ b/modules/jellyfin.nix @@ -47,10 +47,40 @@ in dcdomain = lib.mkOption { type = lib.types.str; - description = "dc domain for ldap."; + description = "dc domain for ldap"; example = "dc=mydomain,dc=com"; }; + oidcProvider = lib.mkOption { + type = lib.types.str; + description = "OIDC provider name"; + default = "Authelia"; + }; + + oidcEndpoint = lib.mkOption { + type = lib.types.str; + description = "OIDC endpoint for SSO"; + example = "https://authelia.example.com"; + }; + + oidcClientID = lib.mkOption { + type = lib.types.str; + description = "Client ID for the OIDC endpoint"; + default = "jellyfin"; + }; + + oidcAdminUserGroup = lib.mkOption { + type = lib.types.str; + description = "OIDC admin group"; + default = "jellyfin_admin"; + }; + + oidcUserGroup = lib.mkOption { + type = lib.types.str; + description = "OIDC user group"; + default = "jellyfin_user"; + }; + sopsFile = lib.mkOption { type = lib.types.path; description = "Sops file location"; @@ -190,6 +220,13 @@ in group = "jellyfin"; restartUnits = [ "jellyfin.service" ]; }; + sops.secrets."jellyfin/sso_secret" = { + inherit (cfg) sopsFile; + mode = "0440"; + owner = "jellyfin"; + group = "jellyfin"; + restartUnits = [ "jellyfin.service" ]; + }; shb.backup.instances.jellyfin = { sourceDirectories = [ @@ -238,10 +275,109 @@ in ''; + + ssoConfig = pkgs.writeText "SSO-Auth.xml" '' + + + + + + + ${cfg.oidcProvider} + + + + ${cfg.oidcEndpoint} + ${cfg.oidcClientID} + %SSO_SECRET% + true + true + true + + + ${cfg.oidcAdminUserGroup} + + + ${cfg.oidcUserGroup} + + false + + groups + + groups + + + + + + + + ''; + + brandingConfig = pkgs.writeText "branding.xml" '' + + + <a href="https://${cfg.subdomain}.${cfg.domain}/SSO/OID/p/${cfg.oidcProvider}" class="raised cancel block emby-button authentik-sso"> + Sign in with ${cfg.oidcProvider}&nbsp; + <img alt="OpenID Connect (authentik)" title="OpenID Connect (authentik)" class="oauth-login-image" src="https://raw.githubusercontent.com/goauthentik/authentik/master/web/icons/icon.png"> + </a> + <a href="https://${cfg.subdomain}.${cfg.domain}/SSOViews/linking" class="raised cancel block emby-button authentik-sso"> + Link ${cfg.oidcProvider} config&nbsp; + </a> + <a href="${cfg.oidcEndpoint}" class="raised cancel block emby-button authentik-sso"> + ${cfg.oidcProvider} config&nbsp; + </a> + + + /* Hide this in lieu of authentik link */ + .emby-button.block.btnForgotPassword { + display: none; + } + + /* Make links look like buttons */ + a.raised.emby-button { + padding: 0.9em 1em; + color: inherit !important; + } + + /* Let disclaimer take full width */ + .disclaimerContainer { + display: block; + } + + /* Optionally, apply some styling to the `.authentik-sso` class, probably let users configure this */ + .authentik-sso { + /* idk set a background image or something lol */ + } + + .oauth-login-image { + height: 24px; + position: absolute; + top: 12px; + } + + true + + ''; in template ldapConfig "/var/lib/jellyfin/plugins/configurations/LDAP-Auth.xml" { "%LDAP_PASSWORD%" = "$(cat /run/secrets/jellyfin/ldap_password)"; - }; + } + + template ssoConfig "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml" { + "%SSO_SECRET%" = "$(cat /run/secrets/jellyfin/sso_secret)"; + } + + template brandingConfig "/var/lib/jellyfin/config/branding.xml" {"%a%" = "%a%";}; + + shb.authelia.oidcClients = [ + { + id = cfg.oidcClientID; + description = "Jellyfin"; + secret = "jbmVCAZluESWbOvbKQtHhjwcuaNjlMVaidMbJGKaHXHPOmwilCWYBFAQtrnohJzIhbuhWTBwhbDKLmdtyrLXeankWgXNspWCmJxzayHiHRvOPDbcsnquYReI"; + public = "false"; + authorization_policy = "one_factor"; + redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/sso/OID/r/${cfg.oidcProvider}" ]; + } + ]; # For backup