Replace the 1301-line monolithic module.nix with focused modules: - modules/servarr.nix (Sonarr/Radarr/Prowlarr) - modules/bazarr.nix (Bazarr provider connections) - modules/jellyseerr.nix (Jellyseerr quality profiles) - modules/default.nix (import aggregator) Python scripts (from prior commit) are referenced as standalone files via PYTHONPATH, with config passed as a JSON file argument. New options: - Add bindAddress option to all services (default 127.0.0.1) - Replace hardcoded wg.service dependency with configurable networkNamespaceService option - Add systemd hardening: PrivateTmp, NoNewPrivileges, ProtectHome, ProtectKernelTunables/Modules, ProtectControlGroups, RestrictSUIDSGID, SystemCallArchitectures=native Test updates: - Extract mock qBittorrent/SABnzbd servers into tests/lib/mocks.nix - Fix duplicate wait_for_unit calls in integration test
183 lines
4.8 KiB
Nix
183 lines
4.8 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.services.bazarrInit;
|
|
|
|
scriptDir = ../scripts;
|
|
|
|
pythonEnv = pkgs.python3.withPackages (
|
|
ps: with ps; [
|
|
pyyaml
|
|
requests
|
|
]
|
|
);
|
|
|
|
bazarrProviderModule = lib.types.submodule {
|
|
options = {
|
|
enable = lib.mkEnableOption "provider connection";
|
|
|
|
dataDir = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Path to the provider's data directory containing config.xml.";
|
|
example = "/services/sonarr";
|
|
};
|
|
|
|
bindAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = "IP address of the provider (Sonarr/Radarr) for Bazarr to connect to.";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
description = "API port of the provider.";
|
|
example = 8989;
|
|
};
|
|
|
|
serviceName = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Name of the systemd service to depend on.";
|
|
example = "sonarr";
|
|
};
|
|
};
|
|
};
|
|
|
|
bazarrInitModule = lib.types.submodule {
|
|
options = {
|
|
enable = lib.mkEnableOption "Bazarr API initialization";
|
|
|
|
dataDir = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Path to Bazarr's data directory containing config/config.yaml.";
|
|
example = "/services/bazarr";
|
|
};
|
|
|
|
bindAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = "IP address the Bazarr API is listening on.";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 6767;
|
|
description = "API port of Bazarr.";
|
|
};
|
|
|
|
apiTimeout = lib.mkOption {
|
|
type = lib.types.ints.positive;
|
|
default = 90;
|
|
description = ''
|
|
Seconds to wait for the Bazarr API to become available before
|
|
considering the init attempt failed. When the API is not reachable
|
|
within this window, the service exits non-zero and systemd's
|
|
Restart=on-failure will schedule another attempt after RestartSec.
|
|
|
|
The systemd start limit is computed from this value to allow 5 full
|
|
retry cycles before the unit enters permanent failure.
|
|
'';
|
|
};
|
|
|
|
sonarr = lib.mkOption {
|
|
type = bazarrProviderModule;
|
|
default = {
|
|
enable = false;
|
|
};
|
|
description = "Sonarr provider configuration.";
|
|
};
|
|
|
|
radarr = lib.mkOption {
|
|
type = bazarrProviderModule;
|
|
default = {
|
|
enable = false;
|
|
};
|
|
description = "Radarr provider configuration.";
|
|
};
|
|
};
|
|
};
|
|
|
|
mkBazarrInitConfig = builtins.toJSON {
|
|
dataDir = cfg.dataDir;
|
|
bindAddress = cfg.bindAddress;
|
|
port = cfg.port;
|
|
apiTimeout = cfg.apiTimeout;
|
|
providers =
|
|
{ }
|
|
// lib.optionalAttrs cfg.sonarr.enable {
|
|
sonarr = {
|
|
enable = true;
|
|
dataDir = cfg.sonarr.dataDir;
|
|
bindAddress = cfg.sonarr.bindAddress;
|
|
port = cfg.sonarr.port;
|
|
};
|
|
}
|
|
// lib.optionalAttrs cfg.radarr.enable {
|
|
radarr = {
|
|
enable = true;
|
|
dataDir = cfg.radarr.dataDir;
|
|
bindAddress = cfg.radarr.bindAddress;
|
|
port = cfg.radarr.port;
|
|
};
|
|
};
|
|
};
|
|
|
|
configFile = pkgs.writeText "bazarr-init-config.json" mkBazarrInitConfig;
|
|
|
|
bazarrDeps =
|
|
[
|
|
"bazarr.service"
|
|
]
|
|
++ (lib.optional cfg.sonarr.enable "${cfg.sonarr.serviceName}.service")
|
|
++ (lib.optional cfg.radarr.enable "${cfg.radarr.serviceName}.service");
|
|
|
|
hardeningConfig = {
|
|
PrivateTmp = true;
|
|
NoNewPrivileges = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectKernelModules = true;
|
|
ProtectControlGroups = true;
|
|
RestrictSUIDSGID = true;
|
|
ProtectHome = true;
|
|
SystemCallArchitectures = "native";
|
|
};
|
|
in
|
|
{
|
|
options.services.bazarrInit = lib.mkOption {
|
|
type = bazarrInitModule;
|
|
default = {
|
|
enable = false;
|
|
};
|
|
description = ''
|
|
Bazarr API initialization for connecting Sonarr and Radarr providers.
|
|
Bazarr uses a different API than Servarr applications, so it has its own module.
|
|
'';
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
systemd.services.bazarr-init = {
|
|
description = "Initialize Bazarr API connections";
|
|
after = bazarrDeps;
|
|
requires = bazarrDeps;
|
|
wantedBy = [ "multi-user.target" ];
|
|
environment.PYTHONPATH = "${scriptDir}";
|
|
unitConfig = {
|
|
StartLimitIntervalSec = 5 * (cfg.apiTimeout + 30);
|
|
StartLimitBurst = 5;
|
|
};
|
|
serviceConfig =
|
|
{
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
Restart = "on-failure";
|
|
RestartSec = 30;
|
|
ExecStart = "${pythonEnv}/bin/python3 ${scriptDir}/bazarr_init.py ${configFile}";
|
|
}
|
|
// hardeningConfig;
|
|
};
|
|
};
|
|
}
|