From e6aab76104c66abfdb6b40ad2bedaf1a1b5289a8 Mon Sep 17 00:00:00 2001
From: albert <albert@sysctl.io>
Date: Thu, 16 Jan 2025 08:20:58 -0800
Subject: [PATCH] Remove btrbk, add btrfs-backup and use btrbk ssh key for it

---
 nixos/common/services/btrbk.nix               | 63 -------------------
 nixos/common/services/snapper.nix             | 24 ++++++-
 nixos/common/software/cli/scripts.nix         |  9 +--
 .../software/cli/scripts/btrfs-backup.sh      | 22 +++----
 nixos/hosts/nuc-server/disks.nix              | 27 +++-----
 secrets/secrets.yaml                          |  9 ++-
 6 files changed, 45 insertions(+), 109 deletions(-)
 delete mode 100644 nixos/common/services/btrbk.nix

diff --git a/nixos/common/services/btrbk.nix b/nixos/common/services/btrbk.nix
deleted file mode 100644
index cfaa0c92..00000000
--- a/nixos/common/services/btrbk.nix
+++ /dev/null
@@ -1,63 +0,0 @@
-{ hostname, pkgs, ... }: {
-
-  sops.secrets."btrbk/ssh_key" = {
-    sopsFile = ../../../secrets/secrets.yaml;
-    owner = "btrbk";
-    group = "btrbk";
-  };
-
-  security.sudo = {
-    enable = true;
-    extraRules = [
-      {
-        commands = [
-          {
-            command = "${pkgs.coreutils-full}/bin/test";
-            options = [ "NOPASSWD" ];
-          }
-          {
-            command = "${pkgs.coreutils-full}/bin/readlink";
-            options = [ "NOPASSWD" ];
-          }
-          {
-            command = "${pkgs.btrfs-progs}/bin/btrfs";
-            options = [ "NOPASSWD" ];
-          }
-        ];
-        users = [ "btrbk" ];
-      }
-    ];
-  };
-
-  # Ensure the btrbk snapshot folders are created since btrbk won't do it automatically:
-  systemd.tmpfiles.rules = [
-    "d /.snapshots/btrbk     0755 btrbk btrbk"
-    "d /nix/.snapshots/btrbk 0755 btrbk btrbk"
-  ];
-
-  # More info:  https://github.com/digint/btrbk/blob/master/btrbk.conf.example
-  # More info:  https://digint.ch/btrbk/doc/btrbk.conf.5.html#_btrfs_specific_options
-  services.btrbk = {
-    instances."synology" = {
-      onCalendar = "daily"; 
-      settings = {
-        snapshot_create = "ondemand";
-        incremental_resolve = "directory";
-        snapshot_preserve_min = "7d";
-        snapshot_preserve = "7d 4w 6m";
-        target_preserve_min = "7d";
-        target_preserve   = "7d 4w 6m";
-        ssh_identity = "/run/secrets/btrbk/ssh_key";
-        ssh_user = "root"; 
-        stream_compress = "gzip";
-        volume."/" = {
-          target = "ssh://synology/volume1/btrbk/hosts/${hostname}";
-          subvolume = {
-            "/"    = { snapshot_dir = "/.snapshots/btrbk";     };
-            "/nix" = { snapshot_dir = "/nix/.snapshots/btrbk"; };
-          };
-        };
-      };
-    };
-  };
-}
diff --git a/nixos/common/services/snapper.nix b/nixos/common/services/snapper.nix
index 56bd4f78..3d48f6bf 100644
--- a/nixos/common/services/snapper.nix
+++ b/nixos/common/services/snapper.nix
@@ -1,5 +1,22 @@
-{ lib, pkgs, desktop, ... }: {
-    environment.systemPackages = [ pkgs.snapper ]  ++ lib.optional (builtins.isString desktop) pkgs.btrfs-assistant;
+{ lib, pkgs, desktop, ... }: 
+let
+  btrfs-backup = pkgs.writeScriptBin "btrfs-backup" "${builtins.readFile ../software/cli/scripts/btrfs-backup.sh}";
+in {
+  environment.systemPackages = [ 
+    pkgs.snapper 
+    btrfs-backup
+  ]  ++ lib.optional (builtins.isString desktop) pkgs.btrfs-assistant;
+    
+    # SSH key for btrfs-backups
+    sops.secrets."btrfs-backups/ssh_key" = {
+      sopsFile = ../../../../secrets/secrets.yaml;
+      owner = "root";
+    };
+
+    sops.secrets."btrfs-backups/gotify_token" = {
+      owner = "root";
+      sopsFile = ../../../../secrets/secrets.yaml;
+    };
 
     services.snapper = {
         snapshotRootOnBoot = true;
@@ -8,7 +25,8 @@
         configs = {
             root = {
                 TIMELINE_CREATE = true;
-                TIMELINE_CLEANUP = true;
+        TIMELINE_CLEANUP = true;
+                NUMBER_LIMIT             = 10; # snapshotRootOnBoot cleanup
                 TIMELINE_LIMIT_YEARLY    = 0;
                 TIMELINE_LIMIT_QUARTERLY = 2;
                 TIMELINE_LIMIT_MONTHLY   = 3;
diff --git a/nixos/common/software/cli/scripts.nix b/nixos/common/software/cli/scripts.nix
index aee0fa9e..8de0f93e 100644
--- a/nixos/common/software/cli/scripts.nix
+++ b/nixos/common/software/cli/scripts.nix
@@ -1,16 +1,9 @@
 { pkgs, ... }:
 let
-  clean-hm     = pkgs.writeScriptBin "clean-hm"     "${builtins.readFile ./scripts/clean-hm.sh}";
-  btrfs-backup = pkgs.writeScriptBin "btrfs-backup" "${builtins.readFile ./scripts/btrfs-backup.sh}";
+  clean-hm = pkgs.writeScriptBin "clean-hm" "${builtins.readFile ./scripts/clean-hm.sh}";
 in {
   environment.systemPackages = [ 
     clean-hm
-    btrfs-backup
   ];  
-
-  sops.secrets."btrfs-backups/gotify_token" = {
-    owner = "albert";
-    sopsFile = ../../../../secrets/secrets.yaml;
-  };
 }
 
diff --git a/nixos/common/software/cli/scripts/btrfs-backup.sh b/nixos/common/software/cli/scripts/btrfs-backup.sh
index d2872d30..50162a98 100644
--- a/nixos/common/software/cli/scripts/btrfs-backup.sh
+++ b/nixos/common/software/cli/scripts/btrfs-backup.sh
@@ -13,7 +13,7 @@
 # - snapper
 # - btrfs-progs
 # - pv
-# - ssh (configured for remote access)
+# - ssh configured for remote access)
 # - curl (for Gotify notifications)
 #
 # Usage: ./script.sh SNAPPER_CONFIG
@@ -26,7 +26,7 @@ set -eE
 if ! command -v snapper >/dev/null 2>&1 || \
    ! command -v btrfs >/dev/null 2>&1 || \
    ! command -v pv >/dev/null 2>&1 || \
-   ! command -v ssh >/dev/null 2>&1 || \
+   ! command -v ssh  >/dev/null 2>&1 || \
    ! command -v curl >/dev/null 2>&1; then
     echo "ERROR: Missing required dependencies. Please ensure snapper, btrfs-progs, pv, ssh, and curl are installed."
     exit 1
@@ -224,7 +224,7 @@ verify_snapshot() {
 
 # Function to verify remote connectivity
 verify_remote() {
-    if ! ssh -q "$REMOTE_HOST" "exit"; then
+    if ! ssh -i /run/secrets/btrfs-backup/ssh_key -q "$REMOTE_HOST" "exit"; then
         send_notification "Backup Failed" "Cannot connect to remote host from $HOSTNME" 8
         log "ERROR: Cannot connect to remote host"
         exit 1
@@ -233,7 +233,7 @@ verify_remote() {
 
 # Function to get remote snapshots
 get_remote_snapshots() {
-    ssh "$REMOTE_HOST" "find '$BASE_DEST_PATH' -maxdepth 1 -type d -name '[0-9]*' | sort -n"
+    ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "find '$BASE_DEST_PATH' -maxdepth 1 -type d -name '[0-9]*' | sort -n"
 }
 
 # Function to get local backup snapshots
@@ -255,13 +255,13 @@ cleanup_snapshots() {
             local snapshot="${local_snapshots[$i]}"
 
             # Delete remote snapshot first
-            if ssh "$REMOTE_HOST" "[ -d '$BASE_DEST_PATH/$snapshot' ]"; then
+            if ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "[ -d '$BASE_DEST_PATH/$snapshot' ]"; then
                 log "Deleting remote snapshot: $snapshot"
-                if ! ssh "$REMOTE_HOST" "btrfs subvolume delete '$BASE_DEST_PATH/$snapshot/snapshot'"; then
+                if ! ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "btrfs subvolume delete '$BASE_DEST_PATH/$snapshot/snapshot'"; then
                     send_notification "Backup Warning" "Failed to delete remote snapshot $snapshot on $HOSTNME" 6
                     log "WARNING - Failed to delete remote snapshot $snapshot"
                 fi
-                if ! ssh "$REMOTE_HOST" "rm -rf '$BASE_DEST_PATH/$snapshot'"; then
+                if ! ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "rm -rf '$BASE_DEST_PATH/$snapshot'"; then
                     send_notification "Backup Warning" "Failed to cleanup remote snapshot directory $snapshot on $HOSTNME" 6
                     log "WARNING - Failed to cleanup remote snapshot directory $snapshot"
                 fi
@@ -292,7 +292,7 @@ if [ -n "$LAST_TRANSFERRED" ]; then
 fi
 
 # Create destination directory if it doesn't exist
-ssh "$REMOTE_HOST" "mkdir -p '$DEST_PATH'"
+ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "mkdir -p '$DEST_PATH'"
 
 # Perform the transfer
 if [ -z "$LAST_TRANSFERRED" ]; then
@@ -300,7 +300,7 @@ if [ -z "$LAST_TRANSFERRED" ]; then
     log "Performing full send of snapshot $NEW_SNAPSHOT"
     sudo btrfs send "$SNAPSHOT_PATH/$NEW_SNAPSHOT/snapshot" | \
         pv --bytes | \
-        ssh "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && {
+        ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && {
             echo "$NEW_SNAPSHOT" | sudo tee "$STATE_FILE"
             log "Full send completed successfully"
         } || {
@@ -315,7 +315,7 @@ else
     sudo btrfs send -p "$SNAPSHOT_PATH/$LAST_TRANSFERRED/snapshot" \
         "$SNAPSHOT_PATH/$NEW_SNAPSHOT/snapshot" | \
         pv --bytes | \
-        ssh "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && {
+        ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && {
             echo "$NEW_SNAPSHOT" | sudo tee "$STATE_FILE"
             log "Incremental send completed successfully"
         } || {
@@ -348,7 +348,7 @@ if [ -n "$LAST_TRANSFERRED" ]; then
 fi
 
 # Final verification
-if ! ssh "$REMOTE_HOST" "btrfs subvolume show '$DEST_PATH/snapshot'" &>/dev/null; then
+if ! ssh -i /run/secrets/btrfs-backup/ssh_key "$REMOTE_HOST" "btrfs subvolume show '$DEST_PATH/snapshot'" &>/dev/null; then
     send_notification "Backup Failed" "Final verification failed for $SNAPPER_CONFIG on $HOSTNME" 8
     log "WARNING - Final verification failed"
     exit 1
diff --git a/nixos/hosts/nuc-server/disks.nix b/nixos/hosts/nuc-server/disks.nix
index 2c873913..659f7350 100644
--- a/nixos/hosts/nuc-server/disks.nix
+++ b/nixos/hosts/nuc-server/disks.nix
@@ -1,27 +1,16 @@
 {
   imports = [ 
     ../../common/services/snapper.nix
-    ../../common/services/btrbk.nix
   ];
 
-  # Ensure the btrbk snapshot folders are created since btrbk won't do it automatically:
-  systemd.tmpfiles.rules = [
-    "d /home/.snapshots/btrbk          0755 btrbk btrbk"
-    "d /Local-Storage/.snapshots/btrbk 0755 btrbk btrbk"
-  ];
-  services.btrbk.instances."synology".settings.volume."/".subvolume = {
-    "/home"          = { snapshot_dir = "/home/.snapshots/btrbk"; };
-    "/Local-Storage" = { snapshot_dir = "/Local-Storage/.snapshots/btrbk"; };
-  };
-  
-  # services.cron = {
-  #   systemCronJobs = [
-  #     "0 0 * * * root btrfs-backup root"
-  #     "0 0 * * * root btrfs-backup nix"
-  #     "0 0 * * * root btrfs-backup home"
-  #     "0 0 * * * root btrfs-backup Local-Storage"
-  #   ];
-  #  };
+  services.cron = {
+    systemCronJobs = [
+      "0 0 * * * root btrfs-backup root"
+      "0 0 * * * root btrfs-backup nix"
+      "0 0 * * * root btrfs-backup home"
+      "0 0 * * * root btrfs-backup Local-Storage"
+    ];
+   };
 
   # extra configs not present in the standard config above
   services.snapper.configs.Local-Storage = {
diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml
index d240832f..a3db0f8a 100644
--- a/secrets/secrets.yaml
+++ b/secrets/secrets.yaml
@@ -4,17 +4,16 @@ services:
     forgejo_token: ENC[AES256_GCM,data:vAH8v82+WI/P0HhtLDfrK66B3u2H49XA1AglfL1LthM6Dm+znBlx4QaFmNk3ag==,iv:/jqtUejqNC9f9kXdUqxl1+LaxKsjXSZdU+I0u+ssmdQ=,tag:+2oWh6sgc7R1PXYxIz3oVQ==,type:str]
 btrfs-backups:
     gotify_token: ENC[AES256_GCM,data:PP8UTJWrDKhonLxN8vEj,iv:hTGWyktK+Ce7hAd0bARztLAQDSvhWgLcKRyGqyfgVKU=,tag:2xboM6Uv8NWld89EUl2jEg==,type:str]
-btrbk:
-    ssh_key: ENC[AES256_GCM,data:HxT85XlROSSKqPOEToSmrpzc6cutWRDkLxIO8o13AENtAlqEfBtcVTe/XKKbUjx+38FkqznVuHgVxXtnWLfON0yCx2PqoWVsluPfW44QFswQdtM0dYYCFy1hK7pg0xSplIeYdNZC9VFhL+SoBYXfTHKpalPo5LwYVrtQnO9yV/PrYw07oYTEKWGVxpi0KJJ2vo4UwxONzEJJSjJU/M0haQ1mvgTU5kFS5e4cMZOy3cGpGSUOPlUDlQgUlANcK92HFZlFnRyd2r+pjYVkSeixfQ9Zq30nNoaSu1J2VZhkt1KTfAXnuGIjlByvSVE9ZF78rlZfTBmZzJEyB3N3rtWEWUOrLZx8ZCh1YIpp6VK2WNJKtW2SpCpkG0fCoKzJiBvf+GuV4P0vdBHo6/xt12CHx498XUZSP644b1vnUim+RRmDQkVnTJnjY4YilIUME8MEFMzNuGamX/IX+U+UDGYwE+/7rc1BMZyJdzQka272uj66Q+fE7f/wzgtVbtgBBkIi3pDfv3smfFU8xGQoNmGJWIy1edvDJpoxsIaZ,iv:SvcLu1ffduCYj6tEfj6cSZM9CSB9TbGXWz4CixXYnsA=,tag:enn3+zPZbDl7IfivWv82Fg==,type:str]
-    ssh_pub_key: ENC[AES256_GCM,data:ARuXIOnW2x2NceekoRPG/Vy+zj/RdryE9m4MYt6e1NFGOgC2k69ZAmBui0+aIly2kJP4VyKJ/cf70APuv4uBmahAClFFdj5ZNrQnINiQzJF6iWOiaRI=,iv:+TqQ1WWaVRy/Ec8N3WUxSlkfU2me2ncII8smq6RsJr8=,tag:G4+/js93EFWudg9cRigxog==,type:str]
+    ssh_key: ENC[AES256_GCM,data:Nri7+3ZAPIEkYwjeyW0uQCycIxVx+ebOg2zHFHvt5MjootsIBXsQhBiGupP2/0ZQ2Val8VgX0wFnMG89GCCpOwiYe8vVaIeLmLOCAAchu16ftNqSN9zNv0aLPZ/lJhYdtnq79hXA6jbrwYONPaVU/5fqbl3+kWhEJtgjhB+eH0O9sLUAS8s66scov7yYHwYH7cWvJUiOomwCx0a6th2MzU2+PDvFCrr1hqQyS3CJdMrLTAHkg0F74pvmVFQB7Z1m2KWPJkaX1ji3/kyjrOKbq++GF6L7TwH03vMXtrHqExF8KTGfmelhlmlJ0UzaoOTZ9SG+0xIEwb7SjY4DNoUO2rZC3A+vn4g/RsYKhXAHEeRFgkAmcWZR65gbEliN7MMFcpwMkIFWEgM4zRmC4ZmGs3zxSxsQyHKqBtL4cRrlLmwXCwPjv4TbiV2BmlCNgahh+WtVTQnx/GQcaGZ9MT8lhRC7dSy0HS9Efc8uUz8e5aAER8F7z1MhemP92Ma3YsNyEUxcHU6Ruk7NUbgqMIxGbGiqXzW3DSMc6L0+,iv:5zjYDsZrg5hekMl58p0o1znrojke4u1LzUY3sJpInkE=,tag:WlNyfgWGSD2Xf7ualoefxA==,type:str]
+    ssh_pub_key: ENC[AES256_GCM,data:DO9KRWiB5yT3eY28P4QPVV4tho5CpaXEF2hHXubK8iAX/tlxzzztGAEGRo572BXP5T+FYB6jO12chLDNWoYDQ92yrdZGy5k5vPZSehTm6t3005Q0CF7JfeO0bX+biQ==,iv:VP2wx3ZD7RE1zDDRthv2329GXemmMKYl7te69KNGY0s=,tag:78QSISotcsMmi3leNcP5sQ==,type:str]
 sops:
     kms: []
     gcp_kms: []
     azure_kv: []
     hc_vault: []
     age: []
-    lastmodified: "2025-01-16T00:34:44Z"
-    mac: ENC[AES256_GCM,data:oG1TahgiYBXqDV9oftlBfhSlUIDwkyPMQncoxq9+443ktXZ4Ze+FpYw3FH4NAR4n59CWfzrwUYWY+YYoRUIUzNrOloygy2z/FAdnyYXs3CDt++4Gilz74P9f5THloXgKF5LMSIyYh091zrEV25nSqdSHQZnXKQOQJAAsitMcAdU=,iv:k7SJ9bnC1eiOSkomIrB1Ou0VoUfRcYI3m0GUKTGATQw=,tag:wfjGh3q7X0EXbx4o37syLQ==,type:str]
+    lastmodified: "2025-01-16T16:08:57Z"
+    mac: ENC[AES256_GCM,data:by/kjCDwQFWkBJPDB/W44w+8VfIm68fGHWuQOpzrnrM4GFfqO4lSbd/Mzy6aZ9sWSkoNO3h4l3lx3RUvpc4nh1JWqfhoKjds9HIqMKcAwFdUtRCZeMc+g5nc5/BLL27duM2F0YaPFNLs2u2hBgMcRME+j80jQ4r7rMhOPIb+DuA=,iv:sYsORoOuvNW/0o4f6TjQe8vGuOQRW02T8ZAO5FApszg=,tag:0PN9MraA+Ozv1ozy7l+Fhw==,type:str]
     pgp:
         - created_at: "2025-01-10T20:21:25Z"
           enc: |-