at 18.09-beta 9.3 kB view raw
1import ./make-test.nix ({ pkgs, ... }: let 2 snakeOil = pkgs.runCommand "snakeoil-certs" { 3 outputs = [ "out" "cacert" "cert" "key" "crl" ]; 4 buildInputs = [ pkgs.gnutls.bin ]; 5 caTemplate = pkgs.writeText "snakeoil-ca.template" '' 6 cn = server 7 expiration_days = -1 8 cert_signing_key 9 ca 10 ''; 11 certTemplate = pkgs.writeText "snakeoil-cert.template" '' 12 cn = server 13 expiration_days = -1 14 tls_www_server 15 encryption_key 16 signing_key 17 ''; 18 crlTemplate = pkgs.writeText "snakeoil-crl.template" '' 19 expiration_days = -1 20 ''; 21 userCertTemplate = pkgs.writeText "snakeoil-user-cert.template" '' 22 organization = snakeoil 23 cn = server 24 expiration_days = -1 25 tls_www_client 26 encryption_key 27 signing_key 28 ''; 29 } '' 30 certtool -p --bits 4096 --outfile ca.key 31 certtool -s --template "$caTemplate" --load-privkey ca.key \ 32 --outfile "$cacert" 33 certtool -p --bits 4096 --outfile "$key" 34 certtool -c --template "$certTemplate" \ 35 --load-ca-privkey ca.key \ 36 --load-ca-certificate "$cacert" \ 37 --load-privkey "$key" \ 38 --outfile "$cert" 39 certtool --generate-crl --template "$crlTemplate" \ 40 --load-ca-privkey ca.key \ 41 --load-ca-certificate "$cacert" \ 42 --outfile "$crl" 43 44 mkdir "$out" 45 46 # Stripping key information before the actual PEM-encoded values is solely 47 # to make test output a bit less verbose when copying the client key to the 48 # actual client. 49 certtool -p --bits 4096 | sed -n \ 50 -e '/^----* *BEGIN/,/^----* *END/p' > "$out/alice.key" 51 52 certtool -c --template "$userCertTemplate" \ 53 --load-privkey "$out/alice.key" \ 54 --load-ca-privkey ca.key \ 55 --load-ca-certificate "$cacert" \ 56 --outfile "$out/alice.cert" 57 ''; 58 59in { 60 name = "taskserver"; 61 62 nodes = rec { 63 server = { 64 services.taskserver.enable = true; 65 services.taskserver.listenHost = "::"; 66 services.taskserver.fqdn = "server"; 67 services.taskserver.organisations = { 68 testOrganisation.users = [ "alice" "foo" ]; 69 anotherOrganisation.users = [ "bob" ]; 70 }; 71 }; 72 73 # New generation of the server with manual config 74 newServer = { lib, nodes, ... }: { 75 imports = [ server ]; 76 services.taskserver.pki.manual = { 77 ca.cert = snakeOil.cacert; 78 server.cert = snakeOil.cert; 79 server.key = snakeOil.key; 80 server.crl = snakeOil.crl; 81 }; 82 # This is to avoid assigning a different network address to the new 83 # generation. 84 networking = lib.mapAttrs (lib.const lib.mkForce) { 85 interfaces.eth1.ipv4 = nodes.server.config.networking.interfaces.eth1.ipv4; 86 inherit (nodes.server.config.networking) 87 hostName primaryIPAddress extraHosts; 88 }; 89 }; 90 91 client1 = { pkgs, ... }: { 92 environment.systemPackages = [ pkgs.taskwarrior pkgs.gnutls ]; 93 users.users.alice.isNormalUser = true; 94 users.users.bob.isNormalUser = true; 95 users.users.foo.isNormalUser = true; 96 users.users.bar.isNormalUser = true; 97 }; 98 99 client2 = client1; 100 }; 101 102 testScript = { nodes, ... }: let 103 cfg = nodes.server.config.services.taskserver; 104 portStr = toString cfg.listenPort; 105 newServerSystem = nodes.newServer.config.system.build.toplevel; 106 switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test"; 107 in '' 108 sub su ($$) { 109 my ($user, $cmd) = @_; 110 my $esc = $cmd =~ s/'/'\\${"'"}'/gr; 111 return "su - $user -c '$esc'"; 112 } 113 114 sub setupClientsFor ($$;$) { 115 my ($org, $user, $extraInit) = @_; 116 117 for my $client ($client1, $client2) { 118 $client->nest("initialize client for user $user", sub { 119 $client->succeed( 120 (su $user, "rm -rf /home/$user/.task"), 121 (su $user, "task rc.confirmation=no config confirmation no") 122 ); 123 124 my $exportinfo = $server->succeed( 125 "nixos-taskserver user export $org $user" 126 ); 127 128 $exportinfo =~ s/'/'\\'''/g; 129 130 $client->nest("importing taskwarrior configuration", sub { 131 my $cmd = su $user, "eval '$exportinfo' >&2"; 132 my ($status, $out) = $client->execute_($cmd); 133 if ($status != 0) { 134 $client->log("output: $out"); 135 die "command `$cmd' did not succeed (exit code $status)\n"; 136 } 137 }); 138 139 eval { &$extraInit($client, $org, $user) }; 140 141 $client->succeed(su $user, 142 "task config taskd.server server:${portStr} >&2" 143 ); 144 145 $client->succeed(su $user, "task sync init >&2"); 146 }); 147 } 148 } 149 150 sub restartServer { 151 $server->succeed("systemctl restart taskserver.service"); 152 $server->waitForOpenPort(${portStr}); 153 } 154 155 sub readdImperativeUser { 156 $server->nest("(re-)add imperative user bar", sub { 157 $server->execute("nixos-taskserver org remove imperativeOrg"); 158 $server->succeed( 159 "nixos-taskserver org add imperativeOrg", 160 "nixos-taskserver user add imperativeOrg bar" 161 ); 162 setupClientsFor "imperativeOrg", "bar"; 163 }); 164 } 165 166 sub testSync ($) { 167 my $user = $_[0]; 168 subtest "sync for user $user", sub { 169 $client1->succeed(su $user, "task add foo >&2"); 170 $client1->succeed(su $user, "task sync >&2"); 171 $client2->fail(su $user, "task list >&2"); 172 $client2->succeed(su $user, "task sync >&2"); 173 $client2->succeed(su $user, "task list >&2"); 174 }; 175 } 176 177 sub checkClientCert ($) { 178 my $user = $_[0]; 179 my $cmd = "gnutls-cli". 180 " --x509cafile=/home/$user/.task/keys/ca.cert". 181 " --x509keyfile=/home/$user/.task/keys/private.key". 182 " --x509certfile=/home/$user/.task/keys/public.cert". 183 " --port=${portStr} server < /dev/null"; 184 return su $user, $cmd; 185 } 186 187 # Explicitly start the VMs so that we don't accidentally start newServer 188 $server->start; 189 $client1->start; 190 $client2->start; 191 192 $server->waitForUnit("taskserver.service"); 193 194 $server->succeed( 195 "nixos-taskserver user list testOrganisation | grep -qxF alice", 196 "nixos-taskserver user list testOrganisation | grep -qxF foo", 197 "nixos-taskserver user list anotherOrganisation | grep -qxF bob" 198 ); 199 200 $server->waitForOpenPort(${portStr}); 201 202 $client1->waitForUnit("multi-user.target"); 203 $client2->waitForUnit("multi-user.target"); 204 205 setupClientsFor "testOrganisation", "alice"; 206 setupClientsFor "testOrganisation", "foo"; 207 setupClientsFor "anotherOrganisation", "bob"; 208 209 testSync $_ for ("alice", "bob", "foo"); 210 211 $server->fail("nixos-taskserver user add imperativeOrg bar"); 212 readdImperativeUser; 213 214 testSync "bar"; 215 216 subtest "checking certificate revocation of user bar", sub { 217 $client1->succeed(checkClientCert "bar"); 218 219 $server->succeed("nixos-taskserver user remove imperativeOrg bar"); 220 restartServer; 221 222 $client1->fail(checkClientCert "bar"); 223 224 $client1->succeed(su "bar", "task add destroy everything >&2"); 225 $client1->fail(su "bar", "task sync >&2"); 226 }; 227 228 readdImperativeUser; 229 230 subtest "checking certificate revocation of org imperativeOrg", sub { 231 $client1->succeed(checkClientCert "bar"); 232 233 $server->succeed("nixos-taskserver org remove imperativeOrg"); 234 restartServer; 235 236 $client1->fail(checkClientCert "bar"); 237 238 $client1->succeed(su "bar", "task add destroy even more >&2"); 239 $client1->fail(su "bar", "task sync >&2"); 240 }; 241 242 readdImperativeUser; 243 244 subtest "check whether declarative config overrides user bar", sub { 245 restartServer; 246 testSync "bar"; 247 }; 248 249 subtest "check manual configuration", sub { 250 # Remove the keys from automatic CA creation, to make sure the new 251 # generation doesn't use keys from before. 252 $server->succeed('rm -rf ${cfg.dataDir}/keys/* >&2'); 253 254 $server->succeed('${switchToNewServer} >&2'); 255 $server->waitForUnit("taskserver.service"); 256 $server->waitForOpenPort(${portStr}); 257 258 $server->succeed( 259 "nixos-taskserver org add manualOrg", 260 "nixos-taskserver user add manualOrg alice" 261 ); 262 263 setupClientsFor "manualOrg", "alice", sub { 264 my ($client, $org, $user) = @_; 265 my $cfgpath = "/home/$user/.task"; 266 267 $client->copyFileFromHost("${snakeOil.cacert}", "$cfgpath/ca.cert"); 268 for my $file ('alice.key', 'alice.cert') { 269 $client->copyFileFromHost("${snakeOil}/$file", "$cfgpath/$file"); 270 } 271 272 for my $file ("$user.key", "$user.cert") { 273 $client->copyFileFromHost( 274 "${snakeOil}/$file", "$cfgpath/$file" 275 ); 276 } 277 $client->copyFileFromHost( 278 "${snakeOil.cacert}", "$cfgpath/ca.cert" 279 ); 280 $client->succeed( 281 (su "alice", "task config taskd.ca $cfgpath/ca.cert"), 282 (su "alice", "task config taskd.key $cfgpath/$user.key"), 283 (su $user, "task config taskd.certificate $cfgpath/$user.cert") 284 ); 285 }; 286 287 testSync "alice"; 288 }; 289 ''; 290})