{ pkgs, lib, config, hostName, ... }: let inherit (config.users) users; backupTarget = "off4"; backupConf = conf: lib.concatMapAttrs (targetHost: c: { "${hostName}/root-to-${targetHost}" = lib.recursiveUpdate { source = "${hostName}/root"; target = "backup@${targetHost}:${backupTarget}/julm/backup/${hostName}"; sendOptions = "raw"; recursive = true; extraArgs = [ "--create-bookmark" "--no-sync-snap" "--no-privilege-elevation" "--preserve-properties" "--preserve-recordsize" "--recursive" "--sendoptions=w" "--recvoptions=u" "--exclude-datasets" "${hostName}/root/nix" "--exclude-datasets" "${hostName}/root/var/cache" "--exclude-datasets" "${hostName}/root/var/log" "--exclude-datasets" "${hostName}/root/home/julm/.cache" "--exclude-datasets" "${hostName}/root/home/julm/Downloads" "--sshconfig" "${pkgs.writeText "ssh-config" '' Host * Ciphers aes128-gcm@openssh.com Compression no StrictHostKeyChecking yes ''}" ]; } c; }) { "aubergine.local" = { }; "blackberry.local" = { }; "nan2gua1.local" = { }; }; in { services.avahi = { enable = true; openFirewall = true; publish = { enable = true; addresses = true; domain = true; hinfo = true; userServices = true; workstation = true; }; reflector = true; }; users.users.backup = { isSystemUser = true; shell = users.root.shell; group = config.users.groups.disk.name; openssh.authorizedKeys.keys = [ (lib.readFile ../pumpkin/syncoid/ssh.key.pub) (lib.readFile ../nan2gua1/syncoid/ssh.key.pub) ]; }; systemd.services."zfs-import@".serviceConfig.ExecStartPost = pkgs.writeShellScript "zfs-allow" '' set -eux pool="$1" case "$pool" in (off2) zfs allow -u ${users.backup.name} change-key,compression,create,destroy,mount,mountpoint,receive,rollback,userprop "$pool"/julm/backup;; (off4) zfs allow -u ${users.backup.name} change-key,compression,create,destroy,mount,mountpoint,receive,rollback,userprop "$pool"/julm/backup;; esac '' + " %I"; networking.nftables.ruleset = lib.mkAfter '' table inet filter { chain input-lan { tcp dport 22 counter accept comment "syncoid: SSH" } chain output-net { skuid @nixos_syncoid_uids \ meta l4proto tcp \ counter accept \ comment "syncoid: SSH" } } ''; systemd.tmpfiles.rules = [ "z /dev/zfs 0660 - ${config.users.groups."disk".name} -" ]; systemd.services."syncoid-${hostName}-root".serviceConfig = { # Explanation: give access to /var/run/avahi-daemon/socket # Using /var/run is not working due to RootDirectoryStartOnly=true BindReadOnlyPaths = [ "/var/run" ]; RootDirectoryStartOnly = lib.mkForce false; ExecStartPost = pkgs.writeShellScript "zfs-fix-bookmarks" '' set -ux for s in $(zfs list -Hrpt snapshot -o name ${hostName}/root); do zfs bookmark "$s" "''${s//@/#}" || true done ''; }; services.syncoid = { enable = true; interval = "*-*-* *:05:00"; #interval = "*:0/1"; sshKey = "ssh.key:${syncoid/ssh.key.cred}"; commonArgs = [ #"--debug" "--no-sync-snap" "--create-bookmark" #"--no-privilege-elevation" #"--no-stream" #"--preserve-recordsize" #"--preserve-properties" ]; service = { serviceConfig.Group = config.users.groups."disk".name; }; commands = { } // backupConf { }; }; programs.bash.interactiveShellInit = '' zfs-backup () { local - set -x dst= if ! zpool list ${backupTarget} then dst=aubergine.sp: fi sudo syncoid --sshkey ~julm/.ssh/id_ed25519 \ --create-bookmark --no-sync-snap --no-privilege-elevation \ --preserve-properties --preserve-recordsize \ --recursive --sendoptions=w --recvoptions=u \ --exclude-datasets ${hostName}/root/nix \ --exclude-datasets ${hostName}/root/var/cache \ --exclude-datasets ${hostName}/root/var/log \ --exclude-datasets ${hostName}/root/home/julm/.cache \ --exclude-datasets ${hostName}/root/home/julm/Downloads \ ${hostName}/root \ ''${dst}${backupTarget}/julm/backup/${hostName} zfs-fix-bookmarks ${hostName}/root 2>/dev/null } ''; }