{ config, lib, pkgs, ... }: with lib; let cfg = config.nixarr; # Helper to create API key extraction for a service mkApiKeyExtractor = serviceName: serviceConfig: { description = "Extract ${serviceName} API key"; after = ["${serviceName}.service"]; requires = ["${serviceName}.service"]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; # Use DynamicUser if the parent service does DynamicUser = serviceConfig.serviceConfig.DynamicUser or false; # Only set User if not using DynamicUser ${ if !(serviceConfig.serviceConfig.DynamicUser or false) then "User" else null } = serviceConfig.user or null; Group = "${serviceName}-api"; UMask = "0027"; # Results in 0640 permissions ExecStartPre = [ "${pkgs.coreutils}/bin/mkdir -p ${cfg.stateDir}/api-keys" "${pkgs.coreutils}/bin/chown root:${serviceName}-api ${cfg.stateDir}/api-keys" "${pkgs.coreutils}/bin/chmod 750 ${cfg.stateDir}/api-keys" # Wait for config file to exist "${pkgs.bash}/bin/bash -c 'while [ ! -f ${serviceConfig.stateDir}/config.xml ]; do sleep 1; done'" ]; # Bazarr api key is located a different place... ExecStart = pkgs.writeShellScript "extract-${serviceName}-api-key" '' ${pkgs.dasel}/bin/dasel -f "${serviceConfig.stateDir}/config.xml" \ -s ".Config.ApiKey" | tr -d '\n\r' > "${cfg.stateDir}/api-keys/${serviceName}.key" chown $USER:${serviceName}-api "${cfg.stateDir}/api-keys/${serviceName}.key" ''; }; }; in { config = mkIf cfg.enable { # Create per-service API key groups users.groups = mkMerge [ (mkIf cfg.sonarr.enable {sonarr-api = {};}) (mkIf cfg.radarr.enable {radarr-api = {};}) (mkIf cfg.lidarr.enable {lidarr-api = {};}) (mkIf cfg.readarr.enable {readarr-api = {};}) (mkIf cfg.prowlarr.enable {prowlarr-api = {};}) # (mkIf cfg.bazarr.enable {bazarr-api = {};}) ]; # Add services that need API keys to their respective groups users.users = mkMerge [ # Static users (mkIf cfg.transmission.enable { transmission.extraGroups = optional cfg.prowlarr.enable "prowlarr-api"; }) (mkIf cfg.transmission.privateTrackers.cross-seed.enable { cross-seed.extraGroups = optional cfg.prowlarr.enable "prowlarr-api"; }) ]; # Add api groups to services with DynamicUser systemd.services = mkMerge [ (mkIf cfg.sonarr.enable {sonarr.serviceConfig.SupplementaryGroups = ["sonarr-api"];}) (mkIf cfg.radarr.enable {radarr.serviceConfig.SupplementaryGroups = ["radarr-api"];}) (mkIf cfg.lidarr.enable {lidarr.serviceConfig.SupplementaryGroups = ["lidarr-api"];}) (mkIf cfg.readarr.enable {readarr.serviceConfig.SupplementaryGroups = ["readarr-api"];}) (mkIf cfg.prowlarr.enable {prowlarr.serviceConfig.SupplementaryGroups = ["prowlarr-api"];}) # (mkIf cfg.bazarr.enable {bazarr.serviceConfig.SupplementaryGroups = ["bazarr-api"];}) (mkIf cfg.recyclarr.enable { recyclarr.serviceConfig.SupplementaryGroups = (optional cfg.sonarr.enable "sonarr-api") ++ (optional cfg.radarr.enable "radarr-api"); }) # Create API key extractors for enabled services (mkIf cfg.sonarr.enable {"sonarr-api-key" = mkApiKeyExtractor "sonarr" cfg.sonarr;}) (mkIf cfg.radarr.enable {"radarr-api-key" = mkApiKeyExtractor "radarr" cfg.radarr;}) (mkIf cfg.lidarr.enable {"lidarr-api-key" = mkApiKeyExtractor "lidarr" cfg.lidarr;}) (mkIf cfg.readarr.enable {"readarr-api-key" = mkApiKeyExtractor "readarr" cfg.readarr;}) (mkIf cfg.prowlarr.enable {"prowlarr-api-key" = mkApiKeyExtractor "prowlarr" cfg.prowlarr;}) # (mkIf cfg.bazarr.enable {"bazarr-api-key" = mkApiKeyExtractor "bazarr" cfg.bazarr;}) ]; # Create the api-keys directory systemd.tmpfiles.rules = [ "d ${cfg.stateDir}/api-keys 0750 root root - -" ]; }; }