From 05a2742a91260ae084cdf52ce9c31775e55b0a53 Mon Sep 17 00:00:00 2001 From: clerie Date: Tue, 16 May 2023 12:21:11 +0200 Subject: [PATCH] modules/backup: add backup module using restic --- configuration/common/default.nix | 7 ++ hosts/minecraft-2/configuration.nix | 17 ++-- modules/backup/default.nix | 120 ++++++++++++++++++++++++++++ modules/default.nix | 1 + 4 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 modules/backup/default.nix diff --git a/configuration/common/default.nix b/configuration/common/default.nix index edb9f73..0e25af1 100644 --- a/configuration/common/default.nix +++ b/configuration/common/default.nix @@ -117,6 +117,13 @@ clerie.nixfiles.enable = true; + clerie.backup = { + targets = { + cyan.serverName = "cyan.backup.clerie.de"; + magenta.serverName = "magenta.backup.clerie.de"; + }; + }; + nixpkgs.overlays = [ (import ../../pkgs/overlay.nix) ]; diff --git a/hosts/minecraft-2/configuration.nix b/hosts/minecraft-2/configuration.nix index 7f67733..805e088 100644 --- a/hosts/minecraft-2/configuration.nix +++ b/hosts/minecraft-2/configuration.nix @@ -51,15 +51,14 @@ in { }; }; - services.restic.backups = { - main = { - paths = [ - "/var/src" - "/var/lib" - ]; - initialize = true; - passwordFile = "/var/src/secrets/restic/main"; - repositoryFile = "/var/src/secrets/restic/repository-cyan"; + clerie.backup = { + enable = true; + jobs = { + main = { + paths = [ + "/var/lib" + ]; + }; }; }; diff --git a/modules/backup/default.nix b/modules/backup/default.nix new file mode 100644 index 0000000..0b45cac --- /dev/null +++ b/modules/backup/default.nix @@ -0,0 +1,120 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.clerie.backup; + + jobTargetPairs = flatten ( + mapAttrsToList (jobName: jobOptions: + let + jobTargets = if jobOptions.targets == null then cfg.targets + else + filterAttrs (targetName: targetOptions: + any (map (jobOptionTarget: jobOptionTarget == targetName) jobOptions.targets) + ) cfg.targets; + in mapAttrsToList (targetName: targetOptions: + { + inherit jobName jobOptions targetName targetOptions; + } + ) jobTargets + ) cfg.jobs + ); + + backupServiceUnits = listToAttrs (map ({jobName, jobOptions, targetName, targetOptions}: let + jobPasswordFile = if jobOptions.passwordFile == null then config.age.secrets."clerie-backup-job-${jobName}".path else jobOptions.passwordFile; + repoPath = if jobOptions.repoPath == null then "/${config.networking.hostName}/${jobName}" else jobOptions.repoPath; + targetPasswordFile = if targetOptions.passwordFile == null then config.age.secrets."clerie-backup-target-${targetName}".path else targetOptions.passwordFile; + targetUsername = if targetOptions.username == null then config.networking.hostName else targetOptions.username; + in + nameValuePair "clerie-backup-${jobName}-${targetName}" { + requires = [ "network.target" "local-fs.target" ]; + path = [ pkgs.restic ]; + + serviceConfig = { + Type = "oneshot"; + }; + + script = '' + set -euo pipefail + + export RESTIC_PASSWORD_FILE=${jobPasswordFile} + export RESTIC_REPOSITORY="rest:https://${targetUsername}:$(cat ${targetPasswordFile})@${targetOptions.serverName}${repoPath}" + + restic snapshots || restic init + + restic backup ${escapeShellArgs jobOptions.paths} + + restic check + ''; + } + ) jobTargetPairs); + + backupServiceTimers = listToAttrs (map ({jobName, jobOptions, targetName, targetOptions}: + nameValuePair "clerie-backup-${jobName}-${targetName}" { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "hourly"; + RandomizedDelaySec = "1h"; + }; + after = [ "network-online.target" ]; + } + ) jobTargetPairs); + + targetOptions = { ... }: { + options = { + passwordFile = mkOption { + type = with types; nullOr str; + default = null; + }; + username = mkOption { + type = with types; nullOr str; + default = null; + }; + serverName = mkOption { + type = types.str; + }; + }; + }; + + jobOptions = { ... }: { + options = { + passwordFile = mkOption { + type = with types; nullOr str; + default = null; + }; + repoPath = mkOption { + type = with types; nullOr str; + default = null; + }; + targets = mkOption { + type = with types; nullOr (listOf str); + default = null; + }; + paths = mkOption { + type = with types; listOf str; + }; + }; + }; +in + +{ + options = { + clerie.backup = { + enable = mkEnableOption "clerie's Backup"; + targets = mkOption { + type = with types; attrsOf (submodule targetOptions); + default = {}; + }; + jobs = mkOption { + type = with types; attrsOf (submodule jobOptions); + default = {}; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services = backupServiceUnits; + systemd.timers = backupServiceTimers; + }; +} diff --git a/modules/default.nix b/modules/default.nix index 7366779..374cc53 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -5,6 +5,7 @@ ./policyrouting ./akne ./anycast_healthchecker + ./backup ./clerie-firewall ./gre-tunnel ./minecraft-server