commit ae494a36a6467b28f55fd5c96c9cec0868a0332c
parent f8ce2d089d024d203d98195fd7b9333ef0b21b58
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 27 Jan 2022 18:10:21 +0100
parent f8ce2d089d024d203d98195fd7b9333ef0b21b58
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 27 Jan 2022 18:10:21 +0100
modules: add restic-backup module
2 files changed, 141 insertions(+), 0 deletions(-)
A
|
134
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/modules/default.nix b/modules/default.nix @@ -0,0 +1,7 @@ +{ + + imports = [ + ./restic-backups.nix + ]; + +}
diff --git a/modules/restic-backups.nix b/modules/restic-backups.nix @@ -0,0 +1,134 @@ +{ options, config, pkgs, lib, ... }: + +with lib; + +let + backups = config.restic-backups; + + backupOpts = { ... }: { + options = { + + user = mkOption { + type = types.str; + default = "root"; + }; + + passwordFile = mkOption { + type = types.str; + }; + + paths = mkOption { + type = with types; listOf str; + default = []; + }; + + postgresDatabases = mkOption { + type = with types; listOf str; + default = []; + }; + + timerConfig = mkOption { + type = types.attrs; + default = { + OnCalendar = "daily"; + RandomizedDelaySec = 300; + }; + }; + + }; + }; + + servers = [ "desastro.ctu.cx" "lollo.ctu.cx" ]; + +in { + + options.restic-backups = mkOption { + type = with types; attrsOf (submodule backupOpts); + default = {}; + }; + + config = mkIf (backups != {}) { + + systemd.services = mapAttrs' ( + name: backup: nameValuePair "restic-backup-${name}" { + restartIfChanged = false; + requires = [ "network.target" "local-fs.target" ]; + onFailure = [ "notify-failure@%i.service" ]; + + path = [ + pkgs.restic + ] ++ optionals (backup.postgresDatabases != []) [ + config.services.postgresql.package + pkgs.zstd + ]; + + serviceConfig = { + Type = "oneshot"; + User = backup.user; + RuntimeDirectory = "restic-backup-${name}"; + CacheDirectory = "restic-backup-${name}"; + CacheDirectoryMode = "0700"; + ReadWritePaths = backup.paths; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + Environment = "RESTIC_PASSWORD_FILE=/tmp/passwordFile"; + ExecStartPre = [ + ( + "!" + (pkgs.writeScript "privileged-pre-start" ('' + #!${pkgs.runtimeShell} + set -eu pipefail + + cp ${backup.passwordFile} /tmp/passwordFile; + cp /run/agenix/restic-server-lollo /tmp/lollo.ctu.cx; + cp /run/agenix/restic-server-desastro /tmp/desastro.ctu.cx; + + chown -R ${backup.user} /tmp + '')) + ) + ( + pkgs.writeScript "pre-start" ('' + #!${pkgs.runtimeShell} + set -eu pipefail + + '' + concatMapStringsSep "\n" (db: '' + echo "Dumping Postgres-database: ${db}" + mkdir -p /tmp/postgresDatabases + pg_dump ${db} | zstd --rsyncable > /tmp/postgresDatabases/${db}.sql.zst + [ $(du -b /tmp/postgresDatabases/${db}.sql.zst | cut -f1) -gt "50" ] || exit 1 + '') backup.postgresDatabases) + ) + ]; + }; + + script = '' + set -eu pipefail + export XDG_CACHE_HOME=/var/cache/restic-backup-${name} + + '' + concatMapStringsSep "\n\n" (server: '' + echo "Backing up to: ${server}" + + export RESTIC_REPOSITORY="rest:https://restic:$(cat /tmp/${server})@restic.${server}/${config.networking.hostName}-${name}" + + #create repo if it not exists + restic snapshots || restic init + + #backup files + restic backup ${concatStringsSep " " (backup.paths ++ optional (backup.postgresDatabases != []) "/tmp/postgresDatabases") } + + restic check + '') servers; + + } + ) backups; + + systemd.timers = mapAttrs' ( + name: backup: nameValuePair "restic-backup-${name}" { + wantedBy = [ "timers.target" ]; + timerConfig = backup.timerConfig; + } + ) backups; + + }; + +}