diff --git a/modules/system/services/actualbudget/backups.nix b/modules/system/services/actualbudget/backups.nix index 092552a..efaeadd 100644 --- a/modules/system/services/actualbudget/backups.nix +++ b/modules/system/services/actualbudget/backups.nix @@ -28,7 +28,7 @@ in ''; }; - custom.services.resticBackup.actual = { + custom.services.resticBackups.actual = { inherit user; healthchecks.enable = true; diff --git a/modules/system/services/forgejo/backups.nix b/modules/system/services/forgejo/backups.nix index a1ef598..7ce3525 100644 --- a/modules/system/services/forgejo/backups.nix +++ b/modules/system/services/forgejo/backups.nix @@ -28,7 +28,7 @@ in ''; }; - custom.services.resticBackup.forgejo = { + custom.services.resticBackups.forgejo = { inherit user; healthchecks.enable = true; diff --git a/modules/system/services/hedgedoc/backups.nix b/modules/system/services/hedgedoc/backups.nix index 55cedd7..70f9a20 100644 --- a/modules/system/services/hedgedoc/backups.nix +++ b/modules/system/services/hedgedoc/backups.nix @@ -28,7 +28,7 @@ in ''; }; - custom.services.resticBackup.hedgedoc = { + custom.services.resticBackups.hedgedoc = { inherit user; healthchecks.enable = true; diff --git a/modules/system/services/nextcloud/backups.nix b/modules/system/services/nextcloud/backups.nix index 92642f1..5cc55fe 100644 --- a/modules/system/services/nextcloud/backups.nix +++ b/modules/system/services/nextcloud/backups.nix @@ -14,7 +14,7 @@ in options.custom.services.nextcloud.backups.enable = lib.mkEnableOption ""; config = lib.mkIf cfg.backups.enable { - custom.services.resticBackup.nextcloud = { + custom.services.resticBackups.nextcloud = { inherit user; healthchecks.enable = true; diff --git a/modules/system/services/restic-backup.nix b/modules/system/services/restic-backup.nix deleted file mode 100644 index b3fae2c..0000000 --- a/modules/system/services/restic-backup.nix +++ /dev/null @@ -1,106 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: -let - backupServices = lib.filterAttrs (_: value: value.enable) config.custom.services.resticBackup; - - healthchecksEnable = (lib.filterAttrs (_: value: value.healthchecks.enable) backupServices) != { }; -in -{ - options.custom.services.resticBackup = lib.mkOption { - type = lib.types.attrsOf ( - lib.types.submodule { - options = { - enable = lib.mkEnableOption "" // { - default = true; - }; - user = lib.mkOption { - type = lib.types.str; - default = config.users.users.root.name; - }; - healthchecks.enable = lib.mkEnableOption ""; - extraConfig = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - default = { }; - }; - }; - } - ); - default = { }; - }; - - config = lib.mkIf (backupServices != { }) { - users.groups.backup.members = builtins.filter (user: user != config.users.users.root.name) ( - lib.mapAttrsToList (_: value: value.user) backupServices - ); - - sops.secrets = - let - resticPermissions = { - mode = "440"; - group = config.users.groups.backup.name; - }; - in - { - "restic/environment" = resticPermissions; - "restic/password" = resticPermissions; - "healthchecks-ping-key" = lib.mkIf healthchecksEnable resticPermissions; - }; - - systemd.tmpfiles.rules = lib.mapAttrsToList ( - name: value: - "d /var/cache/restic-backups-${name} 700 ${value.user} ${config.users.groups.backup.name} -" - ) backupServices; - - services.restic.backups = lib.mapAttrs ( - name: value: - lib.mkMerge [ - { - inherit (value) user; - initialize = true; - repository = "s3:https://s3.eu-central-003.backblazeb2.com/stork-atlas/${name}"; - environmentFile = config.sops.secrets."restic/environment".path; - passwordFile = config.sops.secrets."restic/password".path; - pruneOpts = [ - "--keep-daily 7" - "--keep-weekly 5" - "--keep-monthly 6" - "--keep-yearly 1" - ]; - timerConfig = { - OnCalendar = "03:00"; - RandomizedDelaySec = "1h"; - }; - } - value.extraConfig - ] - ) backupServices; - - systemd.services = lib.mkMerge [ - (lib.mapAttrs' ( - name: _: - lib.nameValuePair "restic-backups-${name}" { - wants = [ "healthcheck-ping@${name}-backup_start.service" ]; - onSuccess = [ "healthcheck-ping@${name}-backup.service" ]; - onFailure = [ "healthcheck-ping@${name}-backup_fail.service" ]; - } - ) (lib.filterAttrs (_: value: value.healthchecks.enable) backupServices)) - - (lib.mkIf healthchecksEnable { - "healthcheck-ping@" = { - description = "Pings healthcheck (%i)"; - serviceConfig.Type = "oneshot"; - scriptArgs = "%i"; - script = '' - ${lib.getExe pkgs.curl} --fail --silent --show-error --max-time 10 --retry 5 https://hc-ping.com/$(cat ${ - config.sops.secrets."healthchecks-ping-key".path - })/$(echo $1 | tr _ /) - ''; - }; - }) - ]; - }; -} diff --git a/modules/system/services/restic-backups/default.nix b/modules/system/services/restic-backups/default.nix new file mode 100644 index 0000000..80da57a --- /dev/null +++ b/modules/system/services/restic-backups/default.nix @@ -0,0 +1,81 @@ +{ + config, + lib, + ... +}: +let + resticBackups = lib.filterAttrs (_: value: value.enable) config.custom.services.resticBackups; + + backupUsers = lib.mapAttrsToList (_: value: value.user) resticBackups; +in +{ + options.custom.services.resticBackups = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + enable = lib.mkEnableOption "" // { + default = true; + }; + user = lib.mkOption { + type = lib.types.str; + default = config.users.users.root.name; + }; + extraConfig = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = { }; + }; + }; + } + ); + default = { }; + }; + + config = lib.mkIf (resticBackups != { }) { + assertions = [ + { + assertion = lib.any (user: user != config.users.users.root.name) backupUsers; + message = "restic shouldn't be run as root"; + } + ]; + + users.groups.backup.members = backupUsers; + + sops.secrets = + let + resticPermissions = { + mode = "440"; + group = config.users.groups.backup.name; + }; + in + { + "restic/environment" = resticPermissions; + "restic/password" = resticPermissions; + }; + + services.restic.backups = + resticBackups + |> lib.mapAttrs ( + name: value: + lib.mkMerge [ + { + inherit (value) user; + initialize = true; + repository = "s3:https://s3.eu-central-003.backblazeb2.com/stork-atlas/${name}"; + environmentFile = config.sops.secrets."restic/environment".path; + passwordFile = config.sops.secrets."restic/password".path; + pruneOpts = [ + "--keep-daily 7" + "--keep-weekly 5" + "--keep-monthly 6" + "--keep-yearly 1" + ]; + timerConfig = { + OnCalendar = "03:00"; + RandomizedDelaySec = "1h"; + }; + } + value.extraConfig + ] + ); + }; +} diff --git a/modules/system/services/restic-backups/healthchecks.nix b/modules/system/services/restic-backups/healthchecks.nix new file mode 100644 index 0000000..be105f4 --- /dev/null +++ b/modules/system/services/restic-backups/healthchecks.nix @@ -0,0 +1,57 @@ +{ + config, + pkgs, + lib, + ... +}: +let + inherit (config.custom.services) resticBackups; + + backupsWithHealthchecks = + resticBackups + |> lib.filterAttrs (_: value: value.enable) + |> lib.filterAttrs (_: value: value.healthchecks.enable); +in +{ + options.custom.services.resticBackups = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options.healthchecks.enable = lib.mkEnableOption ""; + } + ); + }; + + config = lib.mkIf (resticBackups != { }) { + sops.secrets."healthchecks-ping-key" = { + mode = "440"; + group = config.users.groups.backup.name; + }; + + systemd.services = lib.mkMerge [ + { + "healthcheck-ping@" = { + description = "Pings healthcheck (%i)"; + serviceConfig.Type = "oneshot"; + scriptArgs = "%i"; + script = '' + ${lib.getExe pkgs.curl} --fail --silent --show-error --max-time 10 --retry 5 https://hc-ping.com/$(cat ${ + config.sops.secrets."healthchecks-ping-key".path + })/$(echo $1 | tr _ /) + ''; + }; + } + + ( + backupsWithHealthchecks + |> lib.mapAttrs' ( + name: _: + lib.nameValuePair "restic-backups-${name}" { + wants = [ "healthcheck-ping@${name}-backup_start.service" ]; + onSuccess = [ "healthcheck-ping@${name}-backup.service" ]; + onFailure = [ "healthcheck-ping@${name}-backup_fail.service" ]; + } + ) + ) + ]; + }; +} diff --git a/modules/system/services/syncthing/backups.nix b/modules/system/services/syncthing/backups.nix index aef52da..28932ae 100644 --- a/modules/system/services/syncthing/backups.nix +++ b/modules/system/services/syncthing/backups.nix @@ -37,7 +37,7 @@ in ''; }; - custom.services.resticBackup.syncthing = { + custom.services.resticBackups.syncthing = { inherit user; healthchecks.enable = true;