summary refs log tree commit diff
path: root/nixos/tests/borgbackup.nix
blob: 9b39abdfa8edf25259b2b8f8b838203868ddf097 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import ./make-test.nix ({ pkgs, ... }:

let
  passphrase = "supersecret";
  dataDir = "/ran:dom/data";
  excludeFile = "not_this_file";
  keepFile = "important_file";
  keepFileData = "important_data";
  localRepo = "/root/back:up";
  archiveName = "my_archive";
  remoteRepo = "borg@server:."; # No need to specify path
  privateKey = pkgs.writeText "id_ed25519" ''
    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
    QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe
    RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw
    AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg
    9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ=
    -----END OPENSSH PRIVATE KEY-----
  '';
  publicKey = ''
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client
  '';
  privateKeyAppendOnly = pkgs.writeText "id_ed25519" ''
    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
    QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8
    cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw
    AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8
    IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ=
    -----END OPENSSH PRIVATE KEY-----
  '';
  publicKeyAppendOnly = ''
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client
  '';

in {
  name = "borgbackup";
  meta = with pkgs.stdenv.lib; {
    maintainers = with maintainers; [ dotlambda ];
  };

  nodes = {
    client = { ... }: {
      services.borgbackup.jobs = {
        
        local = rec {
          paths = dataDir;
          repo = localRepo;
          preHook = ''
            # Don't append a timestamp
            archiveName="${archiveName}"
          '';
          encryption = {
            mode = "repokey";
            inherit passphrase;
          };
          compression = "auto,zlib,9";
          prune.keep = {
            within = "1y";
            yearly = 5;
          };
          exclude = [ "*/${excludeFile}" ];
          postHook = "echo post";
          startAt = [ ]; # Do not run automatically
        };

        remote = {
          paths = dataDir;
          repo = remoteRepo;
          encryption.mode = "none";
          startAt = [ ];
          environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
        };

        remoteAppendOnly = {
          paths = dataDir;
          repo = remoteRepo;
          encryption.mode = "none";
          startAt = [ ];
          environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly";
        };

      };
    };

    server = { ... }: {
      services.openssh = {
        enable = true;
        passwordAuthentication = false;
        challengeResponseAuthentication = false;
      };

      services.borgbackup.repos.repo1 = {
        authorizedKeys = [ publicKey ];
        path = "/data/borgbackup";
      };

      # Second repo to make sure the authorizedKeys options are merged correctly
      services.borgbackup.repos.repo2 = {
        authorizedKeysAppendOnly = [ publicKeyAppendOnly ];
        path = "/data/borgbackup";
        quota = ".5G";
      };
    };
  };

  testScript = ''
    startAll;

    $client->fail('test -d "${remoteRepo}"');

    $client->succeed("cp ${privateKey} /root/id_ed25519");
    $client->succeed("chmod 0600 /root/id_ed25519");
    $client->succeed("cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly");
    $client->succeed("chmod 0600 /root/id_ed25519.appendOnly");

    $client->succeed("mkdir -p ${dataDir}");
    $client->succeed("touch ${dataDir}/${excludeFile}");
    $client->succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}");

    subtest "local", sub {
      my $borg = "BORG_PASSPHRASE='${passphrase}' borg";
      $client->systemctl("start --wait borgbackup-job-local");
      $client->fail("systemctl is-failed borgbackup-job-local");
      # Make sure exactly one archive has been created
      $client->succeed("c=\$($borg list '${localRepo}' | wc -l) && [[ \$c == '1' ]]");
      # Make sure excludeFile has been excluded
      $client->fail("$borg list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'");
      # Make sure keepFile has the correct content
      $client->succeed("$borg extract '${localRepo}::${archiveName}'");
      $client->succeed('c=$(cat ${dataDir}/${keepFile}) && [[ "$c" == "${keepFileData}" ]]');
    };

    subtest "remote", sub {
      my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg";
      $server->waitForUnit("sshd.service");
      $client->waitForUnit("network.target");
      $client->systemctl("start --wait borgbackup-job-remote");
      $client->fail("systemctl is-failed borgbackup-job-remote");

      # Make sure we can't access repos other than the specified one
      $client->fail("$borg list borg\@server:wrong");

      #TODO: Make sure that data is actually deleted
    };

    subtest "remoteAppendOnly", sub {
      my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg";
      $server->waitForUnit("sshd.service");
      $client->waitForUnit("network.target");
      $client->systemctl("start --wait borgbackup-job-remoteAppendOnly");
      $client->fail("systemctl is-failed borgbackup-job-remoteAppendOnly");

      # Make sure we can't access repos other than the specified one
      $client->fail("$borg list borg\@server:wrong");

      #TODO: Make sure that data is not actually deleted
    };

  '';
})