From fb3723fe52894c49bd4b2fb92244a289d4124b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Mon, 16 Oct 2023 13:10:15 +0200 Subject: [PATCH] nixos/tang: create module for tang server (#247037) This commit adds a module for the tang server and the related nixos test. --- nixos/modules/module-list.nix | 1 + nixos/modules/services/security/tang.nix | 95 ++++++++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/tang.nix | 81 ++++++++++++++++++++ pkgs/servers/tang/default.nix | 13 +++- 5 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 nixos/modules/services/security/tang.nix create mode 100644 nixos/tests/tang.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index aae7069fa5f8..3e5e4e4cc8c5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1164,6 +1164,7 @@ ./services/security/sshguard.nix ./services/security/sslmate-agent.nix ./services/security/step-ca.nix + ./services/security/tang.nix ./services/security/tor.nix ./services/security/torify.nix ./services/security/torsocks.nix diff --git a/nixos/modules/services/security/tang.nix b/nixos/modules/services/security/tang.nix new file mode 100644 index 000000000000..9cb0a22fca42 --- /dev/null +++ b/nixos/modules/services/security/tang.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.tang; +in +{ + options.services.tang = { + enable = mkEnableOption "tang"; + + package = mkOption { + type = types.package; + default = pkgs.tang; + defaultText = literalExpression "pkgs.tang"; + description = mdDoc "The tang package to use."; + }; + + listenStream = mkOption { + type = with types; listOf str; + default = [ "7654" ]; + example = [ "198.168.100.1:7654" "[2001:db8::1]:7654" "7654" ]; + description = mdDoc '' + Addresses and/or ports on which tang should listen. + For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`. + ''; + }; + + ipAddressAllow = mkOption { + example = [ "192.168.1.0/24" ]; + type = types.listOf types.str; + description = '' + Whitelist a list of address prefixes. + Preferably, internal addresses should be used. + ''; + }; + + }; + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + systemd.services."tangd@" = { + description = "Tang server"; + path = [ cfg.package ]; + serviceConfig = { + StandardInput = "socket"; + StandardOutput = "socket"; + StandardError = "journal"; + DynamicUser = true; + StateDirectory = "tang"; + RuntimeDirectory = "tang"; + StateDirectoryMode = "700"; + UMask = "0077"; + CapabilityBoundingSet = [ "" ]; + ExecStart = "${cfg.package}/libexec/tangd %S/tang"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + DeviceAllow = [ "/dev/stdin" ]; + RestrictAddressFamilies = [ "AF_UNIX" ]; + DevicePolicy = "strict"; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + IPAddressDeny = "any"; + IPAddressAllow = cfg.ipAddressAllow; + }; + }; + + systemd.sockets.tangd = { + description = "Tang server"; + wantedBy = [ "sockets.target" ]; + socketConfig = { + ListenStream = cfg.listenStream; + Accept = "yes"; + IPAddressDeny = "any"; + IPAddressAllow = cfg.ipAddressAllow; + }; + }; + }; + meta.maintainers = with lib.maintainers; [ jfroche julienmalka ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index dac3e1d6973d..1ac65df44fd1 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -807,6 +807,7 @@ in { systemd-userdbd = handleTest ./systemd-userdbd.nix {}; systemd-homed = handleTest ./systemd-homed.nix {}; tandoor-recipes = handleTest ./tandoor-recipes.nix {}; + tang = handleTest ./tang.nix {}; taskserver = handleTest ./taskserver.nix {}; tayga = handleTest ./tayga.nix {}; teeworlds = handleTest ./teeworlds.nix {}; diff --git a/nixos/tests/tang.nix b/nixos/tests/tang.nix new file mode 100644 index 000000000000..10486a9feb8c --- /dev/null +++ b/nixos/tests/tang.nix @@ -0,0 +1,81 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "tang"; + meta = with pkgs.lib.maintainers; { + maintainers = [ jfroche ]; + }; + + nodes.server = + { config + , pkgs + , modulesPath + , ... + }: { + imports = [ + "${modulesPath}/../tests/common/auto-format-root-device.nix" + ]; + virtualisation = { + emptyDiskImages = [ 512 ]; + useBootLoader = true; + useEFIBoot = true; + # This requires to have access + # to a host Nix store as + # the new root device is /dev/vdb + # an empty 512MiB drive, containing no Nix store. + mountHostNixStore = true; + }; + + boot.loader.systemd-boot.enable = true; + + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.1"; prefixLength = 24; } + ]; + + environment.systemPackages = with pkgs; [ clevis tang cryptsetup ]; + services.tang = { + enable = true; + ipAddressAllow = [ "127.0.0.1/32" ]; + }; + }; + testScript = '' + start_all() + machine.wait_for_unit("sockets.target") + + with subtest("Check keys are generated"): + machine.wait_until_succeeds("curl -v http://127.0.0.1:7654/adv") + key = machine.wait_until_succeeds("tang-show-keys 7654") + + with subtest("Check systemd access list"): + machine.succeed("ping -c 3 192.168.0.1") + machine.fail("curl -v --connect-timeout 3 http://192.168.0.1:7654/adv") + + with subtest("Check basic encrypt and decrypt message"): + machine.wait_until_succeeds(f"""echo 'Hello World' | clevis encrypt tang '{{ "url": "http://127.0.0.1:7654", "thp":"{key}"}}' > /tmp/encrypted""") + decrypted = machine.wait_until_succeeds("clevis decrypt < /tmp/encrypted") + assert decrypted.strip() == "Hello World" + machine.wait_until_succeeds("tang-show-keys 7654") + + with subtest("Check encrypt and decrypt disk"): + machine.succeed("cryptsetup luksFormat --force-password --batch-mode /dev/vdb <<<'password'") + machine.succeed(f"""clevis luks bind -s1 -y -f -d /dev/vdb tang '{{ "url": "http://127.0.0.1:7654", "thp":"{key}" }}' <<< 'password' """) + clevis_luks = machine.succeed("clevis luks list -d /dev/vdb") + assert clevis_luks.strip() == """1: tang '{"url":"http://127.0.0.1:7654"}'""" + machine.succeed("clevis luks unlock -d /dev/vdb") + machine.succeed("find /dev/mapper -name 'luks*' -exec cryptsetup close {} +") + machine.succeed("clevis luks unlock -d /dev/vdb") + machine.succeed("find /dev/mapper -name 'luks*' -exec cryptsetup close {} +") + # without tang available, unlock should fail + machine.succeed("systemctl stop tangd.socket") + machine.fail("clevis luks unlock -d /dev/vdb") + machine.succeed("systemctl start tangd.socket") + + with subtest("Rotate server keys"): + machine.succeed("${pkgs.tang}/libexec/tangd-rotate-keys -d /var/lib/tang") + machine.succeed("clevis luks unlock -d /dev/vdb") + machine.succeed("find /dev/mapper -name 'luks*' -exec cryptsetup close {} +") + + with subtest("Test systemd service security"): + output = machine.succeed("systemd-analyze security tangd@.service") + machine.log(output) + assert output[-9:-1] == "SAFE :-}" + ''; +}) diff --git a/pkgs/servers/tang/default.nix b/pkgs/servers/tang/default.nix index 227daa2cfb9f..4206973dbd15 100644 --- a/pkgs/servers/tang/default.nix +++ b/pkgs/servers/tang/default.nix @@ -13,6 +13,7 @@ , testers , tang , gitUpdater +, nixosTests }: stdenv.mkDerivation rec { @@ -53,10 +54,13 @@ stdenv.mkDerivation rec { ''; passthru = { - tests.version = testers.testVersion { - package = tang; - command = "${tang}/libexec/tangd --version"; - version = "tangd ${version}"; + tests = { + inherit (nixosTests) tang; + version = testers.testVersion { + package = tang; + command = "${tang}/libexec/tangd --version"; + version = "tangd ${version}"; + }; }; updateScript = gitUpdater { }; }; @@ -67,5 +71,6 @@ stdenv.mkDerivation rec { changelog = "https://github.com/latchset/tang/releases/tag/v${version}"; maintainers = with lib.maintainers; [ fpletz ]; license = lib.licenses.gpl3Plus; + mainProgram = "tangd"; }; }