From be6463cd9d7bbdd6e9cde0660c7bdb98e71befe8 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Wed, 22 Jul 2020 10:34:57 +0200 Subject: [PATCH] nixos/croc: init --- nixos/modules/module-list.nix | 1 + nixos/modules/services/networking/croc.nix | 88 ++++++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/croc.nix | 51 +++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 nixos/modules/services/networking/croc.nix create mode 100644 nixos/tests/croc.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 3055459e781..fca0979dd34 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -632,6 +632,7 @@ ./services/networking/coredns.nix ./services/networking/corerad.nix ./services/networking/coturn.nix + ./services/networking/croc.nix ./services/networking/dante.nix ./services/networking/ddclient.nix ./services/networking/dhcpcd.nix diff --git a/nixos/modules/services/networking/croc.nix b/nixos/modules/services/networking/croc.nix new file mode 100644 index 00000000000..b218fab2196 --- /dev/null +++ b/nixos/modules/services/networking/croc.nix @@ -0,0 +1,88 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) types; + cfg = config.services.croc; + rootDir = "/run/croc"; +in +{ + options.services.croc = { + enable = lib.mkEnableOption "croc relay"; + ports = lib.mkOption { + type = with types; listOf port; + default = [9009 9010 9011 9012 9013]; + description = "Ports of the relay."; + }; + pass = lib.mkOption { + type = with types; either path str; + default = "pass123"; + description = "Password or passwordfile for the relay."; + }; + openFirewall = lib.mkEnableOption "opening of the peer port(s) in the firewall"; + debug = lib.mkEnableOption "debug logs"; + }; + + config = lib.mkIf cfg.enable { + systemd.services.croc = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.croc}/bin/croc --pass '${cfg.pass}' ${lib.optionalString cfg.debug "--debug"} relay --ports ${lib.concatMapStringsSep "," toString cfg.ports}"; + # The following options are only for optimizing: + # systemd-analyze security croc + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DynamicUser = true; + # ProtectClock= adds DeviceAllow=char-rtc r + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + MountAPIVFS = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateNetwork = lib.mkDefault false; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = rootDir; + # Avoid mounting rootDir in the own rootDir of ExecStart='s mount namespace. + InaccessiblePaths = [ "-+${rootDir}" ]; + BindReadOnlyPaths = [ + builtins.storeDir + ] ++ lib.optional (types.path.check cfg.pass) cfg.pass; + # This is for BindReadOnlyPaths= + # to allow traversal of directories they create in RootDirectory=. + UMask = "0066"; + # Create rootDir in the host's mount namespace. + RuntimeDirectory = [(baseNameOf rootDir)]; + RuntimeDirectoryMode = "700"; + SystemCallFilter = [ + "@system-service" + "~@aio" "~@chown" "~@keyring" "~@memlock" + "~@privileged" "~@resources" "~@setuid" + "~@sync" "~@timer" + ]; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + }; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall cfg.ports; + }; + + meta.maintainers = with lib.maintainers; [ hax404 julm ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index c31a20e5408..c9b817ec069 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -82,6 +82,7 @@ in couchdb = handleTest ./couchdb.nix {}; cri-o = handleTestOn ["x86_64-linux"] ./cri-o.nix {}; custom-ca = handleTest ./custom-ca.nix {}; + croc = handleTest ./croc.nix {}; deluge = handleTest ./deluge.nix {}; dhparams = handleTest ./dhparams.nix {}; dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {}; diff --git a/nixos/tests/croc.nix b/nixos/tests/croc.nix new file mode 100644 index 00000000000..c1b6fc7232d --- /dev/null +++ b/nixos/tests/croc.nix @@ -0,0 +1,51 @@ +import ./make-test-python.nix ({ pkgs, ... }: +let + client = { pkgs, ... }: { + environment.systemPackages = [ pkgs.croc ]; + }; + pass = pkgs.writeText "pass" "PassRelay"; +in { + name = "croc"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ hax404 julm ]; + }; + + nodes = { + relay = { + services.croc = { + enable = true; + pass = pass; + openFirewall = true; + }; + }; + sender = client; + receiver = client; + }; + + testScript = '' + start_all() + + # wait until relay is up + relay.wait_for_unit("croc") + relay.wait_for_open_port(9009) + relay.wait_for_open_port(9010) + relay.wait_for_open_port(9011) + relay.wait_for_open_port(9012) + relay.wait_for_open_port(9013) + + # generate testfiles and send them + sender.wait_for_unit("multi-user.target") + sender.execute("echo Hello World > testfile01.txt") + sender.execute("echo Hello Earth > testfile02.txt") + sender.execute( + "croc --pass ${pass} --relay relay send --code topSecret testfile01.txt testfile02.txt &" + ) + + # receive the testfiles and check them + receiver.succeed( + "croc --pass ${pass} --yes --relay relay topSecret" + ) + assert "Hello World" in receiver.succeed("cat testfile01.txt") + assert "Hello Earth" in receiver.succeed("cat testfile02.txt") + ''; +})