{ nixos-lib, pkgsUnstable, nixpkgs-unstable, vhackPackages, pkgs, extraModules, nixLib, ... }: let mail_server = import ./nodes/mail_server.nix {inherit extraModules pkgs vhackPackages;}; inherit (mail_server) mkMailServer; user = import ./nodes/user.nix {inherit pkgs vhackPackages;}; inherit (user) mkUser; in nixos-lib.runTest { hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs name = "email-dns"; node = { specialArgs = {inherit pkgsUnstable vhackPackages nixpkgs-unstable nixLib;}; # Use the nixpkgs as constructed by the `nixpkgs.*` options pkgs = null; }; nodes = { acme = { nodes, lib, ... }: { imports = [./nodes/acme]; networking.nameservers = lib.mkForce [ nodes.name_server.networking.primaryIPAddress ]; }; name_server = import ./nodes/name_server.nix {inherit extraModules;}; mail1_server = mkMailServer "mail1" { class = "individual"; name = "bob"; secret = "bob-password"; email = ["bob@bob.com"]; }; mail2_server = mkMailServer "mail2" { class = "individual"; name = "alice"; secret = "alice-password"; email = ["alice@alice.com"]; }; bob = mkUser "bob" "mail1"; alice = mkUser "alice" "mail2"; }; testScript = {...}: let checkEmailEmpty = pkgs.writeShellScript "assert-empty-emails" '' set -xe # fetchmail returns EXIT_CODE 1 when no new mail fetchmail --verbose >&2 || [ "$?" -eq 1 ] || { echo "Mail was not empty" >&2 exit 1 } ''; checkEmailNotEmpty = pkgs.writeShellScript "assert-empty-emails" '' set -xe # fetchmail returns EXIT_CODE 1 when no new mail fetchmail --verbose >&2 || [ "$?" -ne 1 ] || { echo "No new mail" >&2 exit 1 } ''; checkSpamEmailNotEmpty = pkgs.writeShellScript "assert-empty-emails" '' set -xe # fetchmail returns EXIT_CODE 1 when no new mail fetchmail --folder JUNK --verbose >&2 || [ "$?" -ne 1 ] || { echo "No new mail" >&2 exit 1 } ''; inherit (pkgs) lib; in /* python */ '' from time import sleep # Start dependencies for the other services acme.start() acme.wait_for_unit("pebble.service") name_server.start() name_server.wait_for_unit("nsd.service") # Start the actual testing machines start_all() mail1_server.wait_for_unit("stalwart-mail.service") mail1_server.wait_for_open_port(993) # imap mail1_server.wait_for_open_port(465) # smtp mail2_server.wait_for_unit("stalwart-mail.service") mail2_server.wait_for_open_port(993) # imap mail2_server.wait_for_open_port(465) # smtp alice.wait_for_unit("multi-user.target") bob.wait_for_unit("multi-user.target") name_server.wait_until_succeeds("stat /var/lib/acme/mta-sts.alice.com/cert.pem") name_server.wait_until_succeeds("stat /var/lib/acme/mta-sts.bob.com/cert.pem") with subtest("Add pebble ca key to all services"): for node in [name_server, mail1_server, mail2_server, alice, bob]: node.succeed("${pkgs.writeShellScript "fetch-and-set-ca" '' set -xe # Fetch the randomly generated ca certificate curl https://acme.test:15000/roots/0 > /tmp/ca.crt curl https://acme.test:15000/intermediates/0 >> /tmp/ca.crt # Append it to the various system stores # The file paths are from /modules/security/ca.nix for cert_path in "ssl/certs/ca-certificates.crt" "ssl/certs/ca-bundle.crt" "pki/tls/certs/ca-bundle.crt"; do cert_path="/etc/$cert_path" mv "$cert_path" "$cert_path.old" cat "$cert_path.old" > "$cert_path" cat /tmp/ca.crt >> "$cert_path" done export NIX_SSL_CERT_FILE=/tmp/ca.crt export SSL_CERT_FILE=/tmp/ca.crt # TODO # # P11-Kit trust source. # environment.etc."ssl/trust-source".source = "$${cacertPackage.p11kit}/etc/ssl/trust-source"; ''}") with subtest("Both mailserver successfully started all services"): import json def all_services_running(host): (status, output) = host.systemctl("list-units --state=failed --plain --no-pager --output=json") host_failed = json.loads(output) assert len(host_failed) == 0, f"Expected zero failing services, but found: {json.dumps(host_failed, indent=4)}" all_services_running(mail1_server) all_services_running(mail2_server) with subtest("Both start without mail"): alice.succeed("sudo -u alice ${checkEmailEmpty}") bob.succeed("sudo -u bob ${checkEmailEmpty}") with subtest("Alice can send an empty email to bob"): alice.succeed("sudo -u alice ${pkgs.writeShellScript "alice-send" '' set -xe echo "" | msmtp --debug --account alice bob@bob.com >&2 ''}") # Give `mail2_server` some time to send the email. sleep(160) bob.succeed("sudo -u bob ${checkSpamEmailNotEmpty}") with subtest("Alice can send an non-empty email to bob"): alice.succeed("sudo -u alice ${pkgs.writeShellScript "alice-send" '' set -xe cat << EOF | msmtp --debug --account alice bob@bob.com >&2 Subject: Hi bob, I'm Alice! Good day, Bob! This is an email. It contains a subject and a body. I also assert utf8 support by including my last name in this very message. XOXO Alice van DÃ¥ligen. . EOF ''}") # Give `mail2_server` some time to send the email. sleep(120) bob.succeed("sudo -u bob ${checkEmailNotEmpty}") mail1_server.copy_from_vm("/var/lib/", "server1") mail2_server.copy_from_vm("/var/lib/", "server2") bob.copy_from_vm("/home/bob/mail", "bob") ''; }