nixos/preload: init
[NixPkgs.git] / nixos / tests / borgbackup.nix
blob4160e727f047b3558df4a9af8d1407ea25887e8f
1 import ./make-test-python.nix ({ pkgs, ... }:
3 let
4   passphrase = "supersecret";
5   dataDir = "/ran:dom/data";
6   excludeFile = "not_this_file";
7   keepFile = "important_file";
8   keepFileData = "important_data";
9   localRepo = "/root/back:up";
10   archiveName = "my_archive";
11   remoteRepo = "borg@server:."; # No need to specify path
12   privateKey = pkgs.writeText "id_ed25519" ''
13     -----BEGIN OPENSSH PRIVATE KEY-----
14     b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
15     QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe
16     RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw
17     AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg
18     9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ=
19     -----END OPENSSH PRIVATE KEY-----
20   '';
21   publicKey = ''
22     ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client
23   '';
24   privateKeyAppendOnly = pkgs.writeText "id_ed25519" ''
25     -----BEGIN OPENSSH PRIVATE KEY-----
26     b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
27     QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8
28     cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw
29     AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8
30     IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ=
31     -----END OPENSSH PRIVATE KEY-----
32   '';
33   publicKeyAppendOnly = ''
34     ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client
35   '';
37 in {
38   name = "borgbackup";
39   meta = with pkgs.lib; {
40     maintainers = with maintainers; [ dotlambda ];
41   };
43   nodes = {
44     client = { ... }: {
45       services.borgbackup.jobs = {
47         local = {
48           paths = dataDir;
49           repo = localRepo;
50           preHook = ''
51             # Don't append a timestamp
52             archiveName="${archiveName}"
53           '';
54           encryption = {
55             mode = "repokey";
56             inherit passphrase;
57           };
58           compression = "auto,zlib,9";
59           prune.keep = {
60             within = "1y";
61             yearly = 5;
62           };
63           exclude = [ "*/${excludeFile}" ];
64           postHook = "echo post";
65           startAt = [ ]; # Do not run automatically
66         };
68         remote = {
69           paths = dataDir;
70           repo = remoteRepo;
71           encryption.mode = "none";
72           startAt = [ ];
73           environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
74         };
76         remoteAppendOnly = {
77           paths = dataDir;
78           repo = remoteRepo;
79           encryption.mode = "none";
80           startAt = [ ];
81           environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly";
82         };
84         commandSuccess = {
85           dumpCommand = pkgs.writeScript "commandSuccess" ''
86             echo -n test
87           '';
88           repo = remoteRepo;
89           encryption.mode = "none";
90           startAt = [ ];
91           environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
92         };
94         commandFail = {
95           dumpCommand = "${pkgs.coreutils}/bin/false";
96           repo = remoteRepo;
97           encryption.mode = "none";
98           startAt = [ ];
99           environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
100         };
102         sleepInhibited = {
103           inhibitsSleep = true;
104           # Blocks indefinitely while "backing up" so that we can try to suspend the local system while it's hung
105           dumpCommand = pkgs.writeScript "sleepInhibited" ''
106             cat /dev/zero
107           '';
108           repo = remoteRepo;
109           encryption.mode = "none";
110           startAt = [ ];
111           environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
112         };
114       };
115     };
117     server = { ... }: {
118       services.openssh = {
119         enable = true;
120         settings = {
121           PasswordAuthentication = false;
122           KbdInteractiveAuthentication = false;
123         };
124       };
126       services.borgbackup.repos.repo1 = {
127         authorizedKeys = [ publicKey ];
128         path = "/data/borgbackup";
129       };
131       # Second repo to make sure the authorizedKeys options are merged correctly
132       services.borgbackup.repos.repo2 = {
133         authorizedKeysAppendOnly = [ publicKeyAppendOnly ];
134         path = "/data/borgbackup";
135         quota = ".5G";
136       };
137     };
138   };
140   testScript = ''
141     start_all()
143     client.fail('test -d "${remoteRepo}"')
145     client.succeed(
146         "cp ${privateKey} /root/id_ed25519"
147     )
148     client.succeed("chmod 0600 /root/id_ed25519")
149     client.succeed(
150         "cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly"
151     )
152     client.succeed("chmod 0600 /root/id_ed25519.appendOnly")
154     client.succeed("mkdir -p ${dataDir}")
155     client.succeed("touch ${dataDir}/${excludeFile}")
156     client.succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}")
158     with subtest("local"):
159         borg = "BORG_PASSPHRASE='${passphrase}' borg"
160         client.systemctl("start --wait borgbackup-job-local")
161         client.fail("systemctl is-failed borgbackup-job-local")
162         # Make sure exactly one archive has been created
163         assert int(client.succeed("{} list '${localRepo}' | wc -l".format(borg))) > 0
164         # Make sure excludeFile has been excluded
165         client.fail(
166             "{} list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'".format(borg)
167         )
168         # Make sure keepFile has the correct content
169         client.succeed("{} extract '${localRepo}::${archiveName}'".format(borg))
170         assert "${keepFileData}" in client.succeed("cat ${dataDir}/${keepFile}")
171         # Make sure the same is true when using `borg mount`
172         client.succeed(
173             "mkdir -p /mnt/borg && {} mount '${localRepo}::${archiveName}' /mnt/borg".format(
174                 borg
175             )
176         )
177         assert "${keepFileData}" in client.succeed(
178             "cat /mnt/borg/${dataDir}/${keepFile}"
179         )
181     with subtest("remote"):
182         borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg"
183         server.wait_for_unit("sshd.service")
184         client.wait_for_unit("network.target")
185         client.systemctl("start --wait borgbackup-job-remote")
186         client.fail("systemctl is-failed borgbackup-job-remote")
188         # Make sure we can't access repos other than the specified one
189         client.fail("{} list borg\@server:wrong".format(borg))
191         # TODO: Make sure that data is actually deleted
193     with subtest("remoteAppendOnly"):
194         borg = (
195             "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg"
196         )
197         server.wait_for_unit("sshd.service")
198         client.wait_for_unit("network.target")
199         client.systemctl("start --wait borgbackup-job-remoteAppendOnly")
200         client.fail("systemctl is-failed borgbackup-job-remoteAppendOnly")
202         # Make sure we can't access repos other than the specified one
203         client.fail("{} list borg\@server:wrong".format(borg))
205         # TODO: Make sure that data is not actually deleted
207     with subtest("commandSuccess"):
208         server.wait_for_unit("sshd.service")
209         client.wait_for_unit("network.target")
210         client.systemctl("start --wait borgbackup-job-commandSuccess")
211         client.fail("systemctl is-failed borgbackup-job-commandSuccess")
212         id = client.succeed("borg-job-commandSuccess list | tail -n1 | cut -d' ' -f1").strip()
213         client.succeed(f"borg-job-commandSuccess extract ::{id} stdin")
214         assert "test" == client.succeed("cat stdin")
216     with subtest("commandFail"):
217         server.wait_for_unit("sshd.service")
218         client.wait_for_unit("network.target")
219         client.systemctl("start --wait borgbackup-job-commandFail")
220         client.succeed("systemctl is-failed borgbackup-job-commandFail")
222     with subtest("sleepInhibited"):
223         server.wait_for_unit("sshd.service")
224         client.wait_for_unit("network.target")
225         client.fail("systemd-inhibit --list | grep -q borgbackup")
226         client.systemctl("start borgbackup-job-sleepInhibited")
227         client.wait_until_succeeds("systemd-inhibit --list | grep -q borgbackup")
228         client.systemctl("stop borgbackup-job-sleepInhibited")
229   '';