From ff6fa1c1f38f8b55e0f458604d7409339222cd3b Mon Sep 17 00:00:00 2001 From: Alejandro Angulo Date: Thu, 3 Aug 2023 17:21:58 -0700 Subject: [PATCH] Added syncoid service Sets up ZFS replication. Syncoid modules handles configuration for both the remote and the target but I'm thinking I should split this up in the future. --- modules/services/syncoid/default.nix | 80 +++++++++++++++++++++++++ modules/system/zfs/default.nix | 7 ++- systems/x86_64-linux/gospel/default.nix | 15 +++++ systems/x86_64-linux/node/default.nix | 7 +++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 modules/services/syncoid/default.nix diff --git a/modules/services/syncoid/default.nix b/modules/services/syncoid/default.nix new file mode 100644 index 0000000..f7651df --- /dev/null +++ b/modules/services/syncoid/default.nix @@ -0,0 +1,80 @@ +{ + options, + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.aa.services.syncoid; +in { + options.aa.services.syncoid = with types; { + enable = mkEnableOption "syncoid (ZFS snap replication)"; + commands = mkOption { + type = attrs; + default = {}; + description = "Commands to pass directly to syncoid, see `services.syncoid.commands`"; + }; + remoteTargetUser = mkOption { + type = str; + default = ""; + description = "The user to use on the target machine."; + }; + remoteTargetDatasets = mkOption { + type = listOf str; + default = []; + description = "Datasets to be used as a remote target (e.g. a NAS's backups dataset)"; + }; + remoteTargetPublicKeys = mkOption { + type = listOf str; + default = []; + description = "SSH public keys that the syncoid service's user should trust"; + }; + }; + + config = mkIf cfg.enable { + services.syncoid = { + enable = true; + commands = mkAliasDefinitions options.aa.services.syncoid.commands; + }; + + environment.systemPackages = mkIf (cfg.remoteTargetUser != "") [ + pkgs.lzop + pkgs.mbuffer + ]; + + users = mkIf (cfg.remoteTargetUser != "") { + users."${cfg.remoteTargetUser}" = { + shell = pkgs.bashInteractive; + group = cfg.remoteTargetUser; + isSystemUser = true; + home = "/var/lib/${cfg.remoteTargetUser}"; + createHome = true; + openssh.authorizedKeys.keys = cfg.remoteTargetPublicKeys; + }; + groups."${cfg.remoteTargetUser}" = {}; + }; + + systemd.services.setup-syncoid-remote = { + description = "Permission setup for syncoid remote targets"; + documentation = ["https://github.com/jimsalterjrs/sanoid/wiki/Syncoid#running-without-root"]; + wantedBy = ["multi-user.target"]; + path = [pkgs.zfs]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + }; + + script = '' + DATASETS=(${toString cfg.remoteTargetDatasets}) + for dataset in "''${DATASETS[@]}"; do + zfs allow \ + -u ${cfg.remoteTargetUser} \ + compression,mountpoint,create,mount,receive,rollback,destroy \ + "$dataset" + done + ''; + }; + }; +} diff --git a/modules/system/zfs/default.nix b/modules/system/zfs/default.nix index 7e85fe4..69cfeee 100644 --- a/modules/system/zfs/default.nix +++ b/modules/system/zfs/default.nix @@ -16,7 +16,12 @@ in { config = mkIf cfg.enable { services.zfs = { autoScrub.enable = true; - autoSnapshot.enable = true; + # Still need to set `com.sun:auto-snapshot` to `true` on datasets + # zfs set com.sun:auto-snapshot=true pool/dataset + autoSnapshot = { + enable = true; + flags = "-k -p --utc"; + }; }; }; } diff --git a/systems/x86_64-linux/gospel/default.nix b/systems/x86_64-linux/gospel/default.nix index 542d0fa..8e49750 100644 --- a/systems/x86_64-linux/gospel/default.nix +++ b/systems/x86_64-linux/gospel/default.nix @@ -41,6 +41,21 @@ configureClientRouting = true; configureServerRouting = true; }; + services.syncoid = { + enable = true; + commands = { + "bpool" = { + target = "backups@192.168.113.13:tank/backups/gospel/bpool"; + recursive = true; + sshKey = "/var/lib/syncoid/.ssh/id_ed25519"; + }; + "rpool" = { + target = "backups@192.168.113.13:tank/backups/gospel/rpool"; + recursive = true; + sshKey = "/var/lib/syncoid/.ssh/id_ed25519"; + }; + }; + }; hardware.audio.enable = true; hardware.bluetooth.enable = true; diff --git a/systems/x86_64-linux/node/default.nix b/systems/x86_64-linux/node/default.nix index 11dd534..9a8a9a0 100644 --- a/systems/x86_64-linux/node/default.nix +++ b/systems/x86_64-linux/node/default.nix @@ -28,6 +28,13 @@ enable = true; acmeCertName = "kilonull.com"; }; + services.syncoid = { + # sudo -u backups zfs create -o mountpoint=/tank/backups/gospel tank/backups/gospel + enable = true; + remoteTargetUser = "backups"; + remoteTargetDatasets = ["tank/backups"]; + remoteTargetPublicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAhA+9O2OBMDH1Xnj6isu36df5TOdZG8aEA4JpN2K60e syncoid@gospel"]; + }; security.acme = { enable = true;