diff --git a/nixos/modules/misc/documentation/modular-services.nix b/nixos/modules/misc/documentation/modular-services.nix index c43f95118169..edc60d3bb592 100644 --- a/nixos/modules/misc/documentation/modular-services.nix +++ b/nixos/modules/misc/documentation/modular-services.nix @@ -20,6 +20,7 @@ let modularServicesModule = { options = { "" = fakeSubmodule pkgs.ghostunnel.services.default; + "" = fakeSubmodule pkgs.ktls-utils.services.default; "" = fakeSubmodule pkgs.php.services.default; "" = fakeSubmodule pkgs.snid.services.default; }; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 9a059c30563c..d4883002bb3e 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -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; diff --git a/nixos/tests/tlshd.nix b/nixos/tests/tlshd.nix new file mode 100644 index 000000000000..ee61b3f0bc72 --- /dev/null +++ b/nixos/tests/tlshd.nix @@ -0,0 +1,93 @@ +{ 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 ]; +} diff --git a/pkgs/by-name/kt/ktls-utils/package.nix b/pkgs/by-name/kt/ktls-utils/package.nix index 8820b61c3471..f11dfffa4f47 100644 --- a/pkgs/by-name/kt/ktls-utils/package.nix +++ b/pkgs/by-name/kt/ktls-utils/package.nix @@ -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"; diff --git a/pkgs/by-name/kt/ktls-utils/service.nix b/pkgs/by-name/kt/ktls-utils/service.nix new file mode 100644 index 000000000000..c755a004e6fd --- /dev/null +++ b/pkgs/by-name/kt/ktls-utils/service.nix @@ -0,0 +1,75 @@ +# 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" ]; + }; + }; + }; +}