{ hostPkgs, lib, ... }:
{
  _class = "nixosTest";
  name = "ghostunnel";
  nodes = {
    backend =
      { pkgs, ... }:
      {
        services.nginx.enable = true;
        services.nginx.virtualHosts."backend".root = pkgs.runCommand "webroot" { } ''
          mkdir $out
          echo hi >$out/hi.txt
        '';
        networking.firewall.allowedTCPPorts = [ 80 ];
      };
    service =
      { pkgs, ... }:
      {
        system.services."ghostunnel-plain-old" = {
          imports = [ pkgs.ghostunnel.services.default ];
          ghostunnel = {
            listen = "0.0.0.0:443";
            cert = "/root/service-cert.pem";
            key = "/root/service-key.pem";
            disableAuthentication = true;
            target = "backend:80";
            unsafeTarget = true;
          };
        };
        system.services."ghostunnel-client-cert" = {
          imports = [ pkgs.ghostunnel.services.default ];
          ghostunnel = {
            listen = "0.0.0.0:1443";
            cert = "/root/service-cert.pem";
            key = "/root/service-key.pem";
            cacert = "/root/ca.pem";
            target = "backend:80";
            allowCN = [ "client" ];
            unsafeTarget = true;
          };
        };
        networking.firewall.allowedTCPPorts = [
          443
          1443
        ];
      };
    client =
      { pkgs, ... }:
      {
        environment.systemPackages = [
          pkgs.curl
        ];
      };
  };

  testScript = ''
    import os

    # prepare certificates

    def cmd(command):
      print(f"+{command}")
      r = os.system(command)
      if r != 0:
        raise Exception(f"Command {command} failed with exit code {r}")

    # Create CA
    cmd("${hostPkgs.openssl}/bin/openssl genrsa -out ca-key.pem 4096")
    cmd("${hostPkgs.openssl}/bin/openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -subj '/C=NL/ST=Zuid-Holland/L=The Hague/O=Stevige Balken en Planken B.V./OU=OpSec/CN=Certificate Authority' -out ca.pem")

    # Create service
    cmd("${hostPkgs.openssl}/bin/openssl genrsa -out service-key.pem 4096")
    cmd("${hostPkgs.openssl}/bin/openssl req -subj '/CN=service' -sha256 -new -key service-key.pem -out service.csr")
    cmd("echo subjectAltName = DNS:service,IP:127.0.0.1 >> extfile.cnf")
    cmd("echo extendedKeyUsage = serverAuth >> extfile.cnf")
    cmd("${hostPkgs.openssl}/bin/openssl x509 -req -days 365 -sha256 -in service.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out service-cert.pem -extfile extfile.cnf")

    # Create client
    cmd("${hostPkgs.openssl}/bin/openssl genrsa -out client-key.pem 4096")
    cmd("${hostPkgs.openssl}/bin/openssl req -subj '/CN=client' -new -key client-key.pem -out client.csr")
    cmd("echo extendedKeyUsage = clientAuth > extfile-client.cnf")
    cmd("${hostPkgs.openssl}/bin/openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extfile extfile-client.cnf")

    cmd("ls -al")

    start_all()

    # Configuration
    service.copy_from_host("ca.pem", "/root/ca.pem")
    service.copy_from_host("service-cert.pem", "/root/service-cert.pem")
    service.copy_from_host("service-key.pem", "/root/service-key.pem")
    client.copy_from_host("ca.pem", "/root/ca.pem")
    client.copy_from_host("service-cert.pem", "/root/service-cert.pem")
    client.copy_from_host("client-cert.pem", "/root/client-cert.pem")
    client.copy_from_host("client-key.pem", "/root/client-key.pem")

    backend.wait_for_unit("nginx.service")
    service.wait_for_unit("multi-user.target")
    service.wait_for_unit("multi-user.target")
    client.wait_for_unit("multi-user.target")

    # Check assumptions before the real test
    client.succeed("bash -c 'diff <(curl -v --no-progress-meter http://backend/hi.txt) <(echo hi)'")

    # Plain old simple TLS can connect, ignoring cert
    client.succeed("bash -c 'diff <(curl -v --no-progress-meter --insecure https://service/hi.txt) <(echo hi)'")

    # Plain old simple TLS provides correct signature with its cert
    client.succeed("bash -c 'diff <(curl -v --no-progress-meter --cacert /root/ca.pem https://service/hi.txt) <(echo hi)'")

    # Client can authenticate with certificate
    client.succeed("bash -c 'diff <(curl -v --no-progress-meter --cert /root/client-cert.pem --key /root/client-key.pem --cacert /root/ca.pem https://service:1443/hi.txt) <(echo hi)'")

    # Client must authenticate with certificate
    client.fail("bash -c 'diff <(curl -v --no-progress-meter --cacert /root/ca.pem https://service:1443/hi.txt) <(echo hi)'")
  '';

  meta.maintainers = with lib.maintainers; [
    roberth
  ];
}
