about summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tests/by-name/em/email-dns/nodes/mail_server.nix14
-rw-r--r--tests/by-name/em/email-dns/nodes/name_server.nix234
-rw-r--r--tests/by-name/em/email-dns/nodes/user.nix8
-rw-r--r--tests/by-name/em/email-dns/test.nix38
-rw-r--r--tests/by-name/em/email-http/nodes/mail_server.nix57
-rw-r--r--tests/by-name/em/email-http/nodes/user.nix26
-rw-r--r--tests/by-name/em/email-http/test.nix115
-rw-r--r--tests/by-name/em/email-ip/test.nix4
-rw-r--r--tests/by-name/sh/sharkey/test.nix118
-rwxr-xr-xtests/common/acme/certs/generate (renamed from tests/by-name/em/email-dns/nodes/acme/certs/generate)0
-rwxr-xr-xtests/common/acme/certs/generate.ca (renamed from tests/by-name/em/email-dns/nodes/acme/certs/generate.ca)0
-rwxr-xr-xtests/common/acme/certs/generate.client (renamed from tests/by-name/em/email-dns/nodes/acme/certs/generate.client)0
-rw-r--r--tests/common/acme/certs/output/acme.test.cert.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.cert.pem)0
-rw-r--r--tests/common/acme/certs/output/acme.test.key.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.key.pem)0
-rw-r--r--tests/common/acme/certs/output/acme.test.template (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.template)0
-rw-r--r--tests/common/acme/certs/output/ca.cert.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/ca.cert.pem)0
-rw-r--r--tests/common/acme/certs/output/ca.key.pem (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/ca.key.pem)0
-rw-r--r--tests/common/acme/certs/output/ca.template (renamed from tests/by-name/em/email-dns/nodes/acme/certs/output/ca.template)0
-rw-r--r--tests/common/acme/certs/snakeoil-certs.nix (renamed from tests/by-name/em/email-dns/nodes/acme/certs/snakeoil-certs.nix)0
-rw-r--r--tests/common/acme/client.nix (renamed from tests/by-name/em/email-dns/nodes/acme/client.nix)0
-rw-r--r--tests/common/acme/scripts.nix30
-rw-r--r--tests/common/acme/server.nix (renamed from tests/by-name/em/email-dns/nodes/acme/default.nix)27
-rw-r--r--tests/common/dns/client.nix10
-rw-r--r--tests/common/dns/server.nix43
-rw-r--r--tests/common/email/dkim/alice.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/alice.com/private.age)0
-rw-r--r--tests/common/email/dkim/alice.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/alice.com/public)0
-rw-r--r--tests/common/email/dkim/bob.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/bob.com/private.age)0
-rw-r--r--tests/common/email/dkim/bob.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/bob.com/public)0
-rwxr-xr-xtests/common/email/dkim/gen_key.sh (renamed from tests/by-name/em/email-dns/secrets/dkim/gen_key.sh)0
-rw-r--r--tests/common/email/dkim/mail1.server.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/private.age)0
-rw-r--r--tests/common/email/dkim/mail1.server.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/public)0
-rw-r--r--tests/common/email/dkim/mail2.server.com/private.age (renamed from tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/private.age)0
-rw-r--r--tests/common/email/dkim/mail2.server.com/public (renamed from tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/public)0
-rw-r--r--tests/common/email/hostKey (renamed from tests/by-name/em/email-dns/secrets/hostKey)0
34 files changed, 521 insertions, 203 deletions
diff --git a/tests/by-name/em/email-dns/nodes/mail_server.nix b/tests/by-name/em/email-dns/nodes/mail_server.nix
index a8c528a..279d289 100644
--- a/tests/by-name/em/email-dns/nodes/mail_server.nix
+++ b/tests/by-name/em/email-dns/nodes/mail_server.nix
@@ -13,7 +13,8 @@
       extraModules
       ++ [
         ../../../../../modules
-        ./acme/client.nix
+        ../../../../common/acme/client.nix
+        ../../../../common/dns/client.nix
       ];
 
     environment.systemPackages = [
@@ -21,12 +22,7 @@
       pkgs.openssl
     ];
 
