aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/services/networking/stunnel.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking/stunnel.nix')
-rw-r--r--nixpkgs/nixos/modules/services/networking/stunnel.nix221
1 files changed, 221 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/stunnel.nix b/nixpkgs/nixos/modules/services/networking/stunnel.nix
new file mode 100644
index 00000000000..cbc899f2b4d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/stunnel.nix
@@ -0,0 +1,221 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.stunnel;
+ yesNo = val: if val then "yes" else "no";
+
+ verifyChainPathAssert = n: c: {
+ assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
+ message = "stunnel: \"${n}\" client configuration - hostname verification " +
+ "is not possible without either verifyChain or verifyPeer enabled";
+ };
+
+ serverConfig = {
+ options = {
+ accept = mkOption {
+ type = types.int;
+ description = "On which port stunnel should listen for incoming TLS connections.";
+ };
+
+ connect = mkOption {
+ type = types.int;
+ description = "To which port the decrypted connection should be forwarded.";
+ };
+
+ cert = mkOption {
+ type = types.path;
+ description = "File containing both the private and public keys.";
+ };
+ };
+ };
+
+ clientConfig = {
+ options = {
+ accept = mkOption {
+ type = types.str;
+ description = "IP:Port on which connections should be accepted.";
+ };
+
+ connect = mkOption {
+ type = types.str;
+ description = "IP:Port destination to connect to.";
+ };
+
+ verifyChain = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Check if the provided certificate has a valid certificate chain (against CAPath).";
+ };
+
+ verifyPeer = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Check if the provided certificate is contained in CAPath.";
+ };
+
+ CAPath = mkOption {
+ type = types.path;
+ default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+ description = "Path to a file containing certificates to validate against.";
+ };
+
+ verifyHostname = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "If set, stunnel checks if the provided certificate is valid for the given hostname.";
+ };
+ };
+ };
+
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ services.stunnel = {
+
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to enable the stunnel TLS tunneling service.";
+ };
+
+ user = mkOption {
+ type = with types; nullOr str;
+ default = "nobody";
+ description = "The user under which stunnel runs.";
+ };
+
+ group = mkOption {
+ type = with types; nullOr str;
+ default = "nogroup";
+ description = "The group under which stunnel runs.";
+ };
+
+ logLevel = mkOption {
+ type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ];
+ default = "info";
+ description = "Verbosity of stunnel output.";
+ };
+
+ fipsMode = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enable FIPS 140-2 mode required for compliance.";
+ };
+
+ enableInsecureSSLv3 = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enable support for the insecure SSLv3 protocol.";
+ };
+
+
+ servers = mkOption {
+ description = "Define the server configuations.";
+ type = with types; attrsOf (submodule serverConfig);
+ example = {
+ fancyWebserver = {
+ enable = true;
+ accept = 443;
+ connect = 8080;
+ cert = "/path/to/pem/file";
+ };
+ };
+ default = { };
+ };
+
+ clients = mkOption {
+ description = "Define the client configurations.";
+ type = with types; attrsOf (submodule clientConfig);
+ example = {
+ foobar = {
+ accept = "0.0.0.0:8080";
+ connect = "nixos.org:443";
+ verifyChain = false;
+ };
+ };
+ default = { };
+ };
+ };
+ };
+
+
+ ###### implementation
+
+ config = mkIf cfg.enable {
+
+ assertions = concatLists [
+ (singleton {
+ assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0);
+ message = "stunnel: At least one server- or client-configuration has to be present.";
+ })
+
+ (mapAttrsToList verifyChainPathAssert cfg.clients)
+ ];
+
+ environment.systemPackages = [ pkgs.stunnel ];
+
+ environment.etc."stunnel.cfg".text = ''
+ ${ if cfg.user != null then "setuid = ${cfg.user}" else "" }
+ ${ if cfg.group != null then "setgid = ${cfg.group}" else "" }
+
+ debug = ${cfg.logLevel}
+
+ ${ optionalString cfg.fipsMode "fips = yes" }
+ ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
+
+ ; ----- SERVER CONFIGURATIONS -----
+ ${ lib.concatStringsSep "\n"
+ (lib.mapAttrsToList
+ (n: v: ''
+ [${n}]
+ accept = ${toString v.accept}
+ connect = ${toString v.connect}
+ cert = ${v.cert}
+
+ '')
+ cfg.servers)
+ }
+
+ ; ----- CLIENT CONFIGURATIONS -----
+ ${ lib.concatStringsSep "\n"
+ (lib.mapAttrsToList
+ (n: v: ''
+ [${n}]
+ client = yes
+ accept = ${v.accept}
+ connect = ${v.connect}
+ verifyChain = ${yesNo v.verifyChain}
+ verifyPeer = ${yesNo v.verifyPeer}
+ ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
+ ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
+ OCSPaia = yes
+
+ '')
+ cfg.clients)
+ }
+ '';
+
+ systemd.services.stunnel = {
+ description = "stunnel TLS tunneling service";
+ after = [ "network.target" ];
+ wants = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ restartTriggers = [ config.environment.etc."stunnel.cfg".source ];
+ serviceConfig = {
+ ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}";
+ Type = "forking";
+ };
+ };
+
+ };
+
+}