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