nixos/tang: create module for tang server (#247037)

This commit adds a module for the tang server and the related nixos test.
This commit is contained in:
Jean-François Roche 2023-10-16 13:10:15 +02:00 committed by GitHub
parent 301949dcf0
commit fb3723fe52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 187 additions and 4 deletions

View file

@ -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

View file

@ -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 ];
}

View file

@ -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 {};

81
nixos/tests/tang.nix Normal file
View file

@ -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 :-}"
'';
})

View file

@ -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";
};
}