nixos/stunnel: add module (#33151)

authored by Leon Schuermann and committed by Jörg Thalheim 04c4c010 f0401a23

+222
+1
nixos/modules/module-list.nix
··· 540 540 ./services/networking/ssh/lshd.nix 541 541 ./services/networking/ssh/sshd.nix 542 542 ./services/networking/strongswan.nix 543 + ./services/networking/stunnel.nix 543 544 ./services/networking/supplicant.nix 544 545 ./services/networking/supybot.nix 545 546 ./services/networking/syncthing.nix
+221
nixos/modules/services/networking/stunnel.nix
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + with lib; 4 + 5 + let 6 + 7 + cfg = config.services.stunnel; 8 + yesNo = val: if val then "yes" else "no"; 9 + 10 + verifyChainPathAssert = n: c: { 11 + assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer); 12 + message = "stunnel: \"${n}\" client configuration - hostname verification " + 13 + "is not possible without either verifyChain or verifyPeer enabled"; 14 + }; 15 + 16 + serverConfig = { 17 + options = { 18 + accept = mkOption { 19 + type = types.int; 20 + description = "On which port stunnel should listen for incoming TLS connections."; 21 + }; 22 + 23 + connect = mkOption { 24 + type = types.int; 25 + description = "To which port the decrypted connection should be forwarded."; 26 + }; 27 + 28 + cert = mkOption { 29 + type = types.path; 30 + description = "File containing both the private and public keys."; 31 + }; 32 + }; 33 + }; 34 + 35 + clientConfig = { 36 + options = { 37 + accept = mkOption { 38 + type = types.string; 39 + description = "IP:Port on which connections should be accepted."; 40 + }; 41 + 42 + connect = mkOption { 43 + type = types.string; 44 + description = "IP:Port destination to connect to."; 45 + }; 46 + 47 + verifyChain = mkOption { 48 + type = types.bool; 49 + default = true; 50 + description = "Check if the provided certificate has a valid certificate chain (against CAPath)."; 51 + }; 52 + 53 + verifyPeer = mkOption { 54 + type = types.bool; 55 + default = false; 56 + description = "Check if the provided certificate is contained in CAPath."; 57 + }; 58 + 59 + CAPath = mkOption { 60 + type = types.path; 61 + default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; 62 + description = "Path to a file containing certificates to validate against."; 63 + }; 64 + 65 + verifyHostname = mkOption { 66 + type = with types; nullOr string; 67 + default = null; 68 + description = "If set, stunnel checks if the provided certificate is valid for the given hostname."; 69 + }; 70 + }; 71 + }; 72 + 73 + 74 + in 75 + 76 + { 77 + 78 + ###### interface 79 + 80 + options = { 81 + 82 + services.stunnel = { 83 + 84 + enable = mkOption { 85 + type = types.bool; 86 + default = false; 87 + description = "Whether to enable the stunnel TLS tunneling service."; 88 + }; 89 + 90 + user = mkOption { 91 + type = with types; nullOr string; 92 + default = "nobody"; 93 + description = "The user under which stunnel runs."; 94 + }; 95 + 96 + group = mkOption { 97 + type = with types; nullOr string; 98 + default = "nogroup"; 99 + description = "The group under which stunnel runs."; 100 + }; 101 + 102 + logLevel = mkOption { 103 + type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ]; 104 + default = "info"; 105 + description = "Verbosity of stunnel output."; 106 + }; 107 + 108 + fipsMode = mkOption { 109 + type = types.bool; 110 + default = false; 111 + description = "Enable FIPS 140-2 mode required for compliance."; 112 + }; 113 + 114 + enableInsecureSSLv3 = mkOption { 115 + type = types.bool; 116 + default = false; 117 + description = "Enable support for the insecure SSLv3 protocol."; 118 + }; 119 + 120 + 121 + servers = mkOption { 122 + description = "Define the server configuations."; 123 + type = with types; attrsOf (submodule serverConfig); 124 + example = { 125 + fancyWebserver = { 126 + enable = true; 127 + accept = 443; 128 + connect = 8080; 129 + cert = "/path/to/pem/file"; 130 + }; 131 + }; 132 + default = { }; 133 + }; 134 + 135 + clients = mkOption { 136 + description = "Define the client configurations."; 137 + type = with types; attrsOf (submodule clientConfig); 138 + example = { 139 + foobar = { 140 + accept = "0.0.0.0:8080"; 141 + connect = "nixos.org:443"; 142 + verifyChain = false; 143 + }; 144 + }; 145 + default = { }; 146 + }; 147 + }; 148 + }; 149 + 150 + 151 + ###### implementation 152 + 153 + config = mkIf cfg.enable { 154 + 155 + assertions = concatLists [ 156 + (singleton { 157 + assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0); 158 + message = "stunnel: At least one server- or client-configuration has to be present."; 159 + }) 160 + 161 + (mapAttrsToList verifyChainPathAssert cfg.clients) 162 + ]; 163 + 164 + environment.systemPackages = [ pkgs.stunnel ]; 165 + 166 + environment.etc."stunnel.cfg".text = '' 167 + ${ if cfg.user != null then "setuid = ${cfg.user}" else "" } 168 + ${ if cfg.group != null then "setgid = ${cfg.group}" else "" } 169 + 170 + debug = ${cfg.logLevel} 171 + 172 + ${ optionalString cfg.fipsMode "fips = yes" } 173 + ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" } 174 + 175 + ; ----- SERVER CONFIGURATIONS ----- 176 + ${ lib.concatStringsSep "\n" 177 + (lib.mapAttrsToList 178 + (n: v: '' 179 + [${n}] 180 + accept = ${toString v.accept} 181 + connect = ${toString v.connect} 182 + cert = ${v.cert} 183 + 184 + '') 185 + cfg.servers) 186 + } 187 + 188 + ; ----- CLIENT CONFIGURATIONS ----- 189 + ${ lib.concatStringsSep "\n" 190 + (lib.mapAttrsToList 191 + (n: v: '' 192 + [${n}] 193 + client = yes 194 + accept = ${v.accept} 195 + connect = ${v.connect} 196 + verifyChain = ${yesNo v.verifyChain} 197 + verifyPeer = ${yesNo v.verifyPeer} 198 + ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"} 199 + ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"} 200 + OCSPaia = yes 201 + 202 + '') 203 + cfg.clients) 204 + } 205 + ''; 206 + 207 + systemd.services.stunnel = { 208 + description = "stunnel TLS tunneling service"; 209 + after = [ "network.target" ]; 210 + wants = [ "network.target" ]; 211 + wantedBy = [ "multi-user.target" ]; 212 + restartTriggers = [ config.environment.etc."stunnel.cfg".source ]; 213 + serviceConfig = { 214 + ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}"; 215 + Type = "forking"; 216 + }; 217 + }; 218 + 219 + }; 220 + 221 + }