at 25.11-pre 5.0 kB view raw
1{ 2 pkgs, 3 lib, 4 config, 5 ... 6}: 7 8let 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 23 24 import tomli_w 25 import tomllib 26 from mergedeep import merge 27 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 ) 35 36 args = parser.parse_args() 37 merged: dict[str, Any] = {} 38 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) 43 44 print(tomli_w.dumps(merged)) 45 ''; 46in 47 48{ 49 options = { 50 services.rathole = { 51 enable = lib.mkEnableOption "Rathole"; 52 53 package = lib.mkPackageOption pkgs "rathole" { }; 54 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 }; 66 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 }; 77 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 }; 98 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"; 105 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 }; 163 164 meta.maintainers = with lib.maintainers; [ xokdvium ]; 165}