-    networking.nameservers = lib.mkForce [
-      nodes.name_server.networking.primaryIPAddress
-      nodes.name_server.networking.primaryIPv6Address
-    ];
-
-    age.identityPaths = ["${../secrets/hostKey}"];
+    age.identityPaths = ["${../../../../common/email/hostKey}"];
 
     vhack = {
       stalwart-mail = {
@@ -36,8 +32,8 @@
         security = {
           dkimKeys = let
             loadKey = name: {
-              dkimPublicKey = builtins.readFile (../secrets/dkim + "/${name}/public");
-              dkimPrivateKeyPath = ../secrets/dkim + "/${name}/private.age";
+              dkimPublicKey = builtins.readFile (../../../../common/email/dkim + "/${name}/public");
+              dkimPrivateKeyPath = ../../../../common/email/dkim + "/${name}/private.age";
               keyAlgorithm = "ed25519-sha256";
             };
           in {
diff --git a/tests/by-name/em/email-dns/nodes/name_server.nix b/tests/by-name/em/email-dns/nodes/name_server.nix
index ef657f4..d9d3617 100644
--- a/tests/by-name/em/email-dns/nodes/name_server.nix
+++ b/tests/by-name/em/email-dns/nodes/name_server.nix
@@ -139,14 +139,10 @@ in {
     extraModules
     ++ [
       ../../../../../modules
-      ./acme/client.nix
+      ../../../../common/acme/client.nix
+      ../../../../common/dns/server.nix
     ];
 
-  networking.nameservers = lib.mkForce [
-    nodes.name_server.networking.primaryIPAddress
-    nodes.name_server.networking.primaryIPv6Address
-  ];
-
   services.nginx = {
     logError = "stderr debug";
     virtualHosts = let
@@ -175,145 +171,121 @@ in {
     nginx = {
       enable = true;
     };
-    dns = {
-      enable = true;
-      openFirewall = true;
-      interfaces = [
-        nodes.name_server.networking.primaryIPAddress
-        nodes.name_server.networking.primaryIPv6Address
-      ];
-
-      zones = let
-        stsZone = {
-          SOA = {
-            nameServer = "ns";
-            adminEmail = "admin@server.com";
-            serial = 2025012301;
-          };
+    dns.zones = let
+      stsZone = {
+        SOA = {
+          nameServer = "ns";
+          adminEmail = "admin@server.com";
+          serial = 2025012301;
+        };
 
-          useOrigin = false;
+        useOrigin = false;
 
-          A = [
-            nodes.name_server.networking.primaryIPAddress
-          ];
-          AAAA = [
-            nodes.name_server.networking.primaryIPv6Address
-          ];
+        A = [
+          nodes.name_server.networking.primaryIPAddress
+        ];
+        AAAA = [
+          nodes.name_server.networking.primaryIPv6Address
+        ];
+      };
+    in {
+      "arpa" = {
+        SOA = {
+          nameServer = "ns";
+          adminEmail = "admin@server.com";
+          serial = 2025012301;
         };
-      in {
-        "arpa" = {
-          SOA = {
-            nameServer = "ns";
-            adminEmail = "admin@server.com";
-            serial = 2025012301;
-          };
-          useOrigin = false;
+        useOrigin = false;
 
-          PTR = [
-            {
-              name = "acme.test";
-              ip.v4 = nodes.acme.networking.primaryIPAddress;
-            }
-            {
-              name = "acme.test";
-              ip.v6 = nodes.acme.networking.primaryIPv6Address;
-            }
+        PTR = [
+          {
+            name = "acme.test";
+            ip.v4 = nodes.acme.networking.primaryIPAddress;
+          }
+          {
+            name = "acme.test";
+            ip.v6 = nodes.acme.networking.primaryIPv6Address;
+          }
 
-            {
-              name = "alice.com";
-              ip.v4 = nodes.alice.networking.primaryIPAddress;
-            }
-            {
-              name = "alice.com";
-              ip.v6 = nodes.alice.networking.primaryIPv6Address;
-            }
+          {
+            name = "alice.com";
+            ip.v4 = nodes.alice.networking.primaryIPAddress;
+          }
+          {
+            name = "alice.com";
+            ip.v6 = nodes.alice.networking.primaryIPv6Address;
+          }
 
-            {
-              name = "bob";
-              ip.v4 = nodes.bob.networking.primaryIPAddress;
-            }
-            {
-              name = "bob";
-              ip.v6 = nodes.bob.networking.primaryIPv6Address;
-            }
+          {
+            name = "bob";
+            ip.v4 = nodes.bob.networking.primaryIPAddress;
+          }
+          {
+            name = "bob";
+            ip.v6 = nodes.bob.networking.primaryIPv6Address;
+          }
 
-            {
-              name = "mail1.server.com";
-              ip.v4 = nodes.mail1_server.networking.primaryIPAddress;
-            }
-            {
-              name = "mail1.server.com";
-              ip.v6 = nodes.mail1_server.networking.primaryIPv6Address;
-            }
+          {
+            name = "mail1.server.com";
+            ip.v4 = nodes.mail1_server.networking.primaryIPAddress;
+          }
+          {
+            name = "mail1.server.com";
+            ip.v6 = nodes.mail1_server.networking.primaryIPv6Address;
+          }
 
-            {
-              name = "mail2.server.com";
-              ip.v4 = nodes.mail2_server.networking.primaryIPAddress;
-            }
-            {
-              name = "mail2.server.com";
-              ip.v6 = nodes.mail2_server.networking.primaryIPv6Address;
-            }
+          {
+            name = "mail2.server.com";
+            ip.v4 = nodes.mail2_server.networking.primaryIPAddress;
+          }
+          {
+            name = "mail2.server.com";
+            ip.v6 = nodes.mail2_server.networking.primaryIPv6Address;
+          }
 
-            {
-              name = "ns.server.com";
-              ip.v4 = nodes.name_server.networking.primaryIPAddress;
-            }
-            {
-              name = "ns.server.com";
-              ip.v6 = nodes.name_server.networking.primaryIPv6Address;
-            }
-          ];
-        };
-
-        "alice.com" = mkZone "alice" nodes lib nodes.mail2_server.vhack.stalwart-mail;
-        "mta-sts.alice.com" = stsZone;
-        "bob.com" = mkZone "bob" nodes lib nodes.mail1_server.vhack.stalwart-mail;
-        "mta-sts.bob.com" = stsZone;
-        "mail1.server.com" = mkServerZone "mail1" nodes lib;
-        "mail2.server.com" = mkServerZone "mail2" nodes lib;
-        "ns.server.com" = {
-          SOA = {
-            nameServer = "ns";
-            adminEmail = "admin@server.com";
-            serial = 2025012301;
-          };
-          useOrigin = false;
+          {
+            name = "ns.server.com";
+            ip.v4 = nodes.name_server.networking.primaryIPAddress;
+          }
+          {
+            name = "ns.server.com";
+            ip.v6 = nodes.name_server.networking.primaryIPv6Address;
+          }
+        ];
+      };
 
-          A = [
-            nodes.name_server.networking.primaryIPAddress
-          ];
-          AAAA = [
-            nodes.name_server.networking.primaryIPv6Address
-          ];
+      "alice.com" = mkZone "alice" nodes lib nodes.mail2_server.vhack.stalwart-mail;
+      "mta-sts.alice.com" = stsZone;
+      "bob.com" = mkZone "bob" nodes lib nodes.mail1_server.vhack.stalwart-mail;
+      "mta-sts.bob.com" = stsZone;
+      "mail1.server.com" = mkServerZone "mail1" nodes lib;
+      "mail2.server.com" = mkServerZone "mail2" nodes lib;
+      "ns.server.com" = {
+        SOA = {
+          nameServer = "ns";
+          adminEmail = "admin@server.com";
+          serial = 2025012301;
         };
-        "acme.test" = {
-          SOA = {
-            nameServer = "ns";
-            adminEmail = "admin@server.com";
-            serial = 2025012301;
-          };
-          useOrigin = false;
+        useOrigin = false;
 
-          A = [
-            nodes.acme.networking.primaryIPAddress
-          ];
-          AAAA = [
-            nodes.acme.networking.primaryIPv6Address
-          ];
+        A = [
+          nodes.name_server.networking.primaryIPAddress
+        ];
+        AAAA = [
+          nodes.name_server.networking.primaryIPv6Address
+        ];
+      };
+      "server.com" = {
+        SOA = {
+          nameServer = "ns";
+          adminEmail = "admin@server.com";
+          serial = 2025012301;
         };
-        "server.com" = {
-          SOA = {
-            nameServer = "ns";
-            adminEmail = "admin@server.com";
-            serial = 2025012301;
-          };
 
-          useOrigin = false;
-          NS = [
-            "ns.server.com."
-          ];
-        };
+        useOrigin = false;
+        NS = [
+          "ns.server.com."
+        ];
       };
     };
   };
diff --git a/tests/by-name/em/email-dns/nodes/user.nix b/tests/by-name/em/email-dns/nodes/user.nix
index e4db347..fba02ce 100644
--- a/tests/by-name/em/email-dns/nodes/user.nix
+++ b/tests/by-name/em/email-dns/nodes/user.nix
@@ -8,7 +8,8 @@
     ...
   }: {
     imports = [
-      ./acme/client.nix
+      ../../../../common/acme/client.nix
+      ../../../../common/dns/client.nix
     ];
 
     environment.systemPackages = [
@@ -20,11 +21,6 @@
       pkgs.openssl
     ];
 
-    networking.nameservers = lib.mkForce [
-      nodes.name_server.networking.primaryIPAddress
-      nodes.name_server.networking.primaryIPv6Address
-    ];
-
     users.users."${user}" = {isNormalUser = true;};
 
     systemd.tmpfiles.rules = [
diff --git a/tests/by-name/em/email-dns/test.nix b/tests/by-name/em/email-dns/test.nix
index 32447ae..f0399a5 100644
--- a/tests/by-name/em/email-dns/test.nix
+++ b/tests/by-name/em/email-dns/test.nix
@@ -31,9 +31,9 @@ in
         lib,
         ...
       }: {
-        imports = [./nodes/acme];
-        networking.nameservers = lib.mkForce [
-          nodes.name_server.networking.primaryIPAddress
+        imports = [
+          ../../../common/acme/server.nix
+          ../../../common/dns/client.nix
         ];
       };
 
@@ -44,7 +44,7 @@ in
         {
           class = "individual";
           name = "bob";
-          secret = "bob-password";
+          secret = "{PLAIN}bob-password";
           email = ["bob@bob.com"];
         };
 
@@ -53,7 +53,7 @@ in
         {
           class = "individual";
           name = "alice";
-          secret = "alice-password";
+          secret = "{PLAIN}alice-password";
           email = ["alice@alice.com"];
         };
 
@@ -89,7 +89,8 @@ in
           exit 1
         }
       '';
-      inherit (pkgs) lib;
+
+      acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;};
     in
       /*
       python
@@ -121,30 +122,7 @@ in
 
         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 <nixpgks>/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";
-        ''}")
+            node.succeed("${acme_scripts.add_pebble_acme_ca}")
 
         with subtest("Both mailserver successfully started all services"):
           import json
diff --git a/tests/by-name/em/email-http/nodes/mail_server.nix b/tests/by-name/em/email-http/nodes/mail_server.nix
new file mode 100644
index 0000000..e94c4e9
--- /dev/null
+++ b/tests/by-name/em/email-http/nodes/mail_server.nix
@@ -0,0 +1,57 @@
+{
+  extraModules,
+  pkgs,
+  vhackPackages,
+}: {
+  mkMailServer = serverName: principal: {
+    config,
+    lib,
+    nodes,
+    ...
+  }: {
+    imports =
+      extraModules
+      ++ [
+        ../../../../../modules
+        ../../../../common/acme/client.nix
+      ];
+
+    environment.systemPackages = [
+      pkgs.bind
+      pkgs.openssl
+    ];
+
+    networking.nameservers = lib.mkForce [
+      nodes.name_server.networking.primaryIPAddress
+      nodes.name_server.networking.primaryIPv6Address
+    ];
+
+    age.identityPaths = ["${../../../../common/email/hostKey}"];
+
+    vhack = {
+      stalwart-mail = {
+        enable = true;
+        fqdn = "${serverName}.server.com";
+        admin = "admin@${serverName}.server.com";
+        security = {
+          dkimKeys = let
+            loadKey = name: {
+              dkimPublicKey = builtins.readFile (../../../../common/email/dkim + "/${name}/public");
+              dkimPrivateKeyPath = ../../../../common/email/dkim + "/${name}/private.age";
+              keyAlgorithm = "ed25519-sha256";
+            };
+          in {
+            "mail.server.com" = loadKey "mail1.server.com";
+            "bob.com" = loadKey "bob.com";
+          };
+          verificationMode = "strict";
+        };
+        openFirewall = true;
+        principals =
+          if principal == null
+          then null
+          else [principal];
+      };
+    };
+  };
+}
diff --git a/tests/by-name/em/email-http/nodes/user.nix b/tests/by-name/em/email-http/nodes/user.nix
new file mode 100644
index 0000000..73b9ff7
--- /dev/null
+++ b/tests/by-name/em/email-http/nodes/user.nix
@@ -0,0 +1,26 @@
+{
+  pkgs,
+  vhackPackages,
+}: {
+  mkUser = user: serverName: {
+    nodes,
+    lib,
+    ...
+  }: {
+    imports = [
+      ../../../../common/acme/client.nix
+    ];
+
+    environment.systemPackages = [
+      pkgs.bind
+      pkgs.openssl
+    ];
+
+    networking.nameservers = lib.mkForce [
+      nodes.name_server.networking.primaryIPAddress
+      nodes.name_server.networking.primaryIPv6Address
+    ];
+
+    users.users."${user}" = {isNormalUser = true;};
+  };
+}
diff --git a/tests/by-name/em/email-http/test.nix b/tests/by-name/em/email-http/test.nix
new file mode 100644
index 0000000..f508b9f
--- /dev/null
+++ b/tests/by-name/em/email-http/test.nix
@@ -0,0 +1,115 @@
+{
+  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-http";
+
+    node = {
+      specialArgs = {inherit pkgsUnstable vhackPackages nixpkgs-unstable nixLib;};
+
+      # Use the nixpkgs as constructed by the `nixpkgs.*` options
+      pkgs = null;
+    };
+
+    nodes = {
+      acme = {
+        nodes,
+        lib,
+        ...
+      }: {
+        imports = [
+          ../../../common/acme/server.nix
+          ../../../common/dns/client.nix
+        ];
+      };
+
+      name_server = {nodes, ...}: {
+        imports =
+          extraModules
+          ++ [
+            ../../../common/acme/client.nix
+            ../../../common/dns/server.nix
+          ];
+
+        vhack.dns.zones = {
+          "mail.server.com" = {
+            SOA = {
+              nameServer = "ns";
+              adminEmail = "admin@server.com";
+              serial = 2025012301;
+            };
+            useOrigin = false;
+
+            A = [
+              nodes.mail_server.networking.primaryIPAddress
+            ];
+            AAAA = [
+              nodes.mail_server.networking.primaryIPv6Address
+            ];
+          };
+        };
+      };
+
+      mail_server = mkMailServer "mail" null;
+
+      bob = mkUser "bob" "mail";
+    };
+
+    # TODO(@bpeetz): This test should also test the http JMAP features of stalwart-mail. <2025-04-12>
+    testScript = _: let
+      acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;};
+    in
+      /*
+      python
+      */
+      ''
+        # 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()
+
+        mail_server.wait_for_unit("stalwart-mail.service")
+        mail_server.wait_for_open_port(993) # imap
+        mail_server.wait_for_open_port(465) # smtp
+
+        bob.wait_for_unit("multi-user.target")
+
+        with subtest("Add pebble ca key to all services"):
+          for node in [name_server, mail_server, bob]:
+            node.wait_for_unit("network-online.target")
+            node.succeed("${acme_scripts.add_pebble_acme_ca}")
+
+        with subtest("The 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(mail_server)
+
+        with subtest("Bob can use the self-service interface"):
+          bob.succeed("${pkgs.writeShellScript "check-self-service" ''
+          curl mail.server.com --location --output /home/bob/output.html;
+        ''}")
+
+        bob.copy_from_vm("/home/bob", "")
+      '';
+  }
diff --git a/tests/by-name/em/email-ip/test.nix b/tests/by-name/em/email-ip/test.nix
index 688cd8f..dabc404 100644
--- a/tests/by-name/em/email-ip/test.nix
+++ b/tests/by-name/em/email-ip/test.nix
@@ -113,13 +113,13 @@ in
               {
                 class = "individual";
                 name = "alice";
-                secret = "alice-password";
+                secret = "{PLAIN}alice-password";
                 email = ["alice@${domain}"];
               }
               {
                 class = "individual";
                 name = "bob";
-                secret = "bob-password";
+                secret = "{PLAIN}bob-password";
                 email = ["bob@${domain}"];
               }
             ];
diff --git a/tests/by-name/sh/sharkey/test.nix b/tests/by-name/sh/sharkey/test.nix
new file mode 100644
index 0000000..40efe17
--- /dev/null
+++ b/tests/by-name/sh/sharkey/test.nix
@@ -0,0 +1,118 @@
+{
+  nixos-lib,
+  pkgsUnstable,
+  nixpkgs-unstable,
+  vhackPackages,
+  pkgs,
+  extraModules,
+  nixLib,
+  ...
+}:
+nixos-lib.runTest {
+  hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs
+
+  name = "sharkey";
+
+  node = {
+    specialArgs = {inherit pkgsUnstable extraModules vhackPackages nixpkgs-unstable nixLib;};
+
+    # Use the nixpkgs as constructed by the `nixpkgs.*` options
+    pkgs = null;
+  };
+
+  nodes = {
+    acme = {...}: {
+      imports = [
+        ../../../common/acme/server.nix
+        ../../../common/dns/client.nix
+      ];
+    };
+    name_server = {nodes, ...}: {
+      imports =
+        extraModules
+        ++ [
+          ../../../common/acme/client.nix
+          ../../../common/dns/server.nix
+        ];
+
+      vhack.dns.zones = {
+        "sharkey.server" = {
+          SOA = {
+            nameServer = "ns";
+            adminEmail = "admin@server.com";
+            serial = 2025012301;
+          };
+          useOrigin = false;
+
+          A = [
+            nodes.server.networking.primaryIPAddress
+          ];
+          AAAA = [
+            nodes.server.networking.primaryIPv6Address
+          ];
+        };
+      };
+    };
+
+    server = {config, ...}: {
+      imports =
+        extraModules
+        ++ [
+          ../../../../modules
+          ../../../common/acme/client.nix
+          ../../../common/dns/client.nix
+        ];
+
+      vhack = {
+        persist.enable = true;
+        nginx.enable = true;
+        sharkey = {
+          enable = true;
+          fqdn = "sharkey.server";
+        };
+      };
+    };
+
+    client = {...}: {
+      imports = [
+        ../../../common/acme/client.nix
+        ../../../common/dns/client.nix
+      ];
+    };
+  };
+
+  testScript = {nodes, ...}: let
+    acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;};
+  in
+    /*
+    python
+    */
+    ''
+      # 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()
+
+
+      with subtest("Add pebble ca key to all services"):
+        for node in [name_server, server, client]:
+          node.wait_for_unit("network-online.target")
+          node.succeed("${acme_scripts.add_pebble_acme_ca}")
+
+      server.wait_for_unit("sharkey.service")
+
+      with subtest("All services running"):
+        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(server)
+
+      client.wait_until_succeeds("curl --silent https://sharkey.server | grep 'Thank you for using Sharkey!'")
+    '';
+}
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/generate b/tests/common/acme/certs/generate
index 0d6258e..0d6258e 100755
--- a/tests/by-name/em/email-dns/nodes/acme/certs/generate
+++ b/tests/common/acme/certs/generate
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/generate.ca b/tests/common/acme/certs/generate.ca
index 92832c5..92832c5 100755
--- a/tests/by-name/em/email-dns/nodes/acme/certs/generate.ca
+++ b/tests/common/acme/certs/generate.ca
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/generate.client b/tests/common/acme/certs/generate.client
index 5930298..5930298 100755
--- a/tests/by-name/em/email-dns/nodes/acme/certs/generate.client
+++ b/tests/common/acme/certs/generate.client
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.cert.pem b/tests/common/acme/certs/output/acme.test.cert.pem
index 687101d..687101d 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.cert.pem
+++ b/tests/common/acme/certs/output/acme.test.cert.pem
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.key.pem b/tests/common/acme/certs/output/acme.test.key.pem
index 06195b8..06195b8 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.key.pem
+++ b/tests/common/acme/certs/output/acme.test.key.pem
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.template b/tests/common/acme/certs/output/acme.test.template
index 320a170..320a170 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/output/acme.test.template
+++ b/tests/common/acme/certs/output/acme.test.template
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.cert.pem b/tests/common/acme/certs/output/ca.cert.pem
index 0fa9d14..0fa9d14 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.cert.pem
+++ b/tests/common/acme/certs/output/ca.cert.pem
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.key.pem b/tests/common/acme/certs/output/ca.key.pem
index 64263bc..64263bc 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.key.pem
+++ b/tests/common/acme/certs/output/ca.key.pem
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.template b/tests/common/acme/certs/output/ca.template
index a2295d8..a2295d8 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/output/ca.template
+++ b/tests/common/acme/certs/output/ca.template
diff --git a/tests/by-name/em/email-dns/nodes/acme/certs/snakeoil-certs.nix b/tests/common/acme/certs/snakeoil-certs.nix
index aeb6dfc..aeb6dfc 100644
--- a/tests/by-name/em/email-dns/nodes/acme/certs/snakeoil-certs.nix
+++ b/tests/common/acme/certs/snakeoil-certs.nix
diff --git a/tests/by-name/em/email-dns/nodes/acme/client.nix b/tests/common/acme/client.nix
index 2b870e8..2b870e8 100644
--- a/tests/by-name/em/email-dns/nodes/acme/client.nix
+++ b/tests/common/acme/client.nix
diff --git a/tests/common/acme/scripts.nix b/tests/common/acme/scripts.nix
new file mode 100644
index 0000000..2228823
--- /dev/null
+++ b/tests/common/acme/scripts.nix
@@ -0,0 +1,30 @@
+{pkgs}:
+/*
+* Extra functions useful for the test script.
+*/
+{
+  add_pebble_acme_ca = 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 <nixpgks>/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";
+  '';
+}
diff --git a/tests/by-name/em/email-dns/nodes/acme/default.nix b/tests/common/acme/server.nix
index 236ba6a..997c944 100644
--- a/tests/by-name/em/email-dns/nodes/acme/default.nix
+++ b/tests/common/acme/server.nix
@@ -1,28 +1,5 @@
-# The certificate for the ACME service is exported as:
-#
-#   config.test-support.acme.caCert
-#
-# This value can be used inside the configuration of other test nodes to inject
-# the test certificate into security.pki.certificateFiles or into package
-# overlays.
-#
-# {
-#   acme = { nodes, lib, ... }: {
-#     imports = [ ./common/acme/server ];
-#     networking.nameservers = lib.mkForce [
-#       nodes.mydnsresolver.networking.primaryIPAddress
-#     ];
-#   };
-#
-#   dnsmyresolver = ...;
-# }
-#
-# Keep in mind, that currently only _one_ resolver is supported, if you have
-# more than one resolver in networking.nameservers only the first one will be
-# used.
-#
-# Also make sure that whenever you use a resolver from a different test node
-# that it has to be started _before_ the ACME service.
+# Add this node as acme server.
+# This also needs a DNS server.
 {
   config,
   pkgs,
diff --git a/tests/common/dns/client.nix b/tests/common/dns/client.nix
new file mode 100644
index 0000000..52f3267
--- /dev/null
+++ b/tests/common/dns/client.nix
@@ -0,0 +1,10 @@
+{
+  lib,
+  nodes,
+  ...
+}: {
+  networking.nameservers = lib.mkForce [
+    nodes.name_server.networking.primaryIPAddress
+    nodes.name_server.networking.primaryIPv6Address
+  ];
+}
diff --git a/tests/common/dns/server.nix b/tests/common/dns/server.nix
new file mode 100644
index 0000000..0c8d72c
--- /dev/null
+++ b/tests/common/dns/server.nix
@@ -0,0 +1,43 @@
+{
+  lib,
+  nodes,
+  ...
+}: {
+  imports = [
+    ../../../modules
+  ];
+
+  networking.nameservers = lib.mkForce [
+    nodes.name_server.networking.primaryIPAddress
+    nodes.name_server.networking.primaryIPv6Address
+  ];
+
+  vhack = {
+    dns = {
+      enable = true;
+      openFirewall = true;
+      interfaces = [
+        nodes.name_server.networking.primaryIPAddress
+        nodes.name_server.networking.primaryIPv6Address
+      ];
+
+      zones = {
+        "acme.test" = {
+          SOA = {
+            nameServer = "ns";
+            adminEmail = "admin@server.com";
+            serial = 2025012301;
+          };
+          useOrigin = false;
+
+          A = [
+            nodes.acme.networking.primaryIPAddress
+          ];
+          AAAA = [
+            nodes.acme.networking.primaryIPv6Address
+          ];
+        };
+      };
+    };
+  };
+}
diff --git a/tests/by-name/em/email-dns/secrets/dkim/alice.com/private.age b/tests/common/email/dkim/alice.com/private.age
index 5415fdc..5415fdc 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/alice.com/private.age
+++ b/tests/common/email/dkim/alice.com/private.age
diff --git a/tests/by-name/em/email-dns/secrets/dkim/alice.com/public b/tests/common/email/dkim/alice.com/public
index 0f3c3b2..0f3c3b2 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/alice.com/public
+++ b/tests/common/email/dkim/alice.com/public
diff --git a/tests/by-name/em/email-dns/secrets/dkim/bob.com/private.age b/tests/common/email/dkim/bob.com/private.age
index c07c997..c07c997 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/bob.com/private.age
+++ b/tests/common/email/dkim/bob.com/private.age
diff --git a/tests/by-name/em/email-dns/secrets/dkim/bob.com/public b/tests/common/email/dkim/bob.com/public
index ddea670..ddea670 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/bob.com/public
+++ b/tests/common/email/dkim/bob.com/public
diff --git a/tests/by-name/em/email-dns/secrets/dkim/gen_key.sh b/tests/common/email/dkim/gen_key.sh
index 48b4434..48b4434 100755
--- a/tests/by-name/em/email-dns/secrets/dkim/gen_key.sh
+++ b/tests/common/email/dkim/gen_key.sh
diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/private.age b/tests/common/email/dkim/mail1.server.com/private.age
index 8c5d3c3..8c5d3c3 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/private.age
+++ b/tests/common/email/dkim/mail1.server.com/private.age
diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/public b/tests/common/email/dkim/mail1.server.com/public
index 4941b85..4941b85 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/mail1.server.com/public
+++ b/tests/common/email/dkim/mail1.server.com/public
diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/private.age b/tests/common/email/dkim/mail2.server.com/private.age
index d39631a..d39631a 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/private.age
+++ b/tests/common/email/dkim/mail2.server.com/private.age
diff --git a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/public b/tests/common/email/dkim/mail2.server.com/public
index 5c4406d..5c4406d 100644
--- a/tests/by-name/em/email-dns/secrets/dkim/mail2.server.com/public
+++ b/tests/common/email/dkim/mail2.server.com/public
diff --git a/tests/by-name/em/email-dns/secrets/hostKey b/tests/common/email/hostKey
index 79c9d6c..79c9d6c 100644
--- a/tests/by-name/em/email-dns/secrets/hostKey
+++ b/tests/common/email/hostKey