at 25.11-pre 11 kB view raw
1import ./make-test-python.nix ( 2 { pkgs, ... }: 3 4 let 5 passphrase = "supersecret"; 6 dataDir = "/ran:dom/data"; 7 subDir = "not_anything_here"; 8 excludedSubDirFile = "not_this_file_either"; 9 excludeFile = "not_this_file"; 10 keepFile = "important_file"; 11 keepFileData = "important_data"; 12 localRepo = "/root/back:up"; 13 # a repository on a file system which is not mounted automatically 14 localRepoMount = "/noAutoMount"; 15 archiveName = "my_archive"; 16 remoteRepo = "borg@server:."; # No need to specify path 17 privateKey = pkgs.writeText "id_ed25519" '' 18 -----BEGIN OPENSSH PRIVATE KEY----- 19 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 20 QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe 21 RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw 22 AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg 23 9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ= 24 -----END OPENSSH PRIVATE KEY----- 25 ''; 26 publicKey = '' 27 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client 28 ''; 29 privateKeyAppendOnly = pkgs.writeText "id_ed25519" '' 30 -----BEGIN OPENSSH PRIVATE KEY----- 31 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 32 QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8 33 cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw 34 AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8 35 IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ= 36 -----END OPENSSH PRIVATE KEY----- 37 ''; 38 publicKeyAppendOnly = '' 39 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client 40 ''; 41 42 in 43 { 44 name = "borgbackup"; 45 meta = with pkgs.lib; { 46 maintainers = with maintainers; [ dotlambda ]; 47 }; 48 49 nodes = { 50 client = 51 { ... }: 52 { 53 virtualisation.fileSystems.${localRepoMount} = { 54 device = "tmpfs"; 55 fsType = "tmpfs"; 56 options = [ "noauto" ]; 57 }; 58 59 services.borgbackup.jobs = { 60 61 local = { 62 paths = dataDir; 63 repo = localRepo; 64 preHook = '' 65 # Don't append a timestamp 66 archiveName="${archiveName}" 67 ''; 68 encryption = { 69 mode = "repokey"; 70 inherit passphrase; 71 }; 72 compression = "auto,zlib,9"; 73 prune.keep = { 74 within = "1y"; 75 yearly = 5; 76 }; 77 exclude = [ "*/${excludeFile}" ]; 78 extraCreateArgs = [ 79 "--exclude-caches" 80 "--exclude-if-present" 81 ".dont backup" 82 ]; 83 postHook = "echo post"; 84 startAt = [ ]; # Do not run automatically 85 }; 86 87 localMount = { 88 paths = dataDir; 89 repo = localRepoMount; 90 encryption.mode = "none"; 91 startAt = [ ]; 92 }; 93 94 remote = { 95 paths = dataDir; 96 repo = remoteRepo; 97 encryption.mode = "none"; 98 startAt = [ ]; 99 environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519"; 100 }; 101 102 remoteAppendOnly = { 103 paths = dataDir; 104 repo = remoteRepo; 105 encryption.mode = "none"; 106 startAt = [ ]; 107 environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly"; 108 }; 109 110 commandSuccess = { 111 dumpCommand = pkgs.writeScript "commandSuccess" '' 112 echo -n test 113 ''; 114 repo = remoteRepo; 115 encryption.mode = "none"; 116 startAt = [ ]; 117 environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519"; 118 }; 119 120 commandFail = { 121 dumpCommand = "${pkgs.coreutils}/bin/false"; 122 repo = remoteRepo; 123 encryption.mode = "none"; 124 startAt = [ ]; 125 environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519"; 126 }; 127 128 sleepInhibited = { 129 inhibitsSleep = true; 130 # Blocks indefinitely while "backing up" so that we can try to suspend the local system while it's hung 131 dumpCommand = pkgs.writeScript "sleepInhibited" '' 132 cat /dev/zero 133 ''; 134 repo = remoteRepo; 135 encryption.mode = "none"; 136 startAt = [ ]; 137 environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519"; 138 }; 139 140 }; 141 }; 142 143 server = 144 { ... }: 145 { 146 services.openssh = { 147 enable = true; 148 settings = { 149 PasswordAuthentication = false; 150 KbdInteractiveAuthentication = false; 151 }; 152 }; 153 154 services.borgbackup.repos.repo1 = { 155 authorizedKeys = [ publicKey ]; 156 path = "/data/borgbackup"; 157 }; 158 159 # Second repo to make sure the authorizedKeys options are merged correctly 160 services.borgbackup.repos.repo2 = { 161 authorizedKeysAppendOnly = [ publicKeyAppendOnly ]; 162 path = "/data/borgbackup"; 163 quota = ".5G"; 164 }; 165 }; 166 }; 167 168 testScript = '' 169 start_all() 170 171 client.fail('test -d "${remoteRepo}"') 172 173 client.succeed( 174 "cp ${privateKey} /root/id_ed25519" 175 ) 176 client.succeed("chmod 0600 /root/id_ed25519") 177 client.succeed( 178 "cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly" 179 ) 180 client.succeed("chmod 0600 /root/id_ed25519.appendOnly") 181 182 client.succeed("mkdir -p ${dataDir}/${subDir}") 183 client.succeed("touch ${dataDir}/${excludeFile}") 184 client.succeed("touch '${dataDir}/${subDir}/.dont backup'") 185 client.succeed("touch ${dataDir}/${subDir}/${excludedSubDirFile}") 186 client.succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}") 187 188 with subtest("local"): 189 borg = "BORG_PASSPHRASE='${passphrase}' borg" 190 client.systemctl("start --wait borgbackup-job-local") 191 client.fail("systemctl is-failed borgbackup-job-local") 192 # Make sure exactly one archive has been created 193 assert int(client.succeed("{} list '${localRepo}' | wc -l".format(borg))) > 0 194 # Make sure excludeFile has been excluded 195 client.fail( 196 "{} list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'".format(borg) 197 ) 198 # Make sure excludedSubDirFile has been excluded 199 client.fail( 200 "{} list '${localRepo}::${archiveName}' | grep -qF '${subDir}/${excludedSubDirFile}".format(borg) 201 ) 202 # Make sure keepFile has the correct content 203 client.succeed("{} extract '${localRepo}::${archiveName}'".format(borg)) 204 assert "${keepFileData}" in client.succeed("cat ${dataDir}/${keepFile}") 205 # Make sure the same is true when using `borg mount` 206 client.succeed( 207 "mkdir -p /mnt/borg && {} mount '${localRepo}::${archiveName}' /mnt/borg".format( 208 borg 209 ) 210 ) 211 assert "${keepFileData}" in client.succeed( 212 "cat /mnt/borg/${dataDir}/${keepFile}" 213 ) 214 215 with subtest("localMount"): 216 # the file system for the repo should not be already mounted 217 client.fail("mount | grep ${localRepoMount}") 218 # ensure trying to write to the mountpoint before the fs is mounted fails 219 client.succeed("chattr +i ${localRepoMount}") 220 borg = "borg" 221 client.systemctl("start --wait borgbackup-job-localMount") 222 client.fail("systemctl is-failed borgbackup-job-localMount") 223 # Make sure exactly one archive has been created 224 assert int(client.succeed("{} list '${localRepoMount}' | wc -l".format(borg))) > 0 225 226 with subtest("remote"): 227 borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg" 228 server.wait_for_unit("sshd.service") 229 client.wait_for_unit("network.target") 230 client.systemctl("start --wait borgbackup-job-remote") 231 client.fail("systemctl is-failed borgbackup-job-remote") 232 233 # Make sure we can't access repos other than the specified one 234 client.fail("{} list borg\@server:wrong".format(borg)) 235 236 # TODO: Make sure that data is actually deleted 237 238 with subtest("remoteAppendOnly"): 239 borg = ( 240 "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg" 241 ) 242 server.wait_for_unit("sshd.service") 243 client.wait_for_unit("network.target") 244 client.systemctl("start --wait borgbackup-job-remoteAppendOnly") 245 client.fail("systemctl is-failed borgbackup-job-remoteAppendOnly") 246 247 # Make sure we can't access repos other than the specified one 248 client.fail("{} list borg\@server:wrong".format(borg)) 249 250 # TODO: Make sure that data is not actually deleted 251 252 with subtest("commandSuccess"): 253 server.wait_for_unit("sshd.service") 254 client.wait_for_unit("network.target") 255 client.systemctl("start --wait borgbackup-job-commandSuccess") 256 client.fail("systemctl is-failed borgbackup-job-commandSuccess") 257 id = client.succeed("borg-job-commandSuccess list | tail -n1 | cut -d' ' -f1").strip() 258 client.succeed(f"borg-job-commandSuccess extract ::{id} stdin") 259 assert "test" == client.succeed("cat stdin") 260 261 with subtest("commandFail"): 262 server.wait_for_unit("sshd.service") 263 client.wait_for_unit("network.target") 264 client.systemctl("start --wait borgbackup-job-commandFail") 265 client.succeed("systemctl is-failed borgbackup-job-commandFail") 266 267 with subtest("sleepInhibited"): 268 server.wait_for_unit("sshd.service") 269 client.wait_for_unit("network.target") 270 client.fail("systemd-inhibit --list | grep -q borgbackup") 271 client.systemctl("start borgbackup-job-sleepInhibited") 272 client.wait_until_succeeds("systemd-inhibit --list | grep -q borgbackup") 273 client.systemctl("stop borgbackup-job-sleepInhibited") 274 ''; 275 } 276)