{ lib, host, dsl, commonModule, }: pkgs: let inherit (host.specialArgs.hostInfo) paths; serverDataDir = paths.data; serverSecretsDir = paths.secrets; testContainers = dsl.filterDeployments (c: c.includedInTests) host.containers; it = dsl.mkIntegrationTest { inherit host testContainers pkgs; }; nginxAcmeDomains = if testContainers ? nginx && testContainers.nginx.miscProperties ? acmeDomains then testContainers.nginx.miscProperties.acmeDomains else [ ]; nginxAcmeDir = domain: "${serverDataDir}/core/nginx/acme/${domain}"; fakeCert = domain: pkgs.runCommand "fake-cert-${domain}" { nativeBuildInputs = [ pkgs.openssl ]; } '' mkdir -p "$out" openssl req -x509 -newkey rsa:2048 -nodes -days 1 \ -subj "/CN=${domain}" \ -keyout "$out/key.pem" -out "$out/fullchain.pem" 2>/dev/null ''; mkdirAcmeDirs = lib.concatMapStrings ( domain: ''mkdir -p "${nginxAcmeDir domain}"'' + "\n" ) nginxAcmeDomains; installFakeCerts = lib.concatMapStrings ( domain: let dir = nginxAcmeDir domain; cert = fakeCert domain; in '' if [[ ! -f "${dir}/fullchain.pem" ]]; then install -m 644 ${cert}/fullchain.pem "${dir}/fullchain.pem" install -m 600 ${cert}/key.pem "${dir}/key.pem" fi '' ) nginxAcmeDomains; # used in ssh-admin testSshKey = pkgs.runCommand "test-ssh-key" { nativeBuildInputs = [ pkgs.openssh ]; } '' mkdir -p "$out" ssh-keygen -q -t ed25519 -N "" -f "$out/key" -C test ''; sshpiperTestUser = "admin"; hostSshdPort = 5952; # derive data directories from container bind mounts allDataDirs = lib.unique ( lib.concatMap (c: lib.filter (p: lib.hasPrefix serverDataDir p) c.bindMountHostPaths) ( lib.attrValues testContainers ) ); mkdirAllDataDirs = lib.concatMapStrings (p: ''mkdir -p "${p}"'' + "\n") allDataDirs; # writable data dirs need ownership set to 1000:1000 (the standard # container user uid). services that run as a different user (loki, # prometheus, grafana) fix ownership via StateDirectory on startup. writableDataDirs = lib.unique ( lib.concatMap ( c: if c.kind == "nspawn" then lib.filter (p: lib.hasPrefix serverDataDir p) ( map (bm: bm.hostPath) (lib.attrValues (lib.filterAttrs (_: bm: !bm.isReadOnly) c.bindMounts)) ) else [ ] ) (lib.attrValues testContainers) ); chownWritableDataDirs = lib.concatMapStrings ( p: ''chown -R 1000:1000 "${p}"'' + "\n" ) writableDataDirs; # secret paths needed by test containers secretPaths = lib.unique ( lib.concatMap (c: lib.filter (p: lib.hasPrefix serverSecretsDir p) c.bindMountHostPaths) ( lib.attrValues testContainers ) ); mkdirSecretParents = lib.concatMapStrings (p: ''mkdir -p "$(dirname '${p}')"'' + "\n") secretPaths; in { name = "integration-${host.hostName}"; nodes = { internet = it.internetNode; server = { pkgs, lib, ... }: { imports = [ commonModule host.specialArgs.inputs.microvm.nixosModules.host (dsl.mkHostFromContainers { containers = testContainers; inherit (host) ctx; }) it.serverModule ]; system.activationScripts.test-secrets = # bash '' mkdir -p /nix/var/nix/db /nix/var/nix/daemon-socket mkdir -p /var/log/journal mkdir -p "${serverSecretsDir}" ${mkdirAllDataDirs} ${mkdirAcmeDirs} ${mkdirSecretParents} ${chownWritableDataDirs} for keytype in ed25519 rsa ecdsa; do keyfile="${serverSecretsDir}/ssh_host_''${keytype}_key" if [[ ! -f "$keyfile" ]]; then ${lib.getExe' pkgs.openssh "ssh-keygen"} -q -t "$keytype" -f "$keyfile" -N "" fi done ${installFakeCerts} '' + lib.optionalString (testContainers ? sshpiper) '' mkdir -p "${serverDataDir}/core/sshpiper/user_${sshpiperTestUser}" install -m 644 ${testSshKey}/key.pub \ "${serverDataDir}/core/sshpiper/user_${sshpiperTestUser}/authorized_keys" '' + lib.optionalString (testContainers ? mail) '' for f in mail-admin-password mail-account-password; do target="${serverSecretsDir}/$f" if [[ ! -f "$target" ]]; then head -c 32 /dev/urandom | base64 | tr -d '\n' > "$target" chmod 600 "$target" fi done ''; } // lib.optionalAttrs (testContainers ? nginx && nginxAcmeDomains != [ ]) { containers.nginx.config = { systemd.services = lib.genAttrs (map (d: "acme-order-renew-${d}") nginxAcmeDomains) (_: { enable = lib.mkForce false; }); }; }; }; testScript = it.testScriptPreamble + '' # store paths are world-readable; ssh refuses a key with those permissions SSH_KEY = "/root/test-ssh-key" SSH_OPTS = f"-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -i {SSH_KEY}" TEST_SSH_KEY_STORE = "${testSshKey}/key" SSHPIPER_TEST_USER = "${sshpiperTestUser}" HOST_SSHD_PORT = ${toString hostSshdPort} '' + builtins.readFile ./integration.py; }