diff --git a/nixos/modules/services/networking/sabnzbd/config_merge.py b/nixos/modules/services/networking/sabnzbd/config_merge.py new file mode 100644 index 000000000000..25bd9cd000cc --- /dev/null +++ b/nixos/modules/services/networking/sabnzbd/config_merge.py @@ -0,0 +1,19 @@ +import sys +import os.path +from configobj import ConfigObj, ParseError + +base = ConfigObj() + +for path in sys.argv[1:]: + if os.path.isfile(path): + with open(path) as f: + try: + update = ConfigObj(f) + base.merge(update) + except ParseError as e: + raise Exception(f"Failed to merge {path}") from e + else: + raise Exception(f"Instructed to merge {path} but not found or not a file") + +for line in base.write(): + print(line) diff --git a/nixos/modules/services/networking/sabnzbd/default.nix b/nixos/modules/services/networking/sabnzbd/default.nix index 013c87834674..e44053344ac3 100644 --- a/nixos/modules/services/networking/sabnzbd/default.nix +++ b/nixos/modules/services/networking/sabnzbd/default.nix @@ -145,6 +145,35 @@ in ''; }; + secretFiles = mkOption { + type = with types; listOf path; + description = '' + Path to a list of ini file containing confidential settings such as credentials. + Settings here will be merged with the rest of the configuration (with + the secret settings taking precedence in case of conflicts, and files + that occur later in this list taking precedence over those that + occur earlier). + Recommended settings: + - misc.api_key, misc.nzb_key, misc.username, misc.password + - misc.email_account, misc.email_pwd if email alerts are enabled + - servers..username, servers..password + ''; + default = [ ]; + }; + + allowConfigWrite = mkOption { + type = types.bool; + description = '' + By default we create the sabnzbd configuration read-only, + which keeps the nixos configuration as the single source + of truth. If you want to enable configuration of + sabnzbd via the web interface or use options that require + a writeable configuration, such as quota tracking, enable + this option. + ''; + default = lib.versionOlder config.system.stateVersion "25.11"; + }; + settings = mkOption { description = '' The sabnzbd configuration (see also @@ -481,7 +510,9 @@ in systemd.services.sabnzbd = let - publicIniQuoted = lib.escapeShellArg publicSettingsIni; + files = + (lib.optional cfg.allowConfigWrite sabnzbdIniPath) ++ [ publicSettingsIni ] ++ cfg.secretFiles; + iniPathQuoted = lib.escapeShellArg sabnzbdIniPath; in { description = "sabnzbd server"; @@ -495,6 +526,17 @@ in StateDirectory = cfg.stateDir; ExecStart = "${lib.getExe cfg.package} -d -f ${iniPathQuoted}"; }; + preStart = '' + set -euo pipefail + + ${lib.toShellVar "files" files} + + ${lib.getExe (pkgs.python3.withPackages (py: [ py.configobj ]))} \ + ${./config_merge.py} \ + "''${files[@]}" | \ + install -D -m ${if cfg.allowConfigWrite then "600" else "400"} \ + -o '${cfg.user}' -g '${cfg.group}' /dev/stdin ${iniPathQuoted} + ''; }; networking.firewall = mkIf cfg.openFirewall {