add vm tests for jellyfin and regroup ldap and sso options
This commit is contained in:
parent
97f213a137
commit
e80cc0d3aa
4 changed files with 439 additions and 78 deletions
|
@ -100,6 +100,7 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// (vm_test "authelia" ./test/vm/authelia.nix)
|
// (vm_test "authelia" ./test/vm/authelia.nix)
|
||||||
|
// (vm_test "jellyfin" ./test/vm/jellyfin.nix)
|
||||||
// (vm_test "ldap" ./test/vm/ldap.nix)
|
// (vm_test "ldap" ./test/vm/ldap.nix)
|
||||||
// (vm_test "lib" ./test/vm/lib.nix)
|
// (vm_test "lib" ./test/vm/lib.nix)
|
||||||
// (vm_test "postgresql" ./test/vm/postgresql.nix)
|
// (vm_test "postgresql" ./test/vm/postgresql.nix)
|
||||||
|
|
|
@ -22,9 +22,9 @@ rec {
|
||||||
''
|
''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
set -x
|
set -x
|
||||||
|
mkdir -p $(dirname ${templatePath})
|
||||||
ln -fs ${file} ${templatePath}
|
ln -fs ${file} ${templatePath}
|
||||||
rm -f ${resultPath}
|
rm -f ${resultPath}
|
||||||
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath}
|
|
||||||
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath} > ${resultPath}
|
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath} > ${resultPath}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
|
@ -30,62 +30,94 @@ in
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
ldapHost = lib.mkOption {
|
ldap = lib.mkOption {
|
||||||
|
description = "LDAP configuration.";
|
||||||
|
default = {};
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "LDAP";
|
||||||
|
|
||||||
|
host = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "host serving the LDAP server";
|
description = "Host serving the LDAP server.";
|
||||||
example = "127.0.0.1";
|
example = "127.0.0.1";
|
||||||
};
|
};
|
||||||
|
|
||||||
ldapPort = lib.mkOption {
|
port = lib.mkOption {
|
||||||
type = lib.types.int;
|
type = lib.types.int;
|
||||||
description = "port where the LDAP server is listening";
|
description = "Port where the LDAP server is listening.";
|
||||||
example = 389;
|
example = 389;
|
||||||
};
|
};
|
||||||
|
|
||||||
dcdomain = lib.mkOption {
|
dcdomain = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "dc domain for ldap";
|
description = "DC domain for LDAP.";
|
||||||
example = "dc=mydomain,dc=com";
|
example = "dc=mydomain,dc=com";
|
||||||
};
|
};
|
||||||
|
|
||||||
oidcProvider = lib.mkOption {
|
userGroup = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "LDAP user group";
|
||||||
|
default = "jellyfin_user";
|
||||||
|
};
|
||||||
|
|
||||||
|
adminGroup = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "LDAP admin group";
|
||||||
|
default = "jellyfin_admin";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = "File containing the LDAP admin password.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sso = lib.mkOption {
|
||||||
|
description = "SSO configuration.";
|
||||||
|
default = {};
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "SSO";
|
||||||
|
|
||||||
|
provider = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "OIDC provider name";
|
description = "OIDC provider name";
|
||||||
default = "Authelia";
|
default = "Authelia";
|
||||||
};
|
};
|
||||||
|
|
||||||
authEndpoint = lib.mkOption {
|
endpoint = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "OIDC endpoint for SSO";
|
description = "OIDC endpoint for SSO";
|
||||||
example = "https://authelia.example.com";
|
example = "https://authelia.example.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
oidcClientID = lib.mkOption {
|
clientID = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "Client ID for the OIDC endpoint";
|
description = "Client ID for the OIDC endpoint";
|
||||||
default = "jellyfin";
|
default = "jellyfin";
|
||||||
};
|
};
|
||||||
|
|
||||||
oidcAdminUserGroup = lib.mkOption {
|
adminUserGroup = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "OIDC admin group";
|
description = "OIDC admin group";
|
||||||
default = "jellyfin_admin";
|
default = "jellyfin_admin";
|
||||||
};
|
};
|
||||||
|
|
||||||
oidcUserGroup = lib.mkOption {
|
userGroup = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "OIDC user group";
|
description = "OIDC user group";
|
||||||
default = "jellyfin_user";
|
default = "jellyfin_user";
|
||||||
};
|
};
|
||||||
|
|
||||||
ldapPasswordFile = lib.mkOption {
|
secretFile = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
description = "File containing the LDAP admin password.";
|
description = "File containing the OIDC shared secret.";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
ssoSecretFile = lib.mkOption {
|
|
||||||
type = lib.types.path;
|
|
||||||
description = "File containing the SSO shared secret.";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,6 +139,8 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.nginx.enable = true;
|
||||||
|
|
||||||
# Take advice from https://jellyfin.org/docs/general/networking/nginx/ and https://nixos.wiki/wiki/Plex
|
# Take advice from https://jellyfin.org/docs/general/networking/nginx/ and https://nixos.wiki/wiki/Plex
|
||||||
services.nginx.virtualHosts."${fqdn}" = {
|
services.nginx.virtualHosts."${fqdn}" = {
|
||||||
forceSSL = !(isNull cfg.ssl);
|
forceSSL = !(isNull cfg.ssl);
|
||||||
|
@ -238,17 +272,17 @@ in
|
||||||
ldapConfig = pkgs.writeText "LDAP-Auth.xml" ''
|
ldapConfig = pkgs.writeText "LDAP-Auth.xml" ''
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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">
|
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<LdapServer>${cfg.ldapHost}</LdapServer>
|
<LdapServer>${cfg.ldap.host}</LdapServer>
|
||||||
<LdapPort>${builtins.toString cfg.ldapPort}</LdapPort>
|
<LdapPort>${builtins.toString cfg.ldap.port}</LdapPort>
|
||||||
<UseSsl>false</UseSsl>
|
<UseSsl>false</UseSsl>
|
||||||
<UseStartTls>false</UseStartTls>
|
<UseStartTls>false</UseStartTls>
|
||||||
<SkipSslVerify>false</SkipSslVerify>
|
<SkipSslVerify>false</SkipSslVerify>
|
||||||
<LdapBindUser>uid=admin,ou=people,${cfg.dcdomain}</LdapBindUser>
|
<LdapBindUser>uid=admin,ou=people,${cfg.ldap.dcdomain}</LdapBindUser>
|
||||||
<LdapBindPassword>%LDAP_PASSWORD%</LdapBindPassword>
|
<LdapBindPassword>%LDAP_PASSWORD%</LdapBindPassword>
|
||||||
<LdapBaseDn>ou=people,${cfg.dcdomain}</LdapBaseDn>
|
<LdapBaseDn>ou=people,${cfg.ldap.dcdomain}</LdapBaseDn>
|
||||||
<LdapSearchFilter>(memberof=cn=jellyfin_user,ou=groups,${cfg.dcdomain})</LdapSearchFilter>
|
<LdapSearchFilter>(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})</LdapSearchFilter>
|
||||||
<LdapAdminBaseDn>ou=people,${cfg.dcdomain}</LdapAdminBaseDn>
|
<LdapAdminBaseDn>ou=people,${cfg.ldap.dcdomain}</LdapAdminBaseDn>
|
||||||
<LdapAdminFilter>(memberof=cn=jellyfin_admin,ou=groups,${cfg.dcdomain})</LdapAdminFilter>
|
<LdapAdminFilter>(memberof=cn=${cfg.ldap.adminGroup},ou=groups,${cfg.ldap.dcdomain})</LdapAdminFilter>
|
||||||
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
||||||
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
||||||
<LdapClientCertPath />
|
<LdapClientCertPath />
|
||||||
|
@ -271,22 +305,22 @@ in
|
||||||
<OidConfigs>
|
<OidConfigs>
|
||||||
<item>
|
<item>
|
||||||
<key>
|
<key>
|
||||||
<string>${cfg.oidcProvider}</string>
|
<string>${cfg.sso.provider}</string>
|
||||||
</key>
|
</key>
|
||||||
<value>
|
<value>
|
||||||
<PluginConfiguration>
|
<PluginConfiguration>
|
||||||
<OidEndpoint>${cfg.authEndpoint}</OidEndpoint>
|
<OidEndpoint>${cfg.sso.endpoint}</OidEndpoint>
|
||||||
<OidClientId>${cfg.oidcClientID}</OidClientId>
|
<OidClientId>${cfg.sso.clientID}</OidClientId>
|
||||||
<OidSecret>%SSO_SECRET%</OidSecret>
|
<OidSecret>%SSO_SECRET%</OidSecret>
|
||||||
<Enabled>true</Enabled>
|
<Enabled>true</Enabled>
|
||||||
<EnableAuthorization>true</EnableAuthorization>
|
<EnableAuthorization>true</EnableAuthorization>
|
||||||
<EnableAllFolders>true</EnableAllFolders>
|
<EnableAllFolders>true</EnableAllFolders>
|
||||||
<EnabledFolders />
|
<EnabledFolders />
|
||||||
<AdminRoles>
|
<AdminRoles>
|
||||||
<string>${cfg.oidcAdminUserGroup}</string>
|
<string>${cfg.sso.adminUserGroup}</string>
|
||||||
</AdminRoles>
|
</AdminRoles>
|
||||||
<Roles>
|
<Roles>
|
||||||
<string>${cfg.oidcUserGroup}</string>
|
<string>${cfg.sso.userGroup}</string>
|
||||||
</Roles>
|
</Roles>
|
||||||
<EnableFolderRoles>false</EnableFolderRoles>
|
<EnableFolderRoles>false</EnableFolderRoles>
|
||||||
<FolderRoleMappings />
|
<FolderRoleMappings />
|
||||||
|
@ -305,15 +339,15 @@ in
|
||||||
brandingConfig = pkgs.writeText "branding.xml" ''
|
brandingConfig = pkgs.writeText "branding.xml" ''
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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">
|
<BrandingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<LoginDisclaimer><a href="https://${cfg.subdomain}.${cfg.domain}/SSO/OID/p/${cfg.oidcProvider}" class="raised cancel block emby-button authentik-sso">
|
<LoginDisclaimer><a href="https://${cfg.subdomain}.${cfg.domain}/SSO/OID/p/${cfg.sso.provider}" class="raised cancel block emby-button authentik-sso">
|
||||||
Sign in with ${cfg.oidcProvider}&nbsp;
|
Sign in with ${cfg.sso.provider}&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">
|
<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>
|
||||||
<a href="https://${cfg.subdomain}.${cfg.domain}/SSOViews/linking" class="raised cancel block emby-button authentik-sso">
|
<a href="https://${cfg.subdomain}.${cfg.domain}/SSOViews/linking" class="raised cancel block emby-button authentik-sso">
|
||||||
Link ${cfg.oidcProvider} config&nbsp;
|
Link ${cfg.sso.provider} config&nbsp;
|
||||||
</a>
|
</a>
|
||||||
<a href="${cfg.authEndpoint}" class="raised cancel block emby-button authentik-sso">
|
<a href="${cfg.sso.endpoint}" class="raised cancel block emby-button authentik-sso">
|
||||||
${cfg.oidcProvider} config&nbsp;
|
${cfg.sso.provider} config&nbsp;
|
||||||
</a>
|
</a>
|
||||||
</LoginDisclaimer>
|
</LoginDisclaimer>
|
||||||
<CustomCss>
|
<CustomCss>
|
||||||
|
@ -348,36 +382,36 @@ in
|
||||||
</BrandingOptions>
|
</BrandingOptions>
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
shblib.replaceSecretsScript {
|
lib.strings.optionalString cfg.ldap.enable (shblib.replaceSecretsScript {
|
||||||
file = ldapConfig;
|
file = ldapConfig;
|
||||||
resultPath = "/var/lib/jellyfin/plugins/configurations/LDAP-Auth.xml";
|
resultPath = "/var/lib/jellyfin/plugins/configurations/LDAP-Auth.xml";
|
||||||
replacements = {
|
replacements = {
|
||||||
"%LDAP_PASSWORD%" = "$(cat ${cfg.ldapPasswordFile})";
|
"%LDAP_PASSWORD%" = "$(cat ${cfg.ldap.passwordFile})";
|
||||||
};
|
};
|
||||||
}
|
})
|
||||||
+ shblib.replaceSecretsScript {
|
+ lib.strings.optionalString cfg.sso.enable (shblib.replaceSecretsScript {
|
||||||
file = ssoConfig;
|
file = ssoConfig;
|
||||||
resultPath = "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml";
|
resultPath = "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml";
|
||||||
replacements = {
|
replacements = {
|
||||||
"%SSO_SECRET%" = "$(cat ${cfg.ssoSecretFile})";
|
"%SSO_SECRET%" = "$(cat ${cfg.sso.secretFile})";
|
||||||
};
|
};
|
||||||
}
|
})
|
||||||
+ shblib.replaceSecretsScript {
|
+ lib.strings.optionalString cfg.sso.enable (shblib.replaceSecretsScript {
|
||||||
file = brandingConfig;
|
file = brandingConfig;
|
||||||
resultPath = "/var/lib/jellyfin/config/branding.xml";
|
resultPath = "/var/lib/jellyfin/config/branding.xml";
|
||||||
replacements = {
|
replacements = {
|
||||||
"%a%" = "%a%";
|
"%a%" = "%a%";
|
||||||
};
|
};
|
||||||
};
|
});
|
||||||
|
|
||||||
shb.authelia.oidcClients = [
|
shb.authelia.oidcClients = lib.lists.optionals (!(isNull cfg.sso)) [
|
||||||
{
|
{
|
||||||
id = cfg.oidcClientID;
|
id = cfg.sso.clientID;
|
||||||
description = "Jellyfin";
|
description = "Jellyfin";
|
||||||
secret.source = cfg.ssoSecretFile;
|
secret.source = cfg.sso.secretFile;
|
||||||
public = false;
|
public = false;
|
||||||
authorization_policy = "one_factor";
|
authorization_policy = "one_factor";
|
||||||
redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/sso/OID/r/${cfg.oidcProvider}" ];
|
redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/sso/OID/r/${cfg.sso.provider}" ];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
326
test/vm/jellyfin.nix
Normal file
326
test/vm/jellyfin.nix
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
{
|
||||||
|
basic = pkgs.nixosTest {
|
||||||
|
name = "jellyfin-basic";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
shb.backup = lib.mkOption { type = lib.types.anything; };
|
||||||
|
shb.authelia = lib.mkOption { type = lib.types.anything; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
../../modules/services/jellyfin.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
shb.jellyfin = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "j";
|
||||||
|
};
|
||||||
|
# Nginx port.
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
# TODO: Test login
|
||||||
|
testScript = { nodes, ... }: ''
|
||||||
|
import json
|
||||||
|
|
||||||
|
def curl(target, format, endpoint):
|
||||||
|
return json.loads(target.succeed(
|
||||||
|
"curl --fail-with-body --silent --show-error --output /dev/null --location"
|
||||||
|
+ " --connect-to j.example.com:443:server:443"
|
||||||
|
+ " --connect-to j.example.com:80:server:80"
|
||||||
|
+ f" --write-out '{format}'"
|
||||||
|
+ " " + endpoint
|
||||||
|
))
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_unit("jellyfin.service")
|
||||||
|
server.wait_for_unit("nginx.service")
|
||||||
|
server.wait_for_open_port(8096)
|
||||||
|
|
||||||
|
response = curl(client, """{"code":%{response_code}}""", "http://j.example.com")
|
||||||
|
|
||||||
|
if response['code'] != 200:
|
||||||
|
raise Exception(f"Code is {response['code']}")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
ldap = pkgs.nixosTest {
|
||||||
|
name = "jellyfin-ldap";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
shb.backup = lib.mkOption { type = lib.types.anything; };
|
||||||
|
shb.authelia = lib.mkOption { type = lib.types.anything; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
../../modules/blocks/ldap.nix
|
||||||
|
../../modules/services/jellyfin.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
shb.ldap = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "ldap";
|
||||||
|
ldapPort = 3890;
|
||||||
|
webUIListenPort = 17170;
|
||||||
|
dcdomain = "dc=example,dc=com";
|
||||||
|
ldapUserPasswordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
|
jwtSecretFile = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.jellyfin = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "j";
|
||||||
|
|
||||||
|
ldap = {
|
||||||
|
enable = true;
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = config.shb.ldap.ldapPort;
|
||||||
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
|
passwordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# Nginx port.
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
# TODO: Test login with ldap user
|
||||||
|
testScript = { nodes, ... }: ''
|
||||||
|
import json
|
||||||
|
|
||||||
|
def curl(target, format, endpoint):
|
||||||
|
return json.loads(target.succeed(
|
||||||
|
"curl --fail-with-body --silent --show-error --output /dev/null --location"
|
||||||
|
+ " --connect-to j.example.com:443:server:443"
|
||||||
|
+ " --connect-to j.example.com:80:server:80"
|
||||||
|
+ f" --write-out '{format}'"
|
||||||
|
+ " " + endpoint
|
||||||
|
))
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_unit("jellyfin.service")
|
||||||
|
server.wait_for_unit("nginx.service")
|
||||||
|
server.wait_for_unit("lldap.service")
|
||||||
|
server.wait_for_open_port(8096)
|
||||||
|
|
||||||
|
response = curl(client, """{"code":%{response_code}}""", "http://j.example.com")
|
||||||
|
|
||||||
|
if response['code'] != 200:
|
||||||
|
raise Exception(f"Code is {response['code']}")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
cert = pkgs.nixosTest {
|
||||||
|
name = "jellyfin_cert";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
shb.backup = lib.mkOption { type = lib.types.anything; };
|
||||||
|
shb.authelia = lib.mkOption { type = lib.types.anything; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
../../modules/blocks/nginx.nix
|
||||||
|
../../modules/blocks/postgresql.nix
|
||||||
|
../../modules/blocks/ssl.nix
|
||||||
|
../../modules/services/jellyfin.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
shb.certs = {
|
||||||
|
cas.selfsigned.myca = {
|
||||||
|
name = "My CA";
|
||||||
|
};
|
||||||
|
certs.selfsigned = {
|
||||||
|
n = {
|
||||||
|
ca = config.shb.certs.cas.selfsigned.myca;
|
||||||
|
domain = "*.example.com";
|
||||||
|
group = "nginx";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.nginx.after = [ config.shb.certs.certs.selfsigned.n.systemdService ];
|
||||||
|
systemd.services.nginx.requires = [ config.shb.certs.certs.selfsigned.n.systemdService ];
|
||||||
|
|
||||||
|
shb.jellyfin = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "j";
|
||||||
|
ssl = config.shb.certs.certs.selfsigned.n;
|
||||||
|
};
|
||||||
|
# Nginx port.
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
|
||||||
|
shb.nginx.accessLog = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
# TODO: Test login
|
||||||
|
testScript = { nodes, ... }: ''
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
def curl(target, format, endpoint):
|
||||||
|
return json.loads(target.succeed(
|
||||||
|
"curl --fail-with-body --silent --show-error --output /dev/null --location"
|
||||||
|
+ " --connect-to j.example.com:443:server:443"
|
||||||
|
+ " --connect-to j.example.com:80:server:80"
|
||||||
|
+ f" --write-out '{format}'"
|
||||||
|
+ " " + endpoint
|
||||||
|
))
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_unit("jellyfin.service")
|
||||||
|
server.wait_for_unit("nginx.service")
|
||||||
|
server.wait_for_open_port(8096)
|
||||||
|
|
||||||
|
server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt")
|
||||||
|
client.succeed("rm -r /etc/ssl/certs")
|
||||||
|
client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt")
|
||||||
|
|
||||||
|
response = curl(client, """{"code":%{response_code}}""", "https://j.example.com")
|
||||||
|
|
||||||
|
if response['code'] != 200:
|
||||||
|
raise Exception(f"Code is {response['code']}")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
sso = pkgs.nixosTest {
|
||||||
|
name = "jellyfin_sso";
|
||||||
|
|
||||||
|
nodes.server = { config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
shb.backup = lib.mkOption { type = lib.types.anything; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
../../modules/blocks/authelia.nix
|
||||||
|
../../modules/blocks/ldap.nix
|
||||||
|
../../modules/blocks/postgresql.nix
|
||||||
|
../../modules/blocks/ssl.nix
|
||||||
|
../../modules/services/jellyfin.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
shb.ldap = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "ldap";
|
||||||
|
ldapPort = 3890;
|
||||||
|
webUIListenPort = 17170;
|
||||||
|
dcdomain = "dc=example,dc=com";
|
||||||
|
ldapUserPasswordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
|
jwtSecretFile = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.certs = {
|
||||||
|
cas.selfsigned.myca = {
|
||||||
|
name = "My CA";
|
||||||
|
};
|
||||||
|
certs.selfsigned = {
|
||||||
|
n = {
|
||||||
|
ca = config.shb.certs.cas.selfsigned.myca;
|
||||||
|
domain = "*.example.com";
|
||||||
|
group = "nginx";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.nginx.after = [ config.shb.certs.certs.selfsigned.n.systemdService ];
|
||||||
|
systemd.services.nginx.requires = [ config.shb.certs.certs.selfsigned.n.systemdService ];
|
||||||
|
|
||||||
|
shb.authelia = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "auth";
|
||||||
|
ssl = config.shb.certs.certs.selfsigned.n;
|
||||||
|
|
||||||
|
ldapEndpoint = "ldap://127.0.0.1:${builtins.toString config.shb.ldap.ldapPort}";
|
||||||
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
jwtSecretFile = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
|
ldapAdminPasswordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
|
sessionSecretFile = pkgs.writeText "sessionSecret" "sessionSecret";
|
||||||
|
storageEncryptionKeyFile = pkgs.writeText "storageEncryptionKey" "storageEncryptionKey";
|
||||||
|
identityProvidersOIDCHMACSecretFile = pkgs.writeText "identityProvidersOIDCHMACSecret" "identityProvidersOIDCHMACSecret";
|
||||||
|
identityProvidersOIDCIssuerPrivateKeyFile = (pkgs.runCommand "gen-private-key" {} ''
|
||||||
|
mkdir $out
|
||||||
|
${pkgs.openssl}/bin/openssl genrsa -out $out/private.pem 4096
|
||||||
|
'') + "/private.pem";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.jellyfin = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "j";
|
||||||
|
ssl = config.shb.certs.certs.selfsigned.n;
|
||||||
|
|
||||||
|
ldap = {
|
||||||
|
enable = true;
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = config.shb.ldap.ldapPort;
|
||||||
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
|
passwordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
|
};
|
||||||
|
|
||||||
|
sso = {
|
||||||
|
enable = true;
|
||||||
|
endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
||||||
|
secretFile = pkgs.writeText "ssoSecretFile" "ssoSecretFile";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# Nginx port.
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.client = {};
|
||||||
|
|
||||||
|
# TODO: Test login with ldap user
|
||||||
|
testScript = { nodes, ... }: ''
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
def curl(target, format, endpoint):
|
||||||
|
return json.loads(target.succeed(
|
||||||
|
"curl --fail-with-body --silent --show-error --output /dev/null --location"
|
||||||
|
+ " --connect-to j.example.com:443:server:443"
|
||||||
|
+ " --connect-to j.example.com:80:server:80"
|
||||||
|
+ f" --write-out '{format}'"
|
||||||
|
+ " " + endpoint
|
||||||
|
))
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_unit("jellyfin.service")
|
||||||
|
server.wait_for_unit("nginx.service")
|
||||||
|
server.wait_for_unit("lldap.service")
|
||||||
|
server.wait_for_unit("authelia-auth.example.com.service")
|
||||||
|
server.wait_for_open_port(8096)
|
||||||
|
|
||||||
|
server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt")
|
||||||
|
client.succeed("rm -r /etc/ssl/certs")
|
||||||
|
client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt")
|
||||||
|
|
||||||
|
response = curl(client, """{"code":%{response_code}}""", "https://j.example.com")
|
||||||
|
|
||||||
|
if response['code'] != 200:
|
||||||
|
raise Exception(f"Code is {response['code']}")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue