about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-04-30 19:34:25 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-04-30 19:34:25 +0200
commit5f21796b4a048bb4da1e00d6abe645dbb7cc6082 (patch)
treea9f95f851667403ce02cd4b54f59e61f3b46ea0d
parentmodules/lf/commands/trash_*: Add required `gnused` dependency (diff)
downloadnixos-config-5f21796b4a048bb4da1e00d6abe645dbb7cc6082.zip
modules/backup: Port to storagebox
-rw-r--r--hosts/apzu/default.nix4
-rw-r--r--hosts/tiamat/default.nix4
-rw-r--r--modules/by-name/ba/backup/module.nix98
-rw-r--r--modules/common/default.nix6
-rw-r--r--modules/common/secrets/backup/privatePassword.age14
-rw-r--r--modules/common/secrets/backup/privateSshKey.age22
-rw-r--r--secrets.nix3
7 files changed, 113 insertions, 38 deletions
diff --git a/hosts/apzu/default.nix b/hosts/apzu/default.nix
index e700cb49..96dd99e1 100644
--- a/hosts/apzu/default.nix
+++ b/hosts/apzu/default.nix
@@ -18,10 +18,6 @@
   ];
 
   soispha = {
-    services.backup = {
-      # Apzu should be regularly synced with Tiamat, which performs updates.
-      enable = false;
-    };
     bluetooth.enable = true;
 
     laptop = {
diff --git a/hosts/tiamat/default.nix b/hosts/tiamat/default.nix
index a1796a86..794b8390 100644
--- a/hosts/tiamat/default.nix
+++ b/hosts/tiamat/default.nix
@@ -31,10 +31,6 @@
       systemName = "x86_64-linux";
     };
     services = {
-      backup = {
-        backupDiskUuid = "c06ce163-2955-4388-b212-dfec4448fcf4";
-        enable = true;
-      };
       unison.foreign.address = "apzu.fritz.box";
     };
     programs = {
diff --git a/modules/by-name/ba/backup/module.nix b/modules/by-name/ba/backup/module.nix
index 95abcb14..63186e91 100644
--- a/modules/by-name/ba/backup/module.nix
+++ b/modules/by-name/ba/backup/module.nix
@@ -9,50 +9,88 @@
 # If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   lib,
-  pkgs,
   config,
+  pkgs,
   ...
 }: let
-  backup-script = pkgs.writeShellScriptBin "backsnap" ''
-    set -xeu;
-
-    ${pkgs.util-linux}/bin/mount --mkdir "/dev/disk/by-uuid/${cfg.backupDiskUuid}" "/run/media/${cfg.backupDiskUuid}";
-    ${pkgs.snap-sync-forked}/bin/snap-sync-forked --UUID "${cfg.backupDiskUuid}" --noconfirm;
-    ${pkgs.util-linux}/bin/umount "/run/media/${cfg.backupDiskUuid}";
-  '';
-
   cfg = config.soispha.services.backup;
 in {
   options.soispha.services.backup = {
-    enable = lib.mkEnableOption "backups with my forked snap-sync";
-    backupDiskUuid = lib.mkOption {
+    enable = lib.mkEnableOption "backups via restic to a storagebox";
+
+    user = lib.mkOption {
       type = lib.types.str;
-      example = lib.literalExpression "d1d20ae7-3d8a-44da-86da-677dbbb10c89";
-      description = "The UUID of the backup disk";
+      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";
+    };
+    privatePassword = lib.mkOption {
+      type = lib.types.path;
+      description = "The age-encrypted restic password, passed to agenix";
     };
   };
 
   config = lib.mkIf cfg.enable {
-    systemd = {
-      services.backup = {
-        wantedBy = lib.mkForce [];
-        unitConfig = {
-          Description = "Backup the last snapshots of the persitent-storage subvolume.";
-        };
-        serviceConfig = {
-          Type = "oneshot";
-          ExecStart = "${backup-script}/bin/backsnap";
-        };
+    age.secrets = {
+      resticpass = {
+        file = cfg.privatePassword;
+        mode = "0700";
+        owner = "root";
+        group = "root";
       };
+      resticssh = {
+        file = cfg.privateSshKey;
+        mode = "0700";
+        owner = "root";
+        group = "root";
+      };
+    };
+
+    services.restic.backups = let
+      snapshotDir = "/srv/snapshots";
+      homeDir = "${snapshotDir}/home";
+    in {
+      storagebox = {
+        initialize = true;
+        backupPrepareCommand =
+          # bash
+          ''
+            [ -d /srv/snapshots/home ] && ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume delete /srv/snapshots/home;
+
+            # -r := Make the snapshot read-only
+            ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume snapshot -r /home /srv/snapshots/home;
+
+            [ -d /srv/snapshots/srv ] && ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume delete /srv/snapshots/srv;
+            ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume snapshot -r /srv /srv/snapshots/srv;
+          '';
+        paths = [
+          snapshotDir
+        ];
+        exclude = [
+          "${homeDir}/soispha/.cache"
+        ];
+        extraBackupArgs = [
+          "--verbose" # Spam log
+        ];
+
+        passwordFile = config.age.secrets.resticpass.path;
+        extraOptions = [
+          "rclone.program='ssh -p 23 ${cfg.user}@${cfg.user}.your-storagebox.de -i ${config.age.secrets.resticssh.path} command_forced_on_remote'"
+        ];
+
+        # This setting is normally passed to rclone, but we force
+        # the command on the remote.
+        # As such, the value does not matter and must only be parseable by restic.
+        repository = "rclone: ";
 
-      timers.backup = {
-        wantedBy = ["timers.target"];
-        unitConfig = {
-          Description = "Backup 15min after boot and every 8 hours";
-        };
         timerConfig = {
-          OnBootSec = "15min";
-          OnUnitActiveSec = "8h";
+          Requires = "network-online.target";
+          OnActiveSec = "30m";
+          OnUnitInactiveSec = "2h";
+          Persistent = true;
         };
       };
     };
diff --git a/modules/common/default.nix b/modules/common/default.nix
index 393979d1..893ffab5 100644
--- a/modules/common/default.nix
+++ b/modules/common/default.nix
@@ -45,6 +45,12 @@
         enable = true;
         user = "soispha";
       };
+      backup = {
+        enable = true;
+        user = "u459143-sub1";
+        privateSshKey = ./secrets/backup/privateSshKey.age;
+        privatePassword = ./secrets/backup/privatePassword.age;
+      };
       fwupd.enable = true;
       mpd = {
         enable = true;
diff --git a/modules/common/secrets/backup/privatePassword.age b/modules/common/secrets/backup/privatePassword.age
new file mode 100644
index 00000000..a2aa984a
--- /dev/null
+++ b/modules/common/secrets/backup/privatePassword.age
@@ -0,0 +1,14 @@
+-----BEGIN AGE ENCRYPTED FILE-----
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzbnJ5Z3NXRFRtdEdCNk1z
+NG9Cb2FUTEZ2U2hTdUptWGpneHd2RGxGQlY0ClhqcHlMSUZlLzBPZko4a2hKdnE1
+Q1hnc3ErWlJiSk1HVXFLcSswQlV0WTAKLT4gc3NoLWVkMjU1MTkgelpFb25nIG5I
+cFpMY21ZdU9TUHY5VksraG1xeFByQlM1WE1INEk0SWFJQzIvSXhZbXMKSWxiNlIr
+NjZ6bEgySUJGdnpoZTY2MmxxajJMaUVTM3RkTmhpdkxaRnNCQQotPiBzc2gtZWQy
+NTUxOSA3SGZGVXcgRGtLMHFORm9mUDUwZnBXM0djcFlxTExJbWVDeDJmbVVSRi9l
+QTFmekpROApZY3Z0Zlc1SUdCcllKQ01ZNmdFQkI3UnViVGxzNTJNUkhqc2hOejY3
+MndrCi0+IG1Hbi1ncmVhc2UgUDJJdgpGUmVHa1NPWGtzUmZmMXM3eWkxVnJkek1h
+MUxZeS9qMlArendXZ1ltdFgvMTBabTlPM2hzSjhuVXBQNktHKzc1ClF6bjZob09B
+Ci0tLSB3dEZpbnpVR0pPdGpFVmNyM0JzUGl6eGcreXpkTG9xQUc5S09ndUthaEpR
+CuHgXCIq/AUCuIQWepBRapAab6zczOwxEBpDv5R2kw2mw34UVRLuFs7hB1cHE79y
+7Fc=
+-----END AGE ENCRYPTED FILE-----
diff --git a/modules/common/secrets/backup/privateSshKey.age b/modules/common/secrets/backup/privateSshKey.age
new file mode 100644
index 00000000..a7f30c7c
--- /dev/null
+++ b/modules/common/secrets/backup/privateSshKey.age
@@ -0,0 +1,22 @@
+-----BEGIN AGE ENCRYPTED FILE-----
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJOTFaQVpnQ2VJN1pJbmdu
+STF2UTF1Y3lyU2p3OUJlcWRFWlg3Mm9vSEFFCjg4RklaZ0hqdWdZSlJ0dGx0ckJN
+eHE4R1QyTEJEVkZudnVQV2VpblBYTW8KLT4gc3NoLWVkMjU1MTkgelpFb25nIDR0
+Z2RwTXJGV1VYT2Y1OUcxbXpnOGNEbG9BWmFxeWUzZ1huNW1YQk5BM3cKK1hLNnYx
+VnNvRDZWM3Y4VUpCQTRtbHJOcDJLdTBOUXJ1ak52Mkp3TE9OcwotPiBzc2gtZWQy
+NTUxOSA3SGZGVXcgSHFSOXM1Vm1lY0ErRkZobndEU0xvQjVXbnhDbUlGNmE2dStY
+SEFqSk9TMAo2VU5UbW9rc29kendvcHNuU3hFQ2lBQWNjRUxUSE10RmtjbkdwWGlv
+cTRVCi0+IDwtZ3JlYXNlIHNPT3N1IHNkZzcoQzUgLUM3Ml1Tcgorb3FQcWlDSkJQ
+L1g4WisydkYxdnJMeDNISVVDTGlZdmUyRkV0TkFUdW4ycXZBSlF3UWM2R3FTYjRs
+ckEyaTg2ClVrbwotLS0gdHF5Q3BqV3h2TzEySCs4OFNIYmx1U0hsTWo1RDlTV3VC
+T1RmelZaWXh4YwoeRkYbirDGX9aZj6OVxZ8sVsm2Sz23ikcBhAdTIXKErM7eQcdp
+Y2nA5kUebPVznbSq/XSQNbqvCfSv9wVVTZWww7AqkrltjFS8yxQVMKB37IfnJsZI
+92OCjJKArxAbvS7ERKY51Cf9oY/VqGk5WqxExH5ZvdbL144UI8JzrxhDopVXBWNd
+fCrHirRAdlu3wgVILXV3vk1kmE69sspuV4cer5f/kOBKT1w09s2NFFHH4TO3cLTJ
+Obgi/HWYHBjqGSgwd4bDtNOW2UDE1LN3YeUhloutWhuX8yt+NFzZJtl+CBrRqLKA
+0CK+ZPKPWWKYPCNlzKT4vDF9BZeyCfoEj9dTXvxrNPEB6YYi16aQ8DhI8wxADl41
+ShrULlEmB8rfz7V+8TFmERKISBRSSH0p+XJi8e2DWh2dSco16rizmbFFNDdcYiGT
+WWbXYCFNzj+kNFI/vEtt9dEWmfXlgg01khnyoOuRLedtoCDSuF+8z7YYy6oYt/2h
+gmf4NHOUgyxK56c17JMQ3xT97d2odgqmwB+YZQ13IJCjt3M3pmSssD4sTgmA5alH
+vtUlci6dZdO801h5vOesbAJplXCEyw==
+-----END AGE ENCRYPTED FILE-----
diff --git a/secrets.nix b/secrets.nix
index 4757ced4..8b4a5545 100644
--- a/secrets.nix
+++ b/secrets.nix
@@ -17,6 +17,9 @@ in {
 
   "modules/by-name/ta/taskwarrior/secrets/sync_server_encryption_key.age".publicKeys = [soispha tiamat apzu];
 
+  "modules/common/secrets/backup/privateSshKey.age".publicKeys = [soispha tiamat apzu];
+  "modules/common/secrets/backup/privatePassword.age".publicKeys = [soispha tiamat apzu];
+
   "modules/by-name/se/serverphone/private_keys/ca.key".publicKeys = [soispha tiamat apzu];
   "modules/by-name/se/serverphone/private_keys/server.key".publicKeys = [soispha tiamat apzu];
 }