oxipng: re-enable tests (#375349)
[NixPkgs.git] / nixos / modules / services / networking / rathole.nix
blobb6cd3ff89d9cf4536e43f13cf51db5985f3b3454
2   pkgs,
3   lib,
4   config,
5   ...
6 }:
8 let
9   cfg = config.services.rathole;
10   settingsFormat = pkgs.formats.toml { };
11   py-toml-merge =
12     pkgs.writers.writePython3Bin "py-toml-merge"
13       {
14         libraries = with pkgs.python3Packages; [
15           tomli-w
16           mergedeep
17         ];
18       }
19       ''
20         import argparse
21         from pathlib import Path
22         from typing import Any
24         import tomli_w
25         import tomllib
26         from mergedeep import merge
28         parser = argparse.ArgumentParser(description="Merge multiple TOML files")
29         parser.add_argument(
30             "files",
31             type=Path,
32             nargs="+",
33             help="List of TOML files to merge",
34         )
36         args = parser.parse_args()
37         merged: dict[str, Any] = {}
39         for file in args.files:
40             with open(file, "rb") as fh:
41                 loaded_toml = tomllib.load(fh)
42                 merged = merge(merged, loaded_toml)
44         print(tomli_w.dumps(merged))
45       '';
49   options = {
50     services.rathole = {
51       enable = lib.mkEnableOption "Rathole";
53       package = lib.mkPackageOption pkgs "rathole" { };
55       role = lib.mkOption {
56         type = lib.types.enum [
57           "server"
58           "client"
59         ];
60         description = ''
61           Select whether rathole needs to be run as a `client` or a `server`.
62           Server is a machine with a public IP and client is a device behind NAT,
63           but running some services that need to be exposed to the Internet.
64         '';
65       };
67       credentialsFile = lib.mkOption {
68         type = lib.types.path;
69         default = "/dev/null";
70         description = ''
71           Path to a TOML file to be merged with the settings.
72           Useful to set secret config parameters like tokens, which
73           should not appear in the Nix Store.
74         '';
75         example = "/var/lib/secrets/rathole/config.toml";
76       };
78       settings = lib.mkOption {
79         type = settingsFormat.type;
80         default = { };
81         description = ''
82           Rathole configuration, for options reference
83           see the [example](https://github.com/rapiz1/rathole?tab=readme-ov-file#configuration) on GitHub.
84           Both server and client configurations can be specified at the same time, regardless of the selected role.
85         '';
86         example = {
87           server = {
88             bind_addr = "0.0.0.0:2333";
89             services.my_nas_ssh = {
90               token = "use_a_secret_that_only_you_know";
91               bind_addr = "0.0.0.0:5202";
92             };
93           };
94         };
95       };
96     };
97   };
99   config = lib.mkIf cfg.enable {
100     systemd.services.rathole = {
101       requires = [ "network.target" ];
102       after = [ "network.target" ];
103       wantedBy = [ "multi-user.target" ];
104       description = "Rathole ${cfg.role} Service";
106       serviceConfig =
107         let
108           name = "rathole";
109           configFile = settingsFormat.generate "${name}.toml" cfg.settings;
110           runtimeDir = "/run/${name}";
111           ratholePrestart =
112             "+"
113             + (pkgs.writeShellScript "rathole-prestart" ''
114               DYNUSER_UID=$(stat -c %u ${runtimeDir})
115               DYNUSER_GID=$(stat -c %g ${runtimeDir})
116               ${lib.getExe py-toml-merge} ${configFile} '${cfg.credentialsFile}' |
117                 install -m 600 -o $DYNUSER_UID -g $DYNUSER_GID /dev/stdin ${runtimeDir}/${mergedConfigName}
118             '');
119           mergedConfigName = "merged.toml";
120         in
121         {
122           Type = "simple";
123           Restart = "on-failure";
124           RestartSec = 5;
125           ExecStartPre = ratholePrestart;
126           ExecStart = "${lib.getExe cfg.package} --${cfg.role} ${runtimeDir}/${mergedConfigName}";
127           DynamicUser = true;
128           LimitNOFILE = "1048576";
129           RuntimeDirectory = name;
130           RuntimeDirectoryMode = "0700";
131           # Hardening
132           AmbientCapabilities = "CAP_NET_BIND_SERVICE";
133           CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
134           LockPersonality = true;
135           MemoryDenyWriteExecute = true;
136           PrivateDevices = true;
137           PrivateMounts = true;
138           PrivateTmp = true;
139           # PrivateUsers=true breaks AmbientCapabilities=CAP_NET_BIND_SERVICE
140           ProcSubset = "pid";
141           ProtectClock = true;
142           ProtectControlGroups = true;
143           ProtectHome = true;
144           ProtectHostname = true;
145           ProtectKernelLogs = true;
146           ProtectKernelModules = true;
147           ProtectKernelTunables = true;
148           ProtectProc = "invisible";
149           ProtectSystem = "strict";
150           RemoveIPC = true;
151           RestrictAddressFamilies = [
152             "AF_INET"
153             "AF_INET6"
154           ];
155           RestrictNamespaces = true;
156           RestrictRealtime = true;
157           RestrictSUIDSGID = true;
158           SystemCallArchitectures = "native";
159           UMask = "0066";
160         };
161     };
162   };
164   meta.maintainers = with lib.maintainers; [ xokdvium ];