Commit 55efd062 authored by Tom Fitzhenry's avatar Tom Fitzhenry
Browse files

tlshd: add modular service and test

parent 4eb3c802
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ let
  modularServicesModule = {
    options = {
      "<imports = [ pkgs.ghostunnel.services.default ]>" = fakeSubmodule pkgs.ghostunnel.services.default;
      "<imports = [ pkgs.ktls-utils.services.default ]>" = fakeSubmodule pkgs.ktls-utils.services.default;
      "<imports = [ pkgs.php.services.default ]>" = fakeSubmodule pkgs.php.services.default;
      "<imports = [ pkgs.snid.services.default ]>" = fakeSubmodule pkgs.snid.services.default;
    };
+1 −0
Original line number Diff line number Diff line
@@ -1655,6 +1655,7 @@ in
  tinydns = runTest ./tinydns.nix;
  tinyproxy = runTest ./tinyproxy.nix;
  tinywl = runTest ./tinywl.nix;
  tlshd = runTest ./tlshd.nix;
  tlsrpt = runTest ./tlsrpt.nix;
  tmate-ssh-server = runTest ./tmate-ssh-server.nix;
  tomcat = runTest ./tomcat.nix;

nixos/tests/tlshd.nix

0 → 100644
+93 −0
Original line number Diff line number Diff line
{ lib, pkgs, ... }:
let
  runWithOpenSSL =
    name: cmd:
    pkgs.runCommand name {
      buildInputs = [ pkgs.openssl ];
    } cmd;

  caKey = runWithOpenSSL "ca.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out";
  caCert = runWithOpenSSL "ca.crt" ''
    openssl req -new -x509 -sha256 -key ${caKey} -out $out -subj "/CN=Test CA" -days 36500
  '';

  serverKey = runWithOpenSSL "server.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out";
  serverCert = runWithOpenSSL "server.crt" ''
    openssl req -new -sha256 -key ${serverKey} -out server.csr -subj "/CN=server"
    openssl x509 -req -in server.csr -CA ${caCert} -CAkey ${caKey} \
      -CAcreateserial -out $out -days 36500 -sha256
  '';

  clientKey = runWithOpenSSL "client.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out";
  clientCert = runWithOpenSSL "client.crt" ''
    openssl req -new -sha256 -key ${clientKey} -out client.csr -subj "/CN=client"
    openssl x509 -req -in client.csr -CA ${caCert} -CAkey ${caKey} \
      -CAcreateserial -out $out -days 36500 -sha256
  '';
in
{
  _class = "nixosTest";
  name = "tlshd";

  nodes = {
    server =
      { pkgs, ... }:
      {
        system.services.tlshd = {
          imports = [ pkgs.ktls-utils.services.default ];
          tlshd.settings = {
            "authenticate.server" = {
              "x509.certificate" = toString serverCert;
              "x509.private_key" = toString serverKey;
              "x509.truststore" = toString caCert;
            };
          };
        };

        services.nfs.server = {
          enable = true;
          exports = ''
            /export 192.168.1.0/24(rw,no_root_squash,no_subtree_check,xprtsec=mtls)
          '';
          createMountPoints = true;
        };

        networking.firewall.enable = false;
      };

    client =
      { pkgs, ... }:
      {
        system.services.tlshd = {
          imports = [ pkgs.ktls-utils.services.default ];
          tlshd.settings = {
            "authenticate.client" = {
              "x509.certificate" = toString clientCert;
              "x509.private_key" = toString clientKey;
              "x509.truststore" = toString caCert;
            };
          };
        };

        virtualisation.fileSystems."/mnt/nfs" = {
          device = "server:/export";
          fsType = "nfs";
          options = [ "xprtsec=mtls" ];
        };

        networking.firewall.enable = false;
      };
  };

  testScript = ''
    start_all()
    server.wait_for_unit("nfs-server.service")
    server.wait_for_unit("tlshd.service")
    client.wait_for_unit("tlshd.service")
    client.wait_for_unit("mnt-nfs.mount")
    client.wait_until_succeeds("echo 'hello from client' > /mnt/nfs/test.txt")
    server.wait_until_succeeds("grep 'hello from client' /export/test.txt")
  '';

  meta.maintainers = with lib.maintainers; [ tomfitzhenry ];
}
+9 −1
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
  systemd,
  withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
  nix-update-script,
  nixosTests,
}:

stdenv.mkDerivation (finalAttrs: {
@@ -47,7 +48,14 @@ stdenv.mkDerivation (finalAttrs: {

  doCheck = true;

  passthru.updateScript = nix-update-script { };
  passthru = {
    updateScript = nix-update-script { };
    tests.nixos = nixosTests.tlshd;
    services.default = {
      imports = [ (lib.modules.importApply ./service.nix { }) ];
      tlshd.package = finalAttrs.finalPackage;
    };
  };

  meta = {
    description = "TLS handshake utilities for in-kernel TLS consumers";
+75 −0
Original line number Diff line number Diff line
# Non-module dependencies (`importApply`)
{ }:

# Service module
{
  lib,
  config,
  options,
  ...
}:
let
  inherit (lib)
    getExe
    mkOption
    types
    ;
  cfg = config.tlshd;

  configFile = config.configData."tlshd.conf".path;
in
{
  # https://nixos.org/manual/nixos/unstable/#modular-services
  _class = "service";

  options.tlshd = {
    package = mkOption {
      description = "Package to use for tlshd.";
      defaultText = lib.literalMD "The `ktls-utils` package that provided this module.";
      type = types.package;
    };

    settings = mkOption {
      description = ''
        Configuration for tlshd in INI format.
        See {manpage}`tlshd.conf(5)` for available options.
      '';
      type = types.attrsOf (types.attrsOf types.str);
      default = { };
      example = lib.literalExpression ''
        {
          "authenticate.server" = {
            "x509.certificate" = "/var/lib/tlshd/cert.pem";
            "x509.private_key" = "/var/lib/tlshd/key.pem";
            "x509.truststore" = "/var/lib/tlshd/truststore.pem";
          };
        }
      '';
    };
  };

  config = {
    configData."tlshd.conf".text = lib.generators.toINI { } cfg.settings;

    process.argv = [
      (getExe cfg.package)
      "--config"
      configFile
    ];
  }
  // lib.optionalAttrs (options ? systemd) {
    systemd.service = {
      description = "Handshake service for kernel TLS consumers";
      documentation = [ "man:tlshd(8)" ];
      unitConfig.DefaultDependencies = false;
      before = [ "remote-fs-pre.target" ];
      wantedBy = [ "remote-fs.target" ];
      serviceConfig = {
        Restart = "on-failure";
        DynamicUser = true;
        AmbientCapabilities = [ "CAP_NET_ADMIN" ];
        CapabilityBoundingSet = [ "CAP_NET_ADMIN" ];
      };
    };
  };
}