at 17.09-beta 9.2 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 userCertTemplace = pkgs.writeText "snakoil-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 "$userCertTemplace" \ 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 inherit (nodes.server.config.networking) 86 hostName interfaces primaryIPAddress extraHosts; 87 }; 88 }; 89 90 client1 = { pkgs, ... }: { 91 environment.systemPackages = [ pkgs.taskwarrior pkgs.gnutls ]; 92 users.users.alice.isNormalUser = true; 93 users.users.bob.isNormalUser = true; 94 users.users.foo.isNormalUser = true; 95 users.users.bar.isNormalUser = true; 96 }; 97 98 client2 = client1; 99 }; 100 101 testScript = { nodes, ... }: let 102 cfg = nodes.server.config.services.taskserver; 103 portStr = toString cfg.listenPort; 104 newServerSystem = nodes.newServer.config.system.build.toplevel; 105 switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test"; 106 in '' 107 sub su ($$) { 108 my ($user, $cmd) = @_; 109 my $esc = $cmd =~ s/'/'\\${"'"}'/gr; 110 return "su - $user -c '$esc'"; 111 } 112 113 sub setupClientsFor ($$;$) { 114 my ($org, $user, $extraInit) = @_; 115 116 for my $client ($client1, $client2) { 117 $client->nest("initialize client for user $user", sub { 118 $client->succeed( 119 (su $user, "rm -rf /home/$user/.task"), 120 (su $user, "task rc.confirmation=no config confirmation no") 121 ); 122 123 my $exportinfo = $server->succeed( 124 "nixos-taskserver user export $org $user" 125 ); 126 127 $exportinfo =~ s/'/'\\'''/g; 128 129 $client->nest("importing taskwarrior configuration", sub { 130 my $cmd = su $user, "eval '$exportinfo' >&2"; 131 my ($status, $out) = $client->execute_($cmd); 132 if ($status != 0) { 133 $client->log("output: $out"); 134 die "command `$cmd' did not succeed (exit code $status)\n"; 135 } 136 }); 137 138 eval { &$extraInit($client, $org, $user) }; 139 140 $client->succeed(su $user, 141 "task config taskd.server server:${portStr} >&2" 142 ); 143 144 $client->succeed(su $user, "task sync init >&2"); 145 }); 146 } 147 } 148 149 sub restartServer { 150 $server->succeed("systemctl restart taskserver.service"); 151 $server->waitForOpenPort(${portStr}); 152 } 153 154 sub readdImperativeUser { 155 $server->nest("(re-)add imperative user bar", sub { 156 $server->execute("nixos-taskserver org remove imperativeOrg"); 157 $server->succeed( 158 "nixos-taskserver org add imperativeOrg", 159 "nixos-taskserver user add imperativeOrg bar" 160 ); 161 setupClientsFor "imperativeOrg", "bar"; 162 }); 163 } 164 165 sub testSync ($) { 166 my $user = $_[0]; 167 subtest "sync for user $user", sub { 168 $client1->succeed(su $user, "task add foo >&2"); 169 $client1->succeed(su $user, "task sync >&2"); 170 $client2->fail(su $user, "task list >&2"); 171 $client2->succeed(su $user, "task sync >&2"); 172 $client2->succeed(su $user, "task list >&2"); 173 }; 174 } 175 176 sub checkClientCert ($) { 177 my $user = $_[0]; 178 my $cmd = "gnutls-cli". 179 " --x509cafile=/home/$user/.task/keys/ca.cert". 180 " --x509keyfile=/home/$user/.task/keys/private.key". 181 " --x509certfile=/home/$user/.task/keys/public.cert". 182 " --port=${portStr} server < /dev/null"; 183 return su $user, $cmd; 184 } 185 186 # Explicitly start the VMs so that we don't accidentally start newServer 187 $server->start; 188 $client1->start; 189 $client2->start; 190 191 $server->waitForUnit("taskserver.service"); 192 193 $server->succeed( 194 "nixos-taskserver user list testOrganisation | grep -qxF alice", 195 "nixos-taskserver user list testOrganisation | grep -qxF foo", 196 "nixos-taskserver user list anotherOrganisation | grep -qxF bob" 197 ); 198 199 $server->waitForOpenPort(${portStr}); 200 201 $client1->waitForUnit("multi-user.target"); 202 $client2->waitForUnit("multi-user.target"); 203 204 setupClientsFor "testOrganisation", "alice"; 205 setupClientsFor "testOrganisation", "foo"; 206 setupClientsFor "anotherOrganisation", "bob"; 207 208 testSync $_ for ("alice", "bob", "foo"); 209 210 $server->fail("nixos-taskserver user add imperativeOrg bar"); 211 readdImperativeUser; 212 213 testSync "bar"; 214 215 subtest "checking certificate revocation of user bar", sub { 216 $client1->succeed(checkClientCert "bar"); 217 218 $server->succeed("nixos-taskserver user remove imperativeOrg bar"); 219 restartServer; 220 221 $client1->fail(checkClientCert "bar"); 222 223 $client1->succeed(su "bar", "task add destroy everything >&2"); 224 $client1->fail(su "bar", "task sync >&2"); 225 }; 226 227 readdImperativeUser; 228 229 subtest "checking certificate revocation of org imperativeOrg", sub { 230 $client1->succeed(checkClientCert "bar"); 231 232 $server->succeed("nixos-taskserver org remove imperativeOrg"); 233 restartServer; 234 235 $client1->fail(checkClientCert "bar"); 236 237 $client1->succeed(su "bar", "task add destroy even more >&2"); 238 $client1->fail(su "bar", "task sync >&2"); 239 }; 240 241 readdImperativeUser; 242 243 subtest "check whether declarative config overrides user bar", sub { 244 restartServer; 245 testSync "bar"; 246 }; 247 248 subtest "check manual configuration", sub { 249 # Remove the keys from automatic CA creation, to make sure the new 250 # generation doesn't use keys from before. 251 $server->succeed('rm -rf ${cfg.dataDir}/keys/* >&2'); 252 253 $server->succeed('${switchToNewServer} >&2'); 254 $server->waitForUnit("taskserver.service"); 255 $server->waitForOpenPort(${portStr}); 256 257 $server->succeed( 258 "nixos-taskserver org add manualOrg", 259 "nixos-taskserver user add manualOrg alice" 260 ); 261 262 setupClientsFor "manualOrg", "alice", sub { 263 my ($client, $org, $user) = @_; 264 my $cfgpath = "/home/$user/.task"; 265 266 $client->copyFileFromHost("${snakeOil.cacert}", "$cfgpath/ca.cert"); 267 for my $file ('alice.key', 'alice.cert') { 268 $client->copyFileFromHost("${snakeOil}/$file", "$cfgpath/$file"); 269 } 270 271 for my $file ("$user.key", "$user.cert") { 272 $client->copyFileFromHost( 273 "${snakeOil}/$file", "$cfgpath/$file" 274 ); 275 } 276 $client->copyFileFromHost( 277 "${snakeOil.cacert}", "$cfgpath/ca.cert" 278 ); 279 $client->succeed( 280 (su "alice", "task config taskd.ca $cfgpath/ca.cert"), 281 (su "alice", "task config taskd.key $cfgpath/$user.key"), 282 (su $user, "task config taskd.certificate $cfgpath/$user.cert") 283 ); 284 }; 285 286 testSync "alice"; 287 }; 288 ''; 289})