diff --git a/nixos/modules/services/security/privacyidea.nix b/nixos/modules/services/security/privacyidea.nix index 5cd338ebf7fe..e446e606cad8 100644 --- a/nixos/modules/services/security/privacyidea.nix +++ b/nixos/modules/services/security/privacyidea.nix @@ -61,6 +61,12 @@ let (flip mapAttrs cfg.ldap-proxy.settings (const (mapAttrs (const renderValue))))); + privacyidea-token-janitor = pkgs.writeShellScriptBin "privacyidea-token-janitor" '' + exec -a privacyidea-token-janitor \ + /run/wrappers/bin/sudo -u ${cfg.user} \ + env PRIVACYIDEA_CONFIGFILE=${cfg.stateDir}/privacyidea.cfg \ + ${penv}/bin/privacyidea-token-janitor $@ + ''; in { @@ -178,6 +184,42 @@ in description = lib.mdDoc "Group account under which PrivacyIDEA runs."; }; + tokenjanitor = { + enable = mkEnableOption (lib.mdDoc "automatic runs of the token janitor"); + interval = mkOption { + default = "quarterly"; + type = types.str; + description = lib.mdDoc '' + Interval in which the cleanup program is supposed to run. + See {manpage}`systemd.time(7)` for further information. + ''; + }; + action = mkOption { + type = types.enum [ "delete" "mark" "disable" "unassign" ]; + description = lib.mdDoc '' + Which action to take for matching tokens. + ''; + }; + unassigned = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether to search for **unassigned** tokens + and apply [](#opt-services.privacyidea.tokenjanitor.action) + onto them. + ''; + }; + orphaned = mkOption { + default = true; + type = types.bool; + description = lib.mdDoc '' + Whether to search for **orphaned** tokens + and apply [](#opt-services.privacyidea.tokenjanitor.action) + onto them. + ''; + }; + }; + ldap-proxy = { enable = mkEnableOption (lib.mdDoc "PrivacyIDEA LDAP Proxy"); @@ -228,10 +270,60 @@ in (mkIf cfg.enable { - environment.systemPackages = [ pkgs.privacyidea ]; + assertions = [ + { + assertion = cfg.tokenjanitor.enable -> (cfg.tokenjanitor.orphaned || cfg.tokenjanitor.unassigned); + message = '' + privacyidea-token-janitor has no effect if neither orphaned nor unassigned tokens + are to be searched. + ''; + } + ]; + + environment.systemPackages = [ pkgs.privacyidea (hiPrio privacyidea-token-janitor) ]; services.postgresql.enable = mkDefault true; + systemd.services.privacyidea-tokenjanitor = mkIf cfg.tokenjanitor.enable { + environment.PRIVACYIDEA_CONFIGFILE = "${cfg.stateDir}/privacyidea.cfg"; + path = [ penv ]; + serviceConfig = { + CapabilityBoundingSet = [ "" ]; + ExecStart = "${pkgs.writeShellScript "pi-token-janitor" '' + ${optionalString cfg.tokenjanitor.orphaned '' + echo >&2 "Removing orphaned tokens..." + privacyidea-token-janitor find \ + --orphaned true \ + --action ${cfg.tokenjanitor.action} + ''} + ${optionalString cfg.tokenjanitor.unassigned '' + echo >&2 "Removing unassigned tokens..." + privacyidea-token-janitor find \ + --assigned false \ + --action ${cfg.tokenjanitor.action} + ''} + ''}"; + Group = cfg.group; + LockPersonality = true; + MemoryDenyWriteExecute = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + ReadWritePaths = cfg.stateDir; + Type = "oneshot"; + User = cfg.user; + WorkingDirectory = cfg.stateDir; + }; + }; + systemd.timers.privacyidea-tokenjanitor = mkIf cfg.tokenjanitor.enable { + wantedBy = [ "timers.target" ]; + timerConfig.OnCalendar = cfg.tokenjanitor.interval; + timerConfig.Persistent = true; + }; + systemd.services.privacyidea = let piuwsgi = pkgs.writeText "uwsgi.json" (builtins.toJSON { uwsgi = { diff --git a/pkgs/applications/misc/privacyidea/default.nix b/pkgs/applications/misc/privacyidea/default.nix index d7613b0c2a54..405d992576da 100644 --- a/pkgs/applications/misc/privacyidea/default.nix +++ b/pkgs/applications/misc/privacyidea/default.nix @@ -1,5 +1,5 @@ { lib, fetchFromGitHub, cacert, openssl, nixosTests -, python39 +, python39, fetchpatch }: let @@ -107,6 +107,16 @@ python3'.pkgs.buildPythonPackage rec { pydash ecdsa google-auth importlib-metadata argon2-cffi bcrypt ]; + patches = [ + # Apply https://github.com/privacyidea/privacyidea/pull/3304, fixes + # `Exceeds the limit (4300) for integer string conversion` in the tests, + # see https://hydra.nixos.org/build/192932057 + (fetchpatch { + url = "https://github.com/privacyidea/privacyidea/commit/0e28f36c0b3291a361669f4a3a77c294f4564475.patch"; + sha256 = "sha256-QqcO8bkt+I2JKce/xk2ZhzEaLZ3E4uZ4x5W9Kk0pMQQ="; + }) + ]; + passthru.tests = { inherit (nixosTests) privacyidea; }; checkInputs = with python3'.pkgs; [ openssl mock pytestCheckHook responses testfixtures ];