From 3e40ff7bbe0b3976a16d41b2ea382c1a39133066 Mon Sep 17 00:00:00 2001 From: Felix Buehler Date: Tue, 17 Sep 2024 23:33:28 +0200 Subject: [PATCH 1/2] nixos/sonarr: add settings option --- nixos/modules/services/misc/sonarr.nix | 113 ++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/nixos/modules/services/misc/sonarr.nix b/nixos/modules/services/misc/sonarr.nix index 65f43df08312..b11293cbd27a 100644 --- a/nixos/modules/services/misc/sonarr.nix +++ b/nixos/modules/services/misc/sonarr.nix @@ -7,6 +7,18 @@ }: let cfg = config.services.sonarr; + + settingsEnvVars = lib.pipe cfg.settings [ + (lib.mapAttrsRecursive ( + path: value: + lib.optionalAttrs (value != null) { + name = lib.toUpper "SONARR__${lib.concatStringsSep "__" path}"; + value = toString (if lib.isBool value then lib.boolToString value else value); + } + )) + (lib.collect (x: lib.isString x.name or false && lib.isString x.value or false)) + lib.listToAttrs + ]; in { options = { @@ -27,6 +39,85 @@ in ''; }; + apiKeyFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + description = '' + Path to the file containing the API key for Sonarr (32 chars). + This will overwrite the API key in the config file. + ''; + example = "/run/secrets/sonarr-apikey"; + default = null; + }; + + environmentFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + description = '' + Environment file to pass secret configuration values. + + Each line must follow the `SONARR__SECTION__KEY=value` pattern. + Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config). + ''; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = (pkgs.formats.ini { }).type; + options = { + update = { + mechanism = lib.mkOption { + type = + with lib.types; + nullOr (enum [ + "external" + "builtIn" + "script" + ]); + description = "which update mechanism to use"; + default = "external"; + }; + automatically = lib.mkOption { + type = lib.types.bool; + description = "Automatically download and install updates."; + default = false; + }; + }; + server = { + port = lib.mkOption { + type = lib.types.int; + description = "Port Number"; + default = 8989; + }; + }; + log = { + analyticsEnabled = lib.mkOption { + type = lib.types.bool; + description = "Send Anonymous Usage Data"; + default = false; + }; + }; + }; + }; + example = lib.options.literalExpression '' + { + update.mechanism = "internal"; + server = { + urlbase = "localhost"; + port = 8989; + bindaddress = "*"; + }; + } + ''; + default = { }; + description = '' + Attribute set of arbitrary config options. + Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config). + + WARNING: this configuration is stored in the world-readable Nix store! + Use [](#opt-services.sonarr.environmentFiles) if it contains a secret. + ''; + }; + user = lib.mkOption { type = lib.types.str; default = "sonarr"; @@ -44,6 +135,15 @@ in }; config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !(cfg.settings ? auth && cfg.settings.auth ? apikey); + message = '' + The `services.sonarr.settings` attribute set must not contain `ApiKey`, you should instead use `services.sonarr.apiKeyFile` to avoid storing secrets in the Nix store. + ''; + } + ]; + systemd.tmpfiles.rules = [ "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -" ]; @@ -52,11 +152,20 @@ in description = "Sonarr"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - + environment = settingsEnvVars; serviceConfig = { Type = "simple"; User = cfg.user; Group = cfg.group; + EnvironmentFile = cfg.environmentFiles; + LoadCredential = lib.optional (cfg.apiKeyFile != null) "SONARR__AUTH__APIKEY:${cfg.apiKeyFile}"; + ExecStartPre = lib.optionalString (cfg.apiKeyFile != null) pkgs.writeShellScript "sonarr-apikey" '' + if [ ! -s config.xml ]; then + echo '' > "${cfg.dataDir}/config.xml" + fi + ${lib.getExe pkgs.xmlstarlet} ed -L -u "/Config/ApiKey" -v "@API_KEY@" "${cfg.dataDir}/config.xml" + ${lib.getExe pkgs.replace-secret} '@API_KEY@' ''${CREDENTIALS_DIRECTORY}/SONARR__AUTH__APIKEY "${cfg.dataDir}/config.xml" + ''; ExecStart = utils.escapeSystemdExecArgs [ (lib.getExe cfg.package) "-nobrowser" @@ -67,7 +176,7 @@ in }; networking.firewall = lib.mkIf cfg.openFirewall { - allowedTCPPorts = [ 8989 ]; + allowedTCPPorts = [ cfg.settings.server.port ]; }; users.users = lib.mkIf (cfg.user == "sonarr") { From 203d5d954c6cba152d9497c0cda4a1d93b377817 Mon Sep 17 00:00:00 2001 From: Felix Buehler Date: Wed, 31 Jan 2024 20:13:32 +0100 Subject: [PATCH 2/2] nixos/tests/prometheus-exporters: update exportarr-sonarr test --- nixos/tests/prometheus-exporters.nix | 30 ++++++---------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index be11bddb9329..7b2b757f6479 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -356,36 +356,18 @@ let ''; }; - exportarr-sonarr = let apikey = "eccff6a992bc2e4b88e46d064b26bb4e"; in { + exportarr-sonarr = let apiKeyFile = pkgs.writeText "dummy-api-key" "eccff6a992bc2e4b88e46d064b26bb4e"; in { nodeName = "exportarr_sonarr"; exporterConfig = { enable = true; url = "http://127.0.0.1:8989"; - apiKeyFile = pkgs.writeText "dummy-api-key" apikey; + inherit apiKeyFile; }; metricProvider = { - services.sonarr.enable = true; - systemd.services.sonarr.serviceConfig.ExecStartPre = - let - sonarr_config = pkgs.writeText "config.xml" '' - - info - False - 8989 - 9898 - - * - ${apikey} - None - BuiltIn - main - Sonarr - - ''; - in - [ - ''${pkgs.coreutils}/bin/install -D -m 777 ${sonarr_config} -T /var/lib/sonarr/.config/NzbDrone/config.xml'' - ]; + services.sonarr = { + enable = true; + inherit apiKeyFile; + }; }; exporterTest = '' wait_for_unit("sonarr.service")