db is now optional for sonarr, radarr, and prowlarr.
This commit is contained in:
parent
18274be266
commit
a7e673ac69
4 changed files with 408 additions and 372 deletions
|
@ -101,6 +101,7 @@ in {
|
||||||
|
|
||||||
# System packages
|
# System packages
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
|
# Hyprland
|
||||||
libva-utils # to view graphics capabilities
|
libva-utils # to view graphics capabilities
|
||||||
greetd.tuigreet
|
greetd.tuigreet
|
||||||
rofi-wayland
|
rofi-wayland
|
||||||
|
@ -114,6 +115,8 @@ in {
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
wlogout
|
wlogout
|
||||||
wlr-randr
|
wlr-randr
|
||||||
|
# dev
|
||||||
|
uv
|
||||||
# fun
|
# fun
|
||||||
fastfetch
|
fastfetch
|
||||||
# Scripts
|
# Scripts
|
||||||
|
@ -169,6 +172,8 @@ in {
|
||||||
9001 # api interface
|
9001 # api interface
|
||||||
# Beszel-agent
|
# Beszel-agent
|
||||||
45876
|
45876
|
||||||
|
# scrypted
|
||||||
|
45005
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -265,13 +270,13 @@ in {
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
hardening = true;
|
hardening = true;
|
||||||
apiKeyFile = config.sops.secrets."arr/prowlarr/apiKey".path;
|
apiKeyFile = config.sops.secrets."arr/prowlarr/apiKey".path;
|
||||||
db = {
|
# db = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
hostFile = config.sops.secrets."arr/prowlarr/postgres/host".path;
|
# hostFile = config.sops.secrets."arr/prowlarr/postgres/host".path;
|
||||||
port = 5432;
|
# port = 5432;
|
||||||
userFile = config.sops.secrets."arr/prowlarr/postgres/user".path;
|
# userFile = config.sops.secrets."arr/prowlarr/postgres/user".path;
|
||||||
passwordFile = config.sops.secrets."arr/prowlarr/postgres/password".path;
|
# passwordFile = config.sops.secrets."arr/prowlarr/postgres/password".path;
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
# Radarr
|
# Radarr
|
||||||
radarr = {
|
radarr = {
|
||||||
|
@ -289,14 +294,14 @@ in {
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
hardening = true;
|
hardening = true;
|
||||||
apiKeyFile = config.sops.secrets."arr/radarr/1080p/apiKey".path;
|
apiKeyFile = config.sops.secrets."arr/radarr/1080p/apiKey".path;
|
||||||
db = {
|
# db = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
hostFile = config.sops.secrets."arr/radarr/1080p/postgres/host".path;
|
# hostFile = config.sops.secrets."arr/radarr/1080p/postgres/host".path;
|
||||||
port = 5432;
|
# port = 5432;
|
||||||
dbname = "radarr_main";
|
# dbname = "radarr_main";
|
||||||
userFile = config.sops.secrets."arr/radarr/1080p/postgres/user".path;
|
# userFile = config.sops.secrets."arr/radarr/1080p/postgres/user".path;
|
||||||
passwordFile = config.sops.secrets."arr/radarr/1080p/postgres/password".path;
|
# passwordFile = config.sops.secrets."arr/radarr/1080p/postgres/password".path;
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
moviesAnime = {
|
moviesAnime = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -310,14 +315,14 @@ in {
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
hardening = true;
|
hardening = true;
|
||||||
apiKeyFile = config.sops.secrets."arr/radarr/anime/apiKey".path;
|
apiKeyFile = config.sops.secrets."arr/radarr/anime/apiKey".path;
|
||||||
db = {
|
# db = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
hostFile = config.sops.secrets."arr/radarr/anime/postgres/host".path;
|
# hostFile = config.sops.secrets."arr/radarr/anime/postgres/host".path;
|
||||||
port = 5432;
|
# port = 5432;
|
||||||
dbname = "radarr_anime";
|
# dbname = "radarr_anime";
|
||||||
userFile = config.sops.secrets."arr/radarr/anime/postgres/user".path;
|
# userFile = config.sops.secrets."arr/radarr/anime/postgres/user".path;
|
||||||
passwordFile = config.sops.secrets."arr/radarr/anime/postgres/password".path;
|
# passwordFile = config.sops.secrets."arr/radarr/anime/postgres/password".path;
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -337,14 +342,14 @@ in {
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
hardening = true;
|
hardening = true;
|
||||||
apiKeyFile = config.sops.secrets."arr/sonarr/1080p/apiKey".path;
|
apiKeyFile = config.sops.secrets."arr/sonarr/1080p/apiKey".path;
|
||||||
db = {
|
# db = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
hostFile = config.sops.secrets."arr/sonarr/1080p/postgres/host".path;
|
# hostFile = config.sops.secrets."arr/sonarr/1080p/postgres/host".path;
|
||||||
port = 5432;
|
# port = 5432;
|
||||||
dbname = "sonarr_main";
|
# dbname = "sonarr_main";
|
||||||
userFile = config.sops.secrets."arr/sonarr/1080p/postgres/user".path;
|
# userFile = config.sops.secrets."arr/sonarr/1080p/postgres/user".path;
|
||||||
passwordFile = config.sops.secrets."arr/sonarr/1080p/postgres/password".path;
|
# passwordFile = config.sops.secrets."arr/sonarr/1080p/postgres/password".path;
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
anime = {
|
anime = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -358,14 +363,14 @@ in {
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
hardening = true;
|
hardening = true;
|
||||||
apiKeyFile = config.sops.secrets."arr/sonarr/anime/apiKey".path;
|
apiKeyFile = config.sops.secrets."arr/sonarr/anime/apiKey".path;
|
||||||
db = {
|
# db = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
hostFile = config.sops.secrets."arr/sonarr/anime/postgres/host".path;
|
# hostFile = config.sops.secrets."arr/sonarr/anime/postgres/host".path;
|
||||||
port = 5432;
|
# port = 5432;
|
||||||
dbname = "sonarr_anime";
|
# dbname = "sonarr_anime";
|
||||||
userFile = config.sops.secrets."arr/sonarr/anime/postgres/user".path;
|
# userFile = config.sops.secrets."arr/sonarr/anime/postgres/user".path;
|
||||||
passwordFile = config.sops.secrets."arr/sonarr/anime/postgres/password".path;
|
# passwordFile = config.sops.secrets."arr/sonarr/anime/postgres/password".path;
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
utils,
|
utils,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
with lib;
|
with lib; let
|
||||||
let
|
|
||||||
cfg = config.mySystem.services.prowlarr;
|
cfg = config.mySystem.services.prowlarr;
|
||||||
dbOptions = {
|
dbOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
@ -51,12 +50,11 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options.mySystem.services.prowlarr = {
|
options.mySystem.services.prowlarr = {
|
||||||
enable = mkEnableOption "Prowlarr";
|
enable = mkEnableOption "Prowlarr";
|
||||||
|
|
||||||
package = mkPackageOption pkgs "prowlarr" { };
|
package = mkPackageOption pkgs "prowlarr" {};
|
||||||
|
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
@ -109,7 +107,7 @@ in
|
||||||
|
|
||||||
extraEnvVars = mkOption {
|
extraEnvVars = mkOption {
|
||||||
type = types.attrs;
|
type = types.attrs;
|
||||||
default = { };
|
default = {};
|
||||||
example = {
|
example = {
|
||||||
MY_VAR = "my value";
|
MY_VAR = "my value";
|
||||||
};
|
};
|
||||||
|
@ -125,6 +123,14 @@ in
|
||||||
|
|
||||||
db = mkOption {
|
db = mkOption {
|
||||||
type = types.submodule dbOptions;
|
type = types.submodule dbOptions;
|
||||||
|
default = {
|
||||||
|
enable = false;
|
||||||
|
host = "";
|
||||||
|
port = "5432";
|
||||||
|
user = "";
|
||||||
|
passwordFile = "";
|
||||||
|
dbname = "";
|
||||||
|
};
|
||||||
example = {
|
example = {
|
||||||
enable = true;
|
enable = true;
|
||||||
host = "10.5.0.5"; # or use hostFile
|
host = "10.5.0.5"; # or use hostFile
|
||||||
|
@ -140,11 +146,11 @@ in
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = !(cfg.db.host != "" && cfg.db.hostFile != "");
|
assertion = !(cfg.db.enable && (cfg.db.host != "" && cfg.db.hostFile != ""));
|
||||||
message = "Specify either a direct database host via db.host or a file via db.hostFile (leave direct host empty).";
|
message = "Specify either a direct database host via db.host or a file via db.hostFile (leave direct host empty).";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = !(cfg.db.user != "prowlarr" && cfg.db.userFile != "");
|
assertion = !(cfg.db.enable && (cfg.db.user != "prowlarr" && cfg.db.userFile != ""));
|
||||||
message = "Specify either a direct database user via db.user or a file via db.userFile.";
|
message = "Specify either a direct database user via db.user or a file via db.userFile.";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -159,7 +165,7 @@ in
|
||||||
"network.target"
|
"network.target"
|
||||||
"nss-lookup.target"
|
"nss-lookup.target"
|
||||||
];
|
];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = ["multi-user.target"];
|
||||||
environment = lib.mkMerge [
|
environment = lib.mkMerge [
|
||||||
{
|
{
|
||||||
PROWLARR__APP__INSTANCENAME = "Prowlarr";
|
PROWLARR__APP__INSTANCENAME = "Prowlarr";
|
||||||
|
@ -171,7 +177,7 @@ in
|
||||||
PROWLARR__SERVER__PORT = toString cfg.port;
|
PROWLARR__SERVER__PORT = toString cfg.port;
|
||||||
PROWLARR__UPDATE__BRANCH = "develop";
|
PROWLARR__UPDATE__BRANCH = "develop";
|
||||||
}
|
}
|
||||||
(lib.mkIf cfg.db.enable {
|
(lib.optionalAttrs cfg.db.enable {
|
||||||
PROWLARR__POSTGRES__PORT = toString cfg.db.port;
|
PROWLARR__POSTGRES__PORT = toString cfg.db.port;
|
||||||
PROWLARR__POSTGRES__MAINDB = cfg.db.dbname;
|
PROWLARR__POSTGRES__MAINDB = cfg.db.dbname;
|
||||||
})
|
})
|
||||||
|
@ -197,8 +203,8 @@ in
|
||||||
RestartSec = 5;
|
RestartSec = 5;
|
||||||
}
|
}
|
||||||
(lib.mkIf cfg.hardening {
|
(lib.mkIf cfg.hardening {
|
||||||
CapabilityBoundingSet = [ "" ];
|
CapabilityBoundingSet = [""];
|
||||||
DeviceAllow = [ "" ];
|
DeviceAllow = [""];
|
||||||
DevicePolicy = "closed";
|
DevicePolicy = "closed";
|
||||||
LockPersonality = true;
|
LockPersonality = true;
|
||||||
# Needs access to .Net CLR memory space.
|
# Needs access to .Net CLR memory space.
|
||||||
|
@ -237,7 +243,7 @@ in
|
||||||
#"~@resources"
|
#"~@resources"
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
(lib.mkIf cfg.db.enable {
|
{
|
||||||
ExecStartPre = "+${pkgs.writeShellScript "prowlarr-pre-script" ''
|
ExecStartPre = "+${pkgs.writeShellScript "prowlarr-pre-script" ''
|
||||||
mkdir -p /run/prowlarr
|
mkdir -p /run/prowlarr
|
||||||
rm -f /run/prowlarr/secrets.env
|
rm -f /run/prowlarr/secrets.env
|
||||||
|
@ -258,10 +264,12 @@ in
|
||||||
write_var "PROWLARR__AUTH__APIKEY" "$(cat ${cfg.apiKeyFile})"
|
write_var "PROWLARR__AUTH__APIKEY" "$(cat ${cfg.apiKeyFile})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Database Configuration
|
${lib.optionalString cfg.db.enable ''
|
||||||
write_var "PROWLARR__POSTGRES__HOST" "$([ -n "${cfg.db.host}" ] && echo "${cfg.db.host}" || cat "${cfg.db.hostFile}")"
|
# Database Configuration
|
||||||
write_var "PROWLARR__POSTGRES__USER" "$([ -n "${cfg.db.user}" ] && echo "${cfg.db.user}" || cat "${cfg.db.userFile}")"
|
write_var "PROWLARR__POSTGRES__HOST" "$([ -n "${cfg.db.host}" ] && echo "${cfg.db.host}" || cat "${cfg.db.hostFile}")"
|
||||||
write_var "PROWLARR__POSTGRES__PASSWORD" "$(cat ${cfg.db.passwordFile})"
|
write_var "PROWLARR__POSTGRES__USER" "$([ -n "${cfg.db.user}" ] && echo "${cfg.db.user}" || cat "${cfg.db.userFile}")"
|
||||||
|
write_var "PROWLARR__POSTGRES__PASSWORD" "$(cat ${cfg.db.passwordFile})"
|
||||||
|
''}
|
||||||
|
|
||||||
# Final permissions
|
# Final permissions
|
||||||
chmod 600 /run/prowlarr/secrets.env
|
chmod 600 /run/prowlarr/secrets.env
|
||||||
|
@ -269,18 +277,18 @@ in
|
||||||
''}";
|
''}";
|
||||||
|
|
||||||
EnvironmentFile = (
|
EnvironmentFile = (
|
||||||
[ "-/run/prowlarr/secrets.env" ]
|
["-/run/prowlarr/secrets.env"]
|
||||||
++ lib.optional (cfg.extraEnvVarFile != null && cfg.extraEnvVarFile != "") cfg.extraEnvVarFile
|
++ lib.optional (cfg.extraEnvVarFile != null && cfg.extraEnvVarFile != "") cfg.extraEnvVarFile
|
||||||
);
|
);
|
||||||
})
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall = mkIf cfg.openFirewall {
|
networking.firewall = mkIf cfg.openFirewall {
|
||||||
allowedTCPPorts = [ cfg.port ];
|
allowedTCPPorts = [cfg.port];
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.${cfg.group} = { };
|
users.groups.${cfg.group} = {};
|
||||||
users.users = mkIf (cfg.user == "prowlarr") {
|
users.users = mkIf (cfg.user == "prowlarr") {
|
||||||
prowlarr = {
|
prowlarr = {
|
||||||
inherit (cfg) group;
|
inherit (cfg) group;
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
utils,
|
utils,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
with lib;
|
with lib; let
|
||||||
let
|
|
||||||
cfg = config.mySystem.services.radarr;
|
cfg = config.mySystem.services.radarr;
|
||||||
dbOptions = {
|
dbOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
@ -51,20 +50,18 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options.mySystem.services.radarr = {
|
options.mySystem.services.radarr = {
|
||||||
enable = mkEnableOption "Radarr (global)";
|
enable = mkEnableOption "Radarr (global)";
|
||||||
|
|
||||||
instances = mkOption {
|
instances = mkOption {
|
||||||
type = types.attrsOf (
|
type = types.attrsOf (
|
||||||
types.submodule (
|
types.submodule (
|
||||||
{ name, ... }:
|
{name, ...}: {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
enable = mkEnableOption "Radarr (instance)";
|
enable = mkEnableOption "Radarr (instance)";
|
||||||
|
|
||||||
package = mkPackageOption pkgs "Radarr" { };
|
package = mkPackageOption pkgs "Radarr" {};
|
||||||
|
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
@ -131,12 +128,20 @@ in
|
||||||
passwordFile = "/run/secrets/radarr_db_password";
|
passwordFile = "/run/secrets/radarr_db_password";
|
||||||
dbname = "radarr_main";
|
dbname = "radarr_main";
|
||||||
};
|
};
|
||||||
|
default = {
|
||||||
|
enable = false;
|
||||||
|
host = "";
|
||||||
|
port = "5432";
|
||||||
|
user = "";
|
||||||
|
passwordFile = "";
|
||||||
|
dbname = "";
|
||||||
|
};
|
||||||
description = "Database settings for radarr.";
|
description = "Database settings for radarr.";
|
||||||
};
|
};
|
||||||
|
|
||||||
extraEnvVars = mkOption {
|
extraEnvVars = mkOption {
|
||||||
type = types.attrs;
|
type = types.attrs;
|
||||||
default = { };
|
default = {};
|
||||||
example = {
|
example = {
|
||||||
MY_VAR = "my value";
|
MY_VAR = "my value";
|
||||||
};
|
};
|
||||||
|
@ -153,7 +158,7 @@ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
default = { };
|
default = {};
|
||||||
description = "Radarr instance configurations.";
|
description = "Radarr instance configurations.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -163,8 +168,8 @@ in
|
||||||
assertions = flatten (
|
assertions = flatten (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
if instanceCfg.enable then
|
if instanceCfg.enable
|
||||||
[
|
then [
|
||||||
{
|
{
|
||||||
assertion = !(instanceCfg.db.host != "" && instanceCfg.db.hostFile != "");
|
assertion = !(instanceCfg.db.host != "" && instanceCfg.db.hostFile != "");
|
||||||
message = "Specify either a direct database host via db.host or a file via db.hostFile (leave direct host empty).";
|
message = "Specify either a direct database host via db.host or a file via db.hostFile (leave direct host empty).";
|
||||||
|
@ -178,180 +183,186 @@ in
|
||||||
message = "Specify either a direct API key via apiKey or a file via apiKeyFile (leave direct API key empty).";
|
message = "Specify either a direct API key via apiKey or a file via apiKeyFile (leave direct API key empty).";
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
else
|
else []
|
||||||
[ ]
|
)
|
||||||
) cfg.instances
|
cfg.instances
|
||||||
);
|
);
|
||||||
|
|
||||||
# Create systemd tmpfiles rules for each enabled instance
|
# Create systemd tmpfiles rules for each enabled instance
|
||||||
systemd.tmpfiles.rules = flatten (
|
systemd.tmpfiles.rules = flatten (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
if instanceCfg.enable then
|
if instanceCfg.enable
|
||||||
[
|
then [
|
||||||
"d ${instanceCfg.dataDir} 0775 ${instanceCfg.user} ${instanceCfg.group}"
|
"d ${instanceCfg.dataDir} 0775 ${instanceCfg.user} ${instanceCfg.group}"
|
||||||
]
|
]
|
||||||
else
|
else []
|
||||||
[ ]
|
)
|
||||||
) cfg.instances
|
cfg.instances
|
||||||
);
|
);
|
||||||
|
|
||||||
# Create services for each enabled instance
|
# Create services for each enabled instance
|
||||||
systemd.services = mapAttrs' (
|
systemd.services =
|
||||||
name: instanceCfg:
|
mapAttrs' (
|
||||||
nameValuePair "radarr-${name}" (
|
name: instanceCfg:
|
||||||
mkIf instanceCfg.enable {
|
nameValuePair "radarr-${name}" (
|
||||||
description = "Radarr (${name})";
|
mkIf instanceCfg.enable {
|
||||||
after = [
|
description = "Radarr (${name})";
|
||||||
"network.target"
|
after = [
|
||||||
"nss-lookup.target"
|
"network.target"
|
||||||
];
|
"nss-lookup.target"
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
environment = lib.mkMerge [
|
|
||||||
{
|
|
||||||
RADARR__APP__INSTANCENAME = name;
|
|
||||||
RADARR__APP__THEME = "dark";
|
|
||||||
RADARR__AUTH__METHOD = "External";
|
|
||||||
RADARR__AUTH__REQUIRED = "DisabledForLocalAddresses";
|
|
||||||
RADARR__LOG__DBENABLED = "False";
|
|
||||||
RADARR__LOG__LEVEL = "info";
|
|
||||||
RADARR__SERVER__PORT = toString instanceCfg.port;
|
|
||||||
RADARR__UPDATE__BRANCH = "develop";
|
|
||||||
}
|
|
||||||
(lib.mkIf instanceCfg.db.enable {
|
|
||||||
RADARR__POSTGRES__PORT = toString instanceCfg.db.port;
|
|
||||||
RADARR__POSTGRES__MAINDB = instanceCfg.db.dbname;
|
|
||||||
})
|
|
||||||
instanceCfg.extraEnvVars
|
|
||||||
];
|
|
||||||
|
|
||||||
serviceConfig = lib.mkMerge [
|
|
||||||
{
|
|
||||||
Type = "simple";
|
|
||||||
User = instanceCfg.user;
|
|
||||||
Group = instanceCfg.group;
|
|
||||||
ExecStart = utils.escapeSystemdExecArgs [
|
|
||||||
(lib.getExe instanceCfg.package)
|
|
||||||
"-nobrowser"
|
|
||||||
"-data=${instanceCfg.dataDir}"
|
|
||||||
"-port=${toString instanceCfg.port}"
|
|
||||||
];
|
];
|
||||||
WorkingDirectory = instanceCfg.dataDir;
|
wantedBy = ["multi-user.target"];
|
||||||
RuntimeDirectory = "radarr-${name}";
|
environment = lib.mkMerge [
|
||||||
LogsDirectory = "radarr-${name}";
|
{
|
||||||
RuntimeDirectoryMode = "0750";
|
RADARR__APP__INSTANCENAME = name;
|
||||||
Restart = "on-failure";
|
RADARR__APP__THEME = "dark";
|
||||||
RestartSec = 5;
|
RADARR__AUTH__METHOD = "External";
|
||||||
}
|
RADARR__AUTH__REQUIRED = "DisabledForLocalAddresses";
|
||||||
(lib.mkIf instanceCfg.hardening {
|
RADARR__LOG__DBENABLED = "False";
|
||||||
CapabilityBoundingSet = [ "" ];
|
RADARR__LOG__LEVEL = "info";
|
||||||
DeviceAllow = [ "" ];
|
RADARR__SERVER__PORT = toString instanceCfg.port;
|
||||||
DevicePolicy = "closed";
|
RADARR__UPDATE__BRANCH = "develop";
|
||||||
LockPersonality = true;
|
|
||||||
MemoryDenyWriteExecute = false;
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
PrivateDevices = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
ProtectControlGroups = true;
|
|
||||||
ProtectHome = "read-only";
|
|
||||||
ProtectKernelModules = true;
|
|
||||||
ProtectKernelTunables = true;
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
ReadWritePaths = [
|
|
||||||
instanceCfg.dataDir
|
|
||||||
instanceCfg.moviesDir
|
|
||||||
"/var/log/radarr-${name}"
|
|
||||||
"/eru/media"
|
|
||||||
];
|
|
||||||
RestrictAddressFamilies = [
|
|
||||||
"AF_INET"
|
|
||||||
"AF_INET6"
|
|
||||||
"AF_NETLINK"
|
|
||||||
];
|
|
||||||
RestrictNamespaces = [
|
|
||||||
"uts"
|
|
||||||
"ipc"
|
|
||||||
"pid"
|
|
||||||
"user"
|
|
||||||
"cgroup"
|
|
||||||
"net"
|
|
||||||
];
|
|
||||||
RestrictSUIDSGID = true;
|
|
||||||
SystemCallArchitectures = "native";
|
|
||||||
SystemCallFilter = [
|
|
||||||
"@system-service"
|
|
||||||
];
|
|
||||||
})
|
|
||||||
(lib.mkIf instanceCfg.db.enable {
|
|
||||||
ExecStartPre = "+${pkgs.writeShellScript "radarr-${name}-pre-script" ''
|
|
||||||
mkdir -p /run/radarr-${name}
|
|
||||||
rm -f /run/radarr-${name}/secrets.env
|
|
||||||
|
|
||||||
# Helper function to safely write variables
|
|
||||||
write_var() {
|
|
||||||
local var_name="$1"
|
|
||||||
local value="$2"
|
|
||||||
if [ -n "$value" ]; then
|
|
||||||
printf "%s=%s\n" "$var_name" "$value" >> /run/radarr-${name}/secrets.env
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
(lib.mkIf instanceCfg.db.enable {
|
||||||
|
RADARR__POSTGRES__PORT = toString instanceCfg.db.port;
|
||||||
|
RADARR__POSTGRES__MAINDB = instanceCfg.db.dbname;
|
||||||
|
})
|
||||||
|
instanceCfg.extraEnvVars
|
||||||
|
];
|
||||||
|
|
||||||
# API Key (direct value or file)
|
serviceConfig = lib.mkMerge [
|
||||||
if [ -n "${instanceCfg.apiKey}" ]; then
|
{
|
||||||
write_var "RADARR__AUTH__APIKEY" "${instanceCfg.apiKey}"
|
Type = "simple";
|
||||||
else
|
User = instanceCfg.user;
|
||||||
write_var "RADARR__AUTH__APIKEY" "$(cat ${instanceCfg.apiKeyFile})"
|
Group = instanceCfg.group;
|
||||||
fi
|
ExecStart = utils.escapeSystemdExecArgs [
|
||||||
|
(lib.getExe instanceCfg.package)
|
||||||
|
"-nobrowser"
|
||||||
|
"-data=${instanceCfg.dataDir}"
|
||||||
|
"-port=${toString instanceCfg.port}"
|
||||||
|
];
|
||||||
|
WorkingDirectory = instanceCfg.dataDir;
|
||||||
|
RuntimeDirectory = "radarr-${name}";
|
||||||
|
LogsDirectory = "radarr-${name}";
|
||||||
|
RuntimeDirectoryMode = "0750";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 5;
|
||||||
|
}
|
||||||
|
(lib.mkIf instanceCfg.hardening {
|
||||||
|
CapabilityBoundingSet = [""];
|
||||||
|
DeviceAllow = [""];
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = false;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ReadWritePaths = [
|
||||||
|
instanceCfg.dataDir
|
||||||
|
instanceCfg.moviesDir
|
||||||
|
"/var/log/radarr-${name}"
|
||||||
|
"/eru/media"
|
||||||
|
];
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
"AF_NETLINK"
|
||||||
|
];
|
||||||
|
RestrictNamespaces = [
|
||||||
|
"uts"
|
||||||
|
"ipc"
|
||||||
|
"pid"
|
||||||
|
"user"
|
||||||
|
"cgroup"
|
||||||
|
"net"
|
||||||
|
];
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
{
|
||||||
|
ExecStartPre = "+${pkgs.writeShellScript "radarr-${name}-pre-script" ''
|
||||||
|
mkdir -p /run/radarr-${name}
|
||||||
|
rm -f /run/radarr-${name}/secrets.env
|
||||||
|
|
||||||
# Database Configuration
|
# Helper function to safely write variables
|
||||||
write_var "RADARR__POSTGRES__HOST" "$([ -n "${instanceCfg.db.host}" ] && echo "${instanceCfg.db.host}" || cat "${instanceCfg.db.hostFile}")"
|
write_var() {
|
||||||
write_var "RADARR__POSTGRES__USER" "$([ -n "${instanceCfg.db.userFile}" ] && cat "${instanceCfg.db.userFile}" || echo "${instanceCfg.db.user}")"
|
local var_name="$1"
|
||||||
write_var "RADARR__POSTGRES__PASSWORD" "$(cat ${instanceCfg.db.passwordFile})"
|
local value="$2"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
printf "%s=%s\n" "$var_name" "$value" >> /run/radarr-${name}/secrets.env
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Final permissions
|
# API Key (direct value or file)
|
||||||
chmod 600 /run/radarr-${name}/secrets.env
|
if [ -n "${instanceCfg.apiKey}" ]; then
|
||||||
chown ${instanceCfg.user}:${instanceCfg.group} /run/radarr-${name}/secrets.env
|
write_var "RADARR__AUTH__APIKEY" "${instanceCfg.apiKey}"
|
||||||
''}";
|
else
|
||||||
|
write_var "RADARR__AUTH__APIKEY" "$(cat ${instanceCfg.apiKeyFile})"
|
||||||
|
fi
|
||||||
|
|
||||||
EnvironmentFile = (
|
${lib.optionalString instanceCfg.db.enable ''
|
||||||
[ "-/run/radarr-${name}/secrets.env" ]
|
# Database Configuration
|
||||||
++ lib.optional (
|
write_var "RADARR__POSTGRES__HOST" "$([ -n "${instanceCfg.db.host}" ] && echo "${instanceCfg.db.host}" || cat "${instanceCfg.db.hostFile}")"
|
||||||
instanceCfg.extraEnvVarFile != null && instanceCfg.extraEnvVarFile != ""
|
write_var "RADARR__POSTGRES__USER" "$([ -n "${instanceCfg.db.userFile}" ] && cat "${instanceCfg.db.userFile}" || echo "${instanceCfg.db.user}")"
|
||||||
) instanceCfg.extraEnvVarFile
|
write_var "RADARR__POSTGRES__PASSWORD" "$(cat ${instanceCfg.db.passwordFile})"
|
||||||
);
|
''}
|
||||||
})
|
|
||||||
];
|
# Final permissions
|
||||||
}
|
chmod 600 /run/radarr-${name}/secrets.env
|
||||||
|
chown ${instanceCfg.user}:${instanceCfg.group} /run/radarr-${name}/secrets.env
|
||||||
|
''}";
|
||||||
|
|
||||||
|
EnvironmentFile = (
|
||||||
|
["-/run/radarr-${name}/secrets.env"]
|
||||||
|
++ lib.optional (
|
||||||
|
instanceCfg.extraEnvVarFile != null && instanceCfg.extraEnvVarFile != ""
|
||||||
|
)
|
||||||
|
instanceCfg.extraEnvVarFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) cfg.instances;
|
cfg.instances;
|
||||||
|
|
||||||
# Firewall configurations
|
# Firewall configurations
|
||||||
networking.firewall = mkMerge (
|
networking.firewall = mkMerge (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
mkIf (instanceCfg.enable && instanceCfg.openFirewall) {
|
mkIf (instanceCfg.enable && instanceCfg.openFirewall) {
|
||||||
allowedTCPPorts = [ instanceCfg.port ];
|
allowedTCPPorts = [instanceCfg.port];
|
||||||
}
|
}
|
||||||
) cfg.instances
|
)
|
||||||
|
cfg.instances
|
||||||
);
|
);
|
||||||
|
|
||||||
# Users and groups
|
# Users and groups
|
||||||
users = mkMerge (
|
users = mkMerge (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
mkIf instanceCfg.enable {
|
mkIf instanceCfg.enable {
|
||||||
groups.${instanceCfg.group} = { };
|
groups.${instanceCfg.group} = {};
|
||||||
users = mkIf (instanceCfg.user == "radarr") {
|
users = mkIf (instanceCfg.user == "radarr") {
|
||||||
radarr = {
|
radarr = {
|
||||||
inherit (instanceCfg) group;
|
inherit (instanceCfg) group;
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
# home = instanceCfg.dataDir;
|
# home = instanceCfg.dataDir;
|
||||||
home = "/nahar/radarr";
|
home = "/nahar/radarr";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
)
|
||||||
) cfg.instances
|
cfg.instances
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
utils,
|
utils,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
with lib;
|
with lib; let
|
||||||
let
|
|
||||||
cfg = config.mySystem.services.sonarr;
|
cfg = config.mySystem.services.sonarr;
|
||||||
dbOptions = {
|
dbOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
@ -51,20 +50,18 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options.mySystem.services.sonarr = {
|
options.mySystem.services.sonarr = {
|
||||||
enable = mkEnableOption "Sonarr (global)";
|
enable = mkEnableOption "Sonarr (global)";
|
||||||
|
|
||||||
instances = mkOption {
|
instances = mkOption {
|
||||||
type = types.attrsOf (
|
type = types.attrsOf (
|
||||||
types.submodule (
|
types.submodule (
|
||||||
{ name, ... }:
|
{name, ...}: {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
enable = mkEnableOption "Sonarr (instance)";
|
enable = mkEnableOption "Sonarr (instance)";
|
||||||
|
|
||||||
package = mkPackageOption pkgs "Sonarr" { };
|
package = mkPackageOption pkgs "Sonarr" {};
|
||||||
|
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
@ -131,12 +128,20 @@ in
|
||||||
passwordFile = "/run/secrets/sonarr_db_password";
|
passwordFile = "/run/secrets/sonarr_db_password";
|
||||||
dbname = "sonarr_main";
|
dbname = "sonarr_main";
|
||||||
};
|
};
|
||||||
|
default = {
|
||||||
|
enable = false;
|
||||||
|
host = "";
|
||||||
|
port = "5432";
|
||||||
|
user = "";
|
||||||
|
passwordFile = "";
|
||||||
|
dbname = "";
|
||||||
|
};
|
||||||
description = "Database settings for sonarr.";
|
description = "Database settings for sonarr.";
|
||||||
};
|
};
|
||||||
|
|
||||||
extraEnvVars = mkOption {
|
extraEnvVars = mkOption {
|
||||||
type = types.attrs;
|
type = types.attrs;
|
||||||
default = { };
|
default = {};
|
||||||
example = {
|
example = {
|
||||||
MY_VAR = "my value";
|
MY_VAR = "my value";
|
||||||
};
|
};
|
||||||
|
@ -153,7 +158,7 @@ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
default = { };
|
default = {};
|
||||||
description = "Sonarr instance configurations.";
|
description = "Sonarr instance configurations.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -163,14 +168,14 @@ in
|
||||||
assertions = flatten (
|
assertions = flatten (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
if instanceCfg.enable then
|
if instanceCfg.enable
|
||||||
[
|
then [
|
||||||
{
|
{
|
||||||
assertion = !(instanceCfg.db.host != "" && instanceCfg.db.hostFile != "");
|
assertion = !(instanceCfg.db.enable && (instanceCfg.db.host != "" && instanceCfg.db.hostFile != ""));
|
||||||
message = "Specify either a direct database host via db.host or a file via db.hostFile (leave direct host empty).";
|
message = "Specify either a direct database host via db.host or a file via db.hostFile (leave direct host empty).";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = !(instanceCfg.db.user != "sonarr" && instanceCfg.db.userFile != "");
|
assertion = !(instanceCfg.db.enable && (instanceCfg.db.user != "sonarr" && instanceCfg.db.userFile != ""));
|
||||||
message = "Specify either a direct database user via db.user or a file via db.userFile.";
|
message = "Specify either a direct database user via db.user or a file via db.userFile.";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -178,180 +183,187 @@ in
|
||||||
message = "Specify either a direct API key via apiKey or a file via apiKeyFile (leave direct API key empty).";
|
message = "Specify either a direct API key via apiKey or a file via apiKeyFile (leave direct API key empty).";
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
else
|
else []
|
||||||
[ ]
|
)
|
||||||
) cfg.instances
|
cfg.instances
|
||||||
);
|
);
|
||||||
|
|
||||||
# Create systemd tmpfiles rules for each enabled instance
|
# Create systemd tmpfiles rules for each enabled instance
|
||||||
systemd.tmpfiles.rules = flatten (
|
systemd.tmpfiles.rules = flatten (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
if instanceCfg.enable then
|
if instanceCfg.enable
|
||||||
[
|
then [
|
||||||
"d ${instanceCfg.dataDir} 0775 ${instanceCfg.user} ${instanceCfg.group}"
|
"d ${instanceCfg.dataDir} 0775 ${instanceCfg.user} ${instanceCfg.group}"
|
||||||
]
|
]
|
||||||
else
|
else []
|
||||||
[ ]
|
)
|
||||||
) cfg.instances
|
cfg.instances
|
||||||
);
|
);
|
||||||
|
|
||||||
# Create services for each enabled instance
|
# Create services for each enabled instance
|
||||||
systemd.services = mapAttrs' (
|
systemd.services =
|
||||||
name: instanceCfg:
|
mapAttrs' (
|
||||||
nameValuePair "sonarr-${name}" (
|
name: instanceCfg:
|
||||||
mkIf instanceCfg.enable {
|
nameValuePair "sonarr-${name}" (
|
||||||
description = "Sonarr (${name})";
|
mkIf instanceCfg.enable {
|
||||||
after = [
|
description = "Sonarr (${name})";
|
||||||
"network.target"
|
after = [
|
||||||
"nss-lookup.target"
|
"network.target"
|
||||||
];
|
"nss-lookup.target"
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
environment = lib.mkMerge [
|
|
||||||
{
|
|
||||||
SONARR__APP__INSTANCENAME = name;
|
|
||||||
SONARR__APP__THEME = "dark";
|
|
||||||
SONARR__AUTH__METHOD = "External";
|
|
||||||
SONARR__AUTH__REQUIRED = "DisabledForLocalAddresses";
|
|
||||||
SONARR__LOG__DBENABLED = "False";
|
|
||||||
SONARR__LOG__LEVEL = "info";
|
|
||||||
SONARR__SERVER__PORT = toString instanceCfg.port;
|
|
||||||
SONARR__UPDATE__BRANCH = "develop";
|
|
||||||
}
|
|
||||||
(lib.mkIf instanceCfg.db.enable {
|
|
||||||
SONARR__POSTGRES__PORT = toString instanceCfg.db.port;
|
|
||||||
SONARR__POSTGRES__MAINDB = instanceCfg.db.dbname;
|
|
||||||
})
|
|
||||||
instanceCfg.extraEnvVars
|
|
||||||
];
|
|
||||||
|
|
||||||
serviceConfig = lib.mkMerge [
|
|
||||||
{
|
|
||||||
Type = "simple";
|
|
||||||
User = instanceCfg.user;
|
|
||||||
Group = instanceCfg.group;
|
|
||||||
ExecStart = utils.escapeSystemdExecArgs [
|
|
||||||
(lib.getExe instanceCfg.package)
|
|
||||||
"-nobrowser"
|
|
||||||
"-data=${instanceCfg.dataDir}"
|
|
||||||
"-port=${toString instanceCfg.port}"
|
|
||||||
];
|
];
|
||||||
WorkingDirectory = instanceCfg.dataDir;
|
wantedBy = ["multi-user.target"];
|
||||||
RuntimeDirectory = "sonarr-${name}";
|
environment = lib.mkMerge [
|
||||||
LogsDirectory = "sonarr-${name}";
|
{
|
||||||
RuntimeDirectoryMode = "0750";
|
SONARR__APP__INSTANCENAME = name;
|
||||||
Restart = "on-failure";
|
SONARR__APP__THEME = "dark";
|
||||||
RestartSec = 5;
|
SONARR__AUTH__METHOD = "External";
|
||||||
}
|
SONARR__AUTH__REQUIRED = "DisabledForLocalAddresses";
|
||||||
(lib.mkIf instanceCfg.hardening {
|
SONARR__LOG__DBENABLED = "False";
|
||||||
CapabilityBoundingSet = [ "" ];
|
SONARR__LOG__LEVEL = "info";
|
||||||
DeviceAllow = [ "" ];
|
SONARR__SERVER__PORT = toString instanceCfg.port;
|
||||||
DevicePolicy = "closed";
|
SONARR__UPDATE__BRANCH = "develop";
|
||||||
LockPersonality = true;
|
|
||||||
MemoryDenyWriteExecute = false;
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
PrivateDevices = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
ProtectControlGroups = true;
|
|
||||||
ProtectHome = "read-only";
|
|
||||||
ProtectKernelModules = true;
|
|
||||||
ProtectKernelTunables = true;
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
ReadWritePaths = [
|
|
||||||
instanceCfg.dataDir
|
|
||||||
instanceCfg.tvDir
|
|
||||||
"/var/log/sonarr-${name}"
|
|
||||||
"/eru/media"
|
|
||||||
];
|
|
||||||
RestrictAddressFamilies = [
|
|
||||||
"AF_INET"
|
|
||||||
"AF_INET6"
|
|
||||||
"AF_NETLINK"
|
|
||||||
];
|
|
||||||
RestrictNamespaces = [
|
|
||||||
"uts"
|
|
||||||
"ipc"
|
|
||||||
"pid"
|
|
||||||
"user"
|
|
||||||
"cgroup"
|
|
||||||
"net"
|
|
||||||
"mnt"
|
|
||||||
];
|
|
||||||
RestrictSUIDSGID = true;
|
|
||||||
SystemCallArchitectures = "native";
|
|
||||||
SystemCallFilter = [
|
|
||||||
"@system-service"
|
|
||||||
];
|
|
||||||
})
|
|
||||||
(lib.mkIf instanceCfg.db.enable {
|
|
||||||
ExecStartPre = "+${pkgs.writeShellScript "sonarr-${name}-pre-script" ''
|
|
||||||
mkdir -p /run/sonarr-${name}
|
|
||||||
rm -f /run/sonarr-${name}/secrets.env
|
|
||||||
|
|
||||||
# Helper function to safely write variables
|
|
||||||
write_var() {
|
|
||||||
local var_name="$1"
|
|
||||||
local value="$2"
|
|
||||||
if [ -n "$value" ]; then
|
|
||||||
printf "%s=%s\n" "$var_name" "$value" >> /run/sonarr-${name}/secrets.env
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
(lib.mkIf instanceCfg.db.enable {
|
||||||
|
SONARR__POSTGRES__PORT = toString instanceCfg.db.port;
|
||||||
|
SONARR__POSTGRES__MAINDB = instanceCfg.db.dbname;
|
||||||
|
})
|
||||||
|
instanceCfg.extraEnvVars
|
||||||
|
];
|
||||||
|
|
||||||
# API Key (direct value or file)
|
serviceConfig = lib.mkMerge [
|
||||||
if [ -n "${instanceCfg.apiKey}" ]; then
|
{
|
||||||
write_var "SONARR__AUTH__APIKEY" "${instanceCfg.apiKey}"
|
Type = "simple";
|
||||||
else
|
User = instanceCfg.user;
|
||||||
write_var "SONARR__AUTH__APIKEY" "$(cat ${instanceCfg.apiKeyFile})"
|
Group = instanceCfg.group;
|
||||||
fi
|
ExecStart = utils.escapeSystemdExecArgs [
|
||||||
|
(lib.getExe instanceCfg.package)
|
||||||
|
"-nobrowser"
|
||||||
|
"-data=${instanceCfg.dataDir}"
|
||||||
|
"-port=${toString instanceCfg.port}"
|
||||||
|
];
|
||||||
|
WorkingDirectory = instanceCfg.dataDir;
|
||||||
|
RuntimeDirectory = "sonarr-${name}";
|
||||||
|
LogsDirectory = "sonarr-${name}";
|
||||||
|
RuntimeDirectoryMode = "0750";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 5;
|
||||||
|
}
|
||||||
|
(lib.mkIf instanceCfg.hardening {
|
||||||
|
CapabilityBoundingSet = [""];
|
||||||
|
DeviceAllow = [""];
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = false;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ReadWritePaths = [
|
||||||
|
instanceCfg.dataDir
|
||||||
|
instanceCfg.tvDir
|
||||||
|
"/var/log/sonarr-${name}"
|
||||||
|
"/eru/media"
|
||||||
|
];
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
"AF_NETLINK"
|
||||||
|
];
|
||||||
|
RestrictNamespaces = [
|
||||||
|
"uts"
|
||||||
|
"ipc"
|
||||||
|
"pid"
|
||||||
|
"user"
|
||||||
|
"cgroup"
|
||||||
|
"net"
|
||||||
|
"mnt"
|
||||||
|
];
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
{
|
||||||
|
ExecStartPre = "+${pkgs.writeShellScript "sonarr-${name}-pre-script" ''
|
||||||
|
mkdir -p /run/sonarr-${name}
|
||||||
|
rm -f /run/sonarr-${name}/secrets.env
|
||||||
|
|
||||||
# Database Configuration
|
# Helper function to safely write variables
|
||||||
write_var "SONARR__POSTGRES__HOST" "$([ -n "${instanceCfg.db.host}" ] && echo "${instanceCfg.db.host}" || cat "${instanceCfg.db.hostFile}")"
|
write_var() {
|
||||||
write_var "SONARR__POSTGRES__USER" "$([ -n "${instanceCfg.db.userFile}" ] && cat "${instanceCfg.db.userFile}" || echo "${instanceCfg.db.user}")"
|
local var_name="$1"
|
||||||
write_var "SONARR__POSTGRES__PASSWORD" "$(cat ${instanceCfg.db.passwordFile})"
|
local value="$2"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
printf "%s=%s\n" "$var_name" "$value" >> /run/sonarr-${name}/secrets.env
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Final permissions
|
# API Key (direct value or file)
|
||||||
chmod 600 /run/sonarr-${name}/secrets.env
|
if [ -n "${instanceCfg.apiKey}" ]; then
|
||||||
chown ${instanceCfg.user}:${instanceCfg.group} /run/sonarr-${name}/secrets.env
|
write_var "SONARR__AUTH__APIKEY" "${instanceCfg.apiKey}"
|
||||||
''}";
|
else
|
||||||
|
write_var "SONARR__AUTH__APIKEY" "$(cat ${instanceCfg.apiKeyFile})"
|
||||||
|
fi
|
||||||
|
|
||||||
EnvironmentFile = (
|
${lib.optionalString instanceCfg.db.enable ''
|
||||||
[ "-/run/sonarr-${name}/secrets.env" ]
|
# Database Configuration
|
||||||
++ lib.optional (
|
write_var "SONARR__POSTGRES__HOST" "$([ -n "${instanceCfg.db.host}" ] && echo "${instanceCfg.db.host}" || cat "${instanceCfg.db.hostFile}")"
|
||||||
instanceCfg.extraEnvVarFile != null && instanceCfg.extraEnvVarFile != ""
|
write_var "SONARR__POSTGRES__USER" "$([ -n "${instanceCfg.db.userFile}" ] && cat "${instanceCfg.db.userFile}" || echo "${instanceCfg.db.user}")"
|
||||||
) instanceCfg.extraEnvVarFile
|
write_var "SONARR__POSTGRES__PASSWORD" "$(cat ${instanceCfg.db.passwordFile})"
|
||||||
);
|
''}
|
||||||
})
|
|
||||||
];
|
# Final permissions
|
||||||
}
|
chmod 600 /run/sonarr-${name}/secrets.env
|
||||||
|
chown ${instanceCfg.user}:${instanceCfg.group} /run/sonarr-${name}/secrets.env
|
||||||
|
''}";
|
||||||
|
|
||||||
|
EnvironmentFile = (
|
||||||
|
["-/run/sonarr-${name}/secrets.env"]
|
||||||
|
++ lib.optional (
|
||||||
|
instanceCfg.extraEnvVarFile != null && instanceCfg.extraEnvVarFile != ""
|
||||||
|
)
|
||||||
|
instanceCfg.extraEnvVarFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) cfg.instances;
|
cfg.instances;
|
||||||
|
|
||||||
# Firewall configurations
|
# Firewall configurations
|
||||||
networking.firewall = mkMerge (
|
networking.firewall = mkMerge (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
mkIf (instanceCfg.enable && instanceCfg.openFirewall) {
|
mkIf (instanceCfg.enable && instanceCfg.openFirewall) {
|
||||||
allowedTCPPorts = [ instanceCfg.port ];
|
allowedTCPPorts = [instanceCfg.port];
|
||||||
}
|
}
|
||||||
) cfg.instances
|
)
|
||||||
|
cfg.instances
|
||||||
);
|
);
|
||||||
|
|
||||||
# Users and groups
|
# Users and groups
|
||||||
users = mkMerge (
|
users = mkMerge (
|
||||||
mapAttrsToList (
|
mapAttrsToList (
|
||||||
name: instanceCfg:
|
name: instanceCfg:
|
||||||
mkIf instanceCfg.enable {
|
mkIf instanceCfg.enable {
|
||||||
groups.${instanceCfg.group} = { };
|
groups.${instanceCfg.group} = {};
|
||||||
users = mkIf (instanceCfg.user == "sonarr") {
|
users = mkIf (instanceCfg.user == "sonarr") {
|
||||||
sonarr = {
|
sonarr = {
|
||||||
inherit (instanceCfg) group;
|
inherit (instanceCfg) group;
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
# home = instanceCfg.dataDir;
|
# home = instanceCfg.dataDir;
|
||||||
home = "/nahar/sonarr";
|
home = "/nahar/sonarr";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
)
|
||||||
) cfg.instances
|
cfg.instances
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue