at 25.11-pre 7.1 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 cfg = config.services.spiped; 12in 13{ 14 options = { 15 services.spiped = { 16 enable = mkOption { 17 type = types.bool; 18 default = false; 19 description = "Enable the spiped service module."; 20 }; 21 22 config = mkOption { 23 type = types.attrsOf ( 24 types.submodule ({ 25 options = { 26 encrypt = mkOption { 27 type = types.bool; 28 default = false; 29 description = '' 30 Take unencrypted connections from the 31 `source` socket and send encrypted 32 connections to the `target` socket. 33 ''; 34 }; 35 36 decrypt = mkOption { 37 type = types.bool; 38 default = false; 39 description = '' 40 Take encrypted connections from the 41 `source` socket and send unencrypted 42 connections to the `target` socket. 43 ''; 44 }; 45 46 source = mkOption { 47 type = types.str; 48 description = '' 49 Address on which spiped should listen for incoming 50 connections. Must be in one of the following formats: 51 `/absolute/path/to/unix/socket`, 52 `host.name:port`, 53 `[ip.v4.ad.dr]:port` or 54 `[ipv6::addr]:port` - note that 55 hostnames are resolved when spiped is launched and are 56 not re-resolved later; thus if DNS entries change 57 spiped will continue to connect to the expired 58 address. 59 ''; 60 }; 61 62 target = mkOption { 63 type = types.str; 64 description = "Address to which spiped should connect."; 65 }; 66 67 keyfile = mkOption { 68 type = types.path; 69 description = '' 70 Name of a file containing the spiped key. 71 As the daemon runs as the `spiped` user, 72 the key file must be readable by that user. 73 To securely manage the file within your configuration 74 consider a tool such as agenix or sops-nix. 75 ''; 76 }; 77 78 timeout = mkOption { 79 type = types.int; 80 default = 5; 81 description = '' 82 Timeout, in seconds, after which an attempt to connect to 83 the target or a protocol handshake will be aborted (and the 84 connection dropped) if not completed 85 ''; 86 }; 87 88 maxConns = mkOption { 89 type = types.int; 90 default = 100; 91 description = '' 92 Limit on the number of simultaneous connections allowed. 93 ''; 94 }; 95 96 waitForDNS = mkOption { 97 type = types.bool; 98 default = false; 99 description = '' 100 Wait for DNS. Normally when `spiped` is 101 launched it resolves addresses and binds to its source 102 socket before the parent process returns; with this option 103 it will daemonize first and retry failed DNS lookups until 104 they succeed. This allows `spiped` to 105 launch even if DNS isn't set up yet, but at the expense of 106 losing the guarantee that once `spiped` has 107 finished launching it will be ready to create pipes. 108 ''; 109 }; 110 111 disableKeepalives = mkOption { 112 type = types.bool; 113 default = false; 114 description = "Disable transport layer keep-alives."; 115 }; 116 117 weakHandshake = mkOption { 118 type = types.bool; 119 default = false; 120 description = '' 121 Use fast/weak handshaking: This reduces the CPU time spent 122 in the initial connection setup, at the expense of losing 123 perfect forward secrecy. 124 ''; 125 }; 126 127 resolveRefresh = mkOption { 128 type = types.int; 129 default = 60; 130 description = '' 131 Resolution refresh time for the target socket, in seconds. 132 ''; 133 }; 134 135 disableReresolution = mkOption { 136 type = types.bool; 137 default = false; 138 description = "Disable target address re-resolution."; 139 }; 140 }; 141 }) 142 ); 143 144 default = { }; 145 146 example = literalExpression '' 147 { 148 pipe1 = 149 { keyfile = "/var/lib/spiped/pipe1.key"; 150 encrypt = true; 151 source = "localhost:6000"; 152 target = "endpoint.example.com:7000"; 153 }; 154 pipe2 = 155 { keyfile = "/var/lib/spiped/pipe2.key"; 156 decrypt = true; 157 source = "0.0.0.0:7000"; 158 target = "localhost:3000"; 159 }; 160 } 161 ''; 162 163 description = '' 164 Configuration for a secure pipe daemon. The daemon can be 165 started, stopped, or examined using 166 `systemctl`, under the name 167 `spiped@foo`. 168 ''; 169 }; 170 }; 171 }; 172 173 config = mkIf cfg.enable { 174 assertions = mapAttrsToList (name: c: { 175 assertion = (c.encrypt -> !c.decrypt) || (c.decrypt -> c.encrypt); 176 message = "A pipe must either encrypt or decrypt"; 177 }) cfg.config; 178 179 users.groups.spiped.gid = config.ids.gids.spiped; 180 users.users.spiped = { 181 description = "Secure Pipe Service user"; 182 group = "spiped"; 183 uid = config.ids.uids.spiped; 184 }; 185 186 systemd.services."spiped@" = { 187 description = "Secure pipe '%i'"; 188 after = [ "network.target" ]; 189 190 serviceConfig = { 191 Restart = "always"; 192 User = "spiped"; 193 }; 194 stopIfChanged = false; 195 196 scriptArgs = "%i"; 197 script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`"; 198 }; 199 200 # Setup spiped config files 201 environment.etc = mapAttrs' ( 202 name: cfg: 203 nameValuePair "spiped/${name}.spec" { 204 text = concatStringsSep " " [ 205 (if cfg.encrypt then "-e" else "-d") # Mode 206 "-s ${cfg.source}" # Source 207 "-t ${cfg.target}" # Target 208 "-k ${cfg.keyfile}" # Keyfile 209 "-n ${toString cfg.maxConns}" # Max number of conns 210 "-o ${toString cfg.timeout}" # Timeout 211 (optionalString cfg.waitForDNS "-D") # Wait for DNS 212 (optionalString cfg.weakHandshake "-f") # No PFS 213 (optionalString cfg.disableKeepalives "-j") # Keepalives 214 (if cfg.disableReresolution then "-R" else "-r ${toString cfg.resolveRefresh}") 215 ]; 216 } 217 ) cfg.config; 218 }; 219}