about summary refs log tree commit diff stats
path: root/modules/by-name/ba/backup/module.nix
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/by-name/ba/backup/module.nix140
1 files changed, 102 insertions, 38 deletions
diff --git a/modules/by-name/ba/backup/module.nix b/modules/by-name/ba/backup/module.nix
index 3e07fbd1..8d7d6bbb 100644
--- a/modules/by-name/ba/backup/module.nix
+++ b/modules/by-name/ba/backup/module.nix
@@ -16,40 +16,66 @@
   cfg = config.soispha.services.backup;
 in {
   options.soispha.services.backup = {
-    enable = lib.mkEnableOption "backups via restic to a storagebox";
+    storagebox = {
+      enable = lib.mkEnableOption "remote backups";
+      user = lib.mkOption {
+        type = lib.types.str;
+        description = "The storagebox-user to use";
+        example = "u384702-sub2";
+      };
 
-    user = lib.mkOption {
-      type = lib.types.str;
-      description = "The storagebox-user to use";
-      example = "u384702-sub2";
-    };
-    privateSshKey = lib.mkOption {
-      type = lib.types.path;
-      description = "The age-encrypted ssh-key, passed to agenix";
+      sshKey = lib.mkOption {
+        type = lib.types.path;
+        description = "The age-encrypted ssh-key, passed to agenix";
+        default = ./secrets/storagebox/ssh_key.age;
+      };
+
+      repositoryPassword = lib.mkOption {
+        type = lib.types.path;
+        description = "The age-encrypted restic password, passed to agenix";
+        default = ./secrets/storagebox/repository_password.age;
+      };
     };
-    privatePassword = lib.mkOption {
-      type = lib.types.path;
-      description = "The age-encrypted restic password, passed to agenix";
+
+    local = {
+      enable = lib.mkEnableOption "local backups";
+
+      repositoryPassword = lib.mkOption {
+        type = lib.types.path;
+        description = "The age-encrypted restic password, passed to agenix";
+        default = ./secrets/local/repository_password.age;
+      };
+
+      backupDiskUuid = lib.mkOption {
+        example = lib.literalExpression "d1d20ae7-3d8a-44da-86da-677dbbb10c89";
+        description = "The UUID of the backup disk";
+      };
     };
   };
 
-  config = lib.mkIf cfg.enable {
+  config = {
     age.secrets = {
-      resticpass = {
-        file = cfg.privatePassword;
+      resticStorageboxSshKey = lib.mkIf cfg.storagebox.enable {
+        file = cfg.storagebox.sshKey;
+        mode = "0700";
+        owner = "root";
+        group = "root";
+      };
+      resticStorageboxRepositoryPassword = lib.mkIf cfg.storagebox.enable {
+        file = cfg.storagebox.repositoryPassword;
         mode = "0700";
         owner = "root";
         group = "root";
       };
-      resticssh = {
-        file = cfg.privateSshKey;
+      resticLocalRepositoryPassword = lib.mkIf cfg.local.enable {
+        file = cfg.local.repositoryPassword;
         mode = "0700";
         owner = "root";
         group = "root";
       };
     };
 
-    soispha.programs.ssh = {
+    soispha.programs.ssh = lib.mkIf cfg.storagebox.enable {
       enable = true;
       rootKnownHosts = {
         "[u459143-sub1.your-storagebox.de]:23" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIICf9svRenC/PLKIL9nk6K/pxQgoiFC41wTNvoIncOxs";
@@ -59,30 +85,68 @@ in {
     services.restic.backups = let
       snapshotDir = "/srv/last_snapshot";
       homeDir = "${snapshotDir}/home";
+
+      paths = [
+        snapshotDir
+      ];
+      exclude = [
+        "${homeDir}/soispha/.cache"
+      ];
+      extraBackupArgs = [
+        "--verbose=2"
+      ];
+
+      backupPrepareCommand = extra:
+      # bash
+        extra
+        + ''
+          [ -d "${snapshotDir}" ] && ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume delete "${snapshotDir}"
+
+          # -r := Make the snapshot read-only
+          ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume snapshot -r /srv "${snapshotDir}";
+        '';
     in {
-      storagebox = {
+      local = let
+        backupMountPoint = "/run/media/${cfg.local.backupDiskUuid}";
+      in
+        lib.mkIf cfg.local.enable {
+          inhibitsSleep = true;
+          initialize = true;
+
+          inherit paths exclude extraBackupArgs;
+
+          # TODO: We could maybe use systemd's built-in system for this mounting. <2025-05-01>
+          backupPrepareCommand =
+            backupPrepareCommand
+            # bash
+            ''
+              set -xeu
+              ${lib.getExe' pkgs.util-linux "mount"} --mkdir "/dev/disk/by-uuid/${cfg.local.backupDiskUuid}" "${backupMountPoint}"
+            '';
+          backupCleanupCommand =
+            # bash
+            ''
+              ${lib.getExe' pkgs.util-linux "umount"} "${backupMountPoint}"
+            '';
+
+          passwordFile = config.age.secrets.resticLocalRepositoryPassword.path;
+
+          repository = "${backupMountPoint}/restic-backup-data/";
+
+          # Start on demand.
+          timerConfig = null;
+        };
+
+      storagebox = lib.mkIf cfg.storagebox.enable {
+        inhibitsSleep = true;
         initialize = true;
-        backupPrepareCommand =
-          # bash
-          ''
-            [ -d "${snapshotDir}" ] && ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume delete "${snapshotDir}"
-
-            # -r := Make the snapshot read-only
-            ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume snapshot -r /srv "${snapshotDir}";
-          '';
-        paths = [
-          snapshotDir
-        ];
-        exclude = [
-          "${homeDir}/soispha/.cache"
-        ];
-        extraBackupArgs = [
-          "--verbose=2"
-        ];
 
-        passwordFile = config.age.secrets.resticpass.path;
+        inherit paths exclude extraBackupArgs;
+        backupPrepareCommand = backupPrepareCommand "";
+
+        passwordFile = config.age.secrets.resticStorageboxRepositoryPassword.path;
         extraOptions = [
-          "rclone.program='ssh -p 23 ${cfg.user}@${cfg.user}.your-storagebox.de -i ${config.age.secrets.resticssh.path} command_forced_on_remote'"
+          "rclone.program='ssh -p 23 ${cfg.storagebox.user}@${cfg.storagebox.user}.your-storagebox.de -i ${config.age.secrets.resticStorageboxSshKey.path} command_forced_on_remote'"
         ];
 
         # This setting is normally passed to rclone, but we force