Loading nixos/modules/services/backup/btrbk.nix +42 −15 Original line number Diff line number Diff line Loading @@ -47,7 +47,12 @@ let then [ "${name} ${value}" ] else concatLists (mapAttrsToList (genSection name) value); addDefaults = settings: { backend = "btrfs-progs-sudo"; } // settings; sudo_doas = if config.security.sudo.enable then "sudo" else if config.security.doas.enable then "doas" else throw "The btrbk nixos module needs either sudo or doas enabled in the configuration"; addDefaults = settings: { backend = "btrfs-progs-${sudo_doas}"; } // settings; mkConfigFile = name: settings: pkgs.writeTextFile { name = "btrbk-${name}.conf"; Loading Loading @@ -152,7 +157,8 @@ in }; config = mkIf (sshEnabled || serviceEnabled) { environment.systemPackages = [ pkgs.btrbk ] ++ cfg.extraPackages; security.sudo.extraRules = [ security.sudo = mkIf (sudo_doas == "sudo") { extraRules = [ { users = [ "btrbk" ]; commands = [ Loading @@ -166,6 +172,26 @@ in ]; } ]; }; security.doas = mkIf (sudo_doas == "doas") { extraRules = let doasCmdNoPass = cmd: { users = [ "btrbk" ]; cmd = cmd; noPass = true; }; in [ (doasCmdNoPass "${pkgs.btrfs-progs}/bin/btrfs") (doasCmdNoPass "${pkgs.coreutils}/bin/mkdir") (doasCmdNoPass "${pkgs.coreutils}/bin/readlink") # for ssh, they are not the same than the one hard coded in ${pkgs.btrbk} (doasCmdNoPass "/run/current-system/bin/btrfs") (doasCmdNoPass "/run/current-system/sw/bin/mkdir") (doasCmdNoPass "/run/current-system/sw/bin/readlink") # doas matches command, not binary (doasCmdNoPass "btrfs") (doasCmdNoPass "mkdir") (doasCmdNoPass "readlink") ]; }; users.users.btrbk = { isSystemUser = true; # ssh needs a home directory Loading @@ -183,8 +209,9 @@ in "best-effort" = 2; "realtime" = 1; }.${cfg.ioSchedulingClass}; sudo_doas_flag = "--${sudo_doas}"; in ''command="${pkgs.util-linux}/bin/ionice -t -c ${toString ioniceClass} ${optionalString (cfg.niceness >= 1) "${pkgs.coreutils}/bin/nice -n ${toString cfg.niceness}"} ${pkgs.btrbk}/share/btrbk/scripts/ssh_filter_btrbk.sh --sudo ${options}" ${v.key}'' ''command="${pkgs.util-linux}/bin/ionice -t -c ${toString ioniceClass} ${optionalString (cfg.niceness >= 1) "${pkgs.coreutils}/bin/nice -n ${toString cfg.niceness}"} ${pkgs.btrbk}/share/btrbk/scripts/ssh_filter_btrbk.sh ${sudo_doas_flag} ${options}" ${v.key}'' ) cfg.sshAccess; }; Loading nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ in { breitbandmessung = handleTest ./breitbandmessung.nix {}; brscan5 = handleTest ./brscan5.nix {}; btrbk = handleTest ./btrbk.nix {}; btrbk-doas = handleTest ./btrbk-doas.nix {}; btrbk-no-timer = handleTest ./btrbk-no-timer.nix {}; btrbk-section-order = handleTest ./btrbk-section-order.nix {}; buildbot = handleTest ./buildbot.nix {}; Loading nixos/tests/btrbk-doas.nix 0 → 100644 +114 −0 Original line number Diff line number Diff line import ./make-test-python.nix ({ pkgs, ... }: let privateKey = '' -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg 9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ= -----END OPENSSH PRIVATE KEY----- ''; publicKey = '' ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv ''; in { name = "btrbk-doas"; meta = with pkgs.lib; { maintainers = with maintainers; [ symphorien tu-maurice ]; }; nodes = { archive = { ... }: { security.sudo.enable = false; security.doas.enable = true; environment.systemPackages = with pkgs; [ btrfs-progs ]; # note: this makes the privateKey world readable. # don't do it with real ssh keys. environment.etc."btrbk_key".text = privateKey; services.btrbk = { extraPackages = [ pkgs.lz4 ]; instances = { remote = { onCalendar = "minutely"; settings = { ssh_identity = "/etc/btrbk_key"; ssh_user = "btrbk"; stream_compress = "lz4"; volume = { "ssh://main/mnt" = { target = "/mnt"; snapshot_dir = "btrbk/remote"; subvolume = "to_backup"; }; }; }; }; }; }; }; main = { ... }: { security.sudo.enable = false; security.doas.enable = true; environment.systemPackages = with pkgs; [ btrfs-progs ]; services.openssh = { enable = true; passwordAuthentication = false; kbdInteractiveAuthentication = false; }; services.btrbk = { extraPackages = [ pkgs.lz4 ]; sshAccess = [ { key = publicKey; roles = [ "source" "send" "info" "delete" ]; } ]; instances = { local = { onCalendar = "minutely"; settings = { volume = { "/mnt" = { snapshot_dir = "btrbk/local"; subvolume = "to_backup"; }; }; }; }; }; }; }; }; testScript = '' start_all() # create btrfs partition at /mnt for machine in (archive, main): machine.succeed("dd if=/dev/zero of=/data_fs bs=120M count=1") machine.succeed("mkfs.btrfs /data_fs") machine.succeed("mkdir /mnt") machine.succeed("mount /data_fs /mnt") # what to backup and where main.succeed("btrfs subvolume create /mnt/to_backup") main.succeed("mkdir -p /mnt/btrbk/{local,remote}") # check that local snapshots work with subtest("local"): main.succeed("echo foo > /mnt/to_backup/bar") main.wait_until_succeeds("cat /mnt/btrbk/local/*/bar | grep foo") main.succeed("echo bar > /mnt/to_backup/bar") main.succeed("cat /mnt/btrbk/local/*/bar | grep foo") # check that btrfs send/receive works and ssh access works with subtest("remote"): archive.wait_until_succeeds("cat /mnt/*/bar | grep bar") main.succeed("echo baz > /mnt/to_backup/bar") archive.succeed("cat /mnt/*/bar | grep bar") ''; }) pkgs/tools/backup/btrbk/default.nix +1 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ stdenv.mkDerivation rec { ''; passthru.tests = { inherit (nixosTests) btrbk btrbk-no-timer btrbk-section-order; inherit (nixosTests) btrbk btrbk-no-timer btrbk-section-order btrbk-doas; }; passthru.updateScript = genericUpdater { Loading Loading
nixos/modules/services/backup/btrbk.nix +42 −15 Original line number Diff line number Diff line Loading @@ -47,7 +47,12 @@ let then [ "${name} ${value}" ] else concatLists (mapAttrsToList (genSection name) value); addDefaults = settings: { backend = "btrfs-progs-sudo"; } // settings; sudo_doas = if config.security.sudo.enable then "sudo" else if config.security.doas.enable then "doas" else throw "The btrbk nixos module needs either sudo or doas enabled in the configuration"; addDefaults = settings: { backend = "btrfs-progs-${sudo_doas}"; } // settings; mkConfigFile = name: settings: pkgs.writeTextFile { name = "btrbk-${name}.conf"; Loading Loading @@ -152,7 +157,8 @@ in }; config = mkIf (sshEnabled || serviceEnabled) { environment.systemPackages = [ pkgs.btrbk ] ++ cfg.extraPackages; security.sudo.extraRules = [ security.sudo = mkIf (sudo_doas == "sudo") { extraRules = [ { users = [ "btrbk" ]; commands = [ Loading @@ -166,6 +172,26 @@ in ]; } ]; }; security.doas = mkIf (sudo_doas == "doas") { extraRules = let doasCmdNoPass = cmd: { users = [ "btrbk" ]; cmd = cmd; noPass = true; }; in [ (doasCmdNoPass "${pkgs.btrfs-progs}/bin/btrfs") (doasCmdNoPass "${pkgs.coreutils}/bin/mkdir") (doasCmdNoPass "${pkgs.coreutils}/bin/readlink") # for ssh, they are not the same than the one hard coded in ${pkgs.btrbk} (doasCmdNoPass "/run/current-system/bin/btrfs") (doasCmdNoPass "/run/current-system/sw/bin/mkdir") (doasCmdNoPass "/run/current-system/sw/bin/readlink") # doas matches command, not binary (doasCmdNoPass "btrfs") (doasCmdNoPass "mkdir") (doasCmdNoPass "readlink") ]; }; users.users.btrbk = { isSystemUser = true; # ssh needs a home directory Loading @@ -183,8 +209,9 @@ in "best-effort" = 2; "realtime" = 1; }.${cfg.ioSchedulingClass}; sudo_doas_flag = "--${sudo_doas}"; in ''command="${pkgs.util-linux}/bin/ionice -t -c ${toString ioniceClass} ${optionalString (cfg.niceness >= 1) "${pkgs.coreutils}/bin/nice -n ${toString cfg.niceness}"} ${pkgs.btrbk}/share/btrbk/scripts/ssh_filter_btrbk.sh --sudo ${options}" ${v.key}'' ''command="${pkgs.util-linux}/bin/ionice -t -c ${toString ioniceClass} ${optionalString (cfg.niceness >= 1) "${pkgs.coreutils}/bin/nice -n ${toString cfg.niceness}"} ${pkgs.btrbk}/share/btrbk/scripts/ssh_filter_btrbk.sh ${sudo_doas_flag} ${options}" ${v.key}'' ) cfg.sshAccess; }; Loading
nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ in { breitbandmessung = handleTest ./breitbandmessung.nix {}; brscan5 = handleTest ./brscan5.nix {}; btrbk = handleTest ./btrbk.nix {}; btrbk-doas = handleTest ./btrbk-doas.nix {}; btrbk-no-timer = handleTest ./btrbk-no-timer.nix {}; btrbk-section-order = handleTest ./btrbk-section-order.nix {}; buildbot = handleTest ./buildbot.nix {}; Loading
nixos/tests/btrbk-doas.nix 0 → 100644 +114 −0 Original line number Diff line number Diff line import ./make-test-python.nix ({ pkgs, ... }: let privateKey = '' -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg 9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ= -----END OPENSSH PRIVATE KEY----- ''; publicKey = '' ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv ''; in { name = "btrbk-doas"; meta = with pkgs.lib; { maintainers = with maintainers; [ symphorien tu-maurice ]; }; nodes = { archive = { ... }: { security.sudo.enable = false; security.doas.enable = true; environment.systemPackages = with pkgs; [ btrfs-progs ]; # note: this makes the privateKey world readable. # don't do it with real ssh keys. environment.etc."btrbk_key".text = privateKey; services.btrbk = { extraPackages = [ pkgs.lz4 ]; instances = { remote = { onCalendar = "minutely"; settings = { ssh_identity = "/etc/btrbk_key"; ssh_user = "btrbk"; stream_compress = "lz4"; volume = { "ssh://main/mnt" = { target = "/mnt"; snapshot_dir = "btrbk/remote"; subvolume = "to_backup"; }; }; }; }; }; }; }; main = { ... }: { security.sudo.enable = false; security.doas.enable = true; environment.systemPackages = with pkgs; [ btrfs-progs ]; services.openssh = { enable = true; passwordAuthentication = false; kbdInteractiveAuthentication = false; }; services.btrbk = { extraPackages = [ pkgs.lz4 ]; sshAccess = [ { key = publicKey; roles = [ "source" "send" "info" "delete" ]; } ]; instances = { local = { onCalendar = "minutely"; settings = { volume = { "/mnt" = { snapshot_dir = "btrbk/local"; subvolume = "to_backup"; }; }; }; }; }; }; }; }; testScript = '' start_all() # create btrfs partition at /mnt for machine in (archive, main): machine.succeed("dd if=/dev/zero of=/data_fs bs=120M count=1") machine.succeed("mkfs.btrfs /data_fs") machine.succeed("mkdir /mnt") machine.succeed("mount /data_fs /mnt") # what to backup and where main.succeed("btrfs subvolume create /mnt/to_backup") main.succeed("mkdir -p /mnt/btrbk/{local,remote}") # check that local snapshots work with subtest("local"): main.succeed("echo foo > /mnt/to_backup/bar") main.wait_until_succeeds("cat /mnt/btrbk/local/*/bar | grep foo") main.succeed("echo bar > /mnt/to_backup/bar") main.succeed("cat /mnt/btrbk/local/*/bar | grep foo") # check that btrfs send/receive works and ssh access works with subtest("remote"): archive.wait_until_succeeds("cat /mnt/*/bar | grep bar") main.succeed("echo baz > /mnt/to_backup/bar") archive.succeed("cat /mnt/*/bar | grep bar") ''; })
pkgs/tools/backup/btrbk/default.nix +1 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ stdenv.mkDerivation rec { ''; passthru.tests = { inherit (nixosTests) btrbk btrbk-no-timer btrbk-section-order; inherit (nixosTests) btrbk btrbk-no-timer btrbk-section-order btrbk-doas; }; passthru.updateScript = genericUpdater { Loading