1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 inherit (lib) types;
9 cfg = config.services.croc;
10 rootDir = "/run/croc";
11in
12{
13 options.services.croc = {
14 enable = lib.mkEnableOption "croc relay";
15 ports = lib.mkOption {
16 type = with types; listOf port;
17 default = [
18 9009
19 9010
20 9011
21 9012
22 9013
23 ];
24 description = "Ports of the relay.";
25 };
26 pass = lib.mkOption {
27 type = with types; either path str;
28 default = "pass123";
29 description = "Password or passwordfile for the relay.";
30 };
31 openFirewall = lib.mkEnableOption "opening of the peer port(s) in the firewall";
32 debug = lib.mkEnableOption "debug logs";
33 };
34
35 config = lib.mkIf cfg.enable {
36 systemd.services.croc = {
37 after = [ "network.target" ];
38 wantedBy = [ "multi-user.target" ];
39 serviceConfig = {
40 ExecStart = "${pkgs.croc}/bin/croc --pass '${cfg.pass}' ${lib.optionalString cfg.debug "--debug"} relay --ports ${
41 lib.concatMapStringsSep "," toString cfg.ports
42 }";
43 # The following options are only for optimizing:
44 # systemd-analyze security croc
45 AmbientCapabilities = "";
46 CapabilityBoundingSet = "";
47 DynamicUser = true;
48 # ProtectClock= adds DeviceAllow=char-rtc r
49 DeviceAllow = "";
50 LockPersonality = true;
51 MemoryDenyWriteExecute = true;
52 MountAPIVFS = true;
53 NoNewPrivileges = true;
54 PrivateDevices = true;
55 PrivateMounts = true;
56 PrivateNetwork = lib.mkDefault false;
57 PrivateTmp = true;
58 PrivateUsers = true;
59 ProcSubset = "pid";
60 ProtectClock = true;
61 ProtectControlGroups = true;
62 ProtectHome = true;
63 ProtectHostname = true;
64 ProtectKernelLogs = true;
65 ProtectKernelModules = true;
66 ProtectKernelTunables = true;
67 ProtectProc = "invisible";
68 ProtectSystem = "strict";
69 RemoveIPC = true;
70 RestrictAddressFamilies = [
71 "AF_INET"
72 "AF_INET6"
73 ];
74 RestrictNamespaces = true;
75 RestrictRealtime = true;
76 RestrictSUIDSGID = true;
77 RootDirectory = rootDir;
78 # Avoid mounting rootDir in the own rootDir of ExecStart='s mount namespace.
79 InaccessiblePaths = [ "-+${rootDir}" ];
80 BindReadOnlyPaths = [
81 builtins.storeDir
82 ] ++ lib.optional (types.path.check cfg.pass) cfg.pass;
83 # This is for BindReadOnlyPaths=
84 # to allow traversal of directories they create in RootDirectory=.
85 UMask = "0066";
86 # Create rootDir in the host's mount namespace.
87 RuntimeDirectory = [ (baseNameOf rootDir) ];
88 RuntimeDirectoryMode = "700";
89 SystemCallFilter = [
90 "@system-service"
91 "~@aio"
92 "~@keyring"
93 "~@memlock"
94 "~@privileged"
95 "~@setuid"
96 "~@sync"
97 "~@timer"
98 ];
99 SystemCallArchitectures = "native";
100 SystemCallErrorNumber = "EPERM";
101 };
102 };
103
104 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall cfg.ports;
105 };
106
107 meta.maintainers = with lib.maintainers; [
108 hax404
109 julm
110 ];
111}