nixos/stunnel: Make free-form
This unlocks stunnel's other ~100 configuration directives, allowing full stunnel use in NixOS.
This commit is contained in:
parent
61585d1cd7
commit
131399effb
|
@ -7,80 +7,27 @@ let
|
||||||
cfg = config.services.stunnel;
|
cfg = config.services.stunnel;
|
||||||
yesNo = val: if val then "yes" else "no";
|
yesNo = val: if val then "yes" else "no";
|
||||||
|
|
||||||
|
verifyRequiredField = type: field: n: c: {
|
||||||
|
assertion = hasAttr field c;
|
||||||
|
message = "stunnel: \"${n}\" ${type} configuration - Field ${field} is required.";
|
||||||
|
};
|
||||||
|
|
||||||
verifyChainPathAssert = n: c: {
|
verifyChainPathAssert = n: c: {
|
||||||
assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
|
assertion = (c.verifyHostname or null) == null || (c.verifyChain || c.verifyPeer);
|
||||||
message = "stunnel: \"${n}\" client configuration - hostname verification " +
|
message = "stunnel: \"${n}\" client configuration - hostname verification " +
|
||||||
"is not possible without either verifyChain or verifyPeer enabled";
|
"is not possible without either verifyChain or verifyPeer enabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
serverConfig = {
|
removeNulls = mapAttrs (_: filterAttrs (_: v: v != null));
|
||||||
options = {
|
mkValueString = v:
|
||||||
accept = mkOption {
|
if v == true then "yes"
|
||||||
type = types.either types.str types.int;
|
else if v == false then "no"
|
||||||
description = ''
|
else generators.mkValueStringDefault {} v;
|
||||||
On which [host:]port stunnel should listen for incoming TLS connections.
|
generateConfig = c:
|
||||||
Note that unlike other softwares stunnel ipv6 address need no brackets,
|
generators.toINI {
|
||||||
so to listen on all IPv6 addresses on port 1234 one would use ':::1234'.
|
mkSectionName = id;
|
||||||
'';
|
mkKeyValue = k: v: "${k} = ${mkValueString v}";
|
||||||
};
|
} (removeNulls c);
|
||||||
|
|
||||||
connect = mkOption {
|
|
||||||
type = types.either types.str types.int;
|
|
||||||
description = "Port or IP:Port to which 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.nullOr types.path;
|
|
||||||
default = null;
|
|
||||||
description = "Path to a directory containing certificates to validate against.";
|
|
||||||
};
|
|
||||||
|
|
||||||
CAFile = mkOption {
|
|
||||||
type = types.nullOr types.path;
|
|
||||||
default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
|
||||||
defaultText = literalExpression ''"''${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
|
in
|
||||||
|
|
||||||
|
@ -130,8 +77,13 @@ in
|
||||||
|
|
||||||
|
|
||||||
servers = mkOption {
|
servers = mkOption {
|
||||||
description = "Define the server configuations.";
|
description = ''
|
||||||
type = with types; attrsOf (submodule serverConfig);
|
Define the server configuations.
|
||||||
|
|
||||||
|
See "SERVICE-LEVEL OPTIONS" in <citerefentry><refentrytitle>stunnel</refentrytitle>
|
||||||
|
<manvolnum>8</manvolnum></citerefentry>.
|
||||||
|
'';
|
||||||
|
type = with types; attrsOf (attrsOf (nullOr (oneOf [bool int str])));
|
||||||
example = {
|
example = {
|
||||||
fancyWebserver = {
|
fancyWebserver = {
|
||||||
accept = 443;
|
accept = 443;
|
||||||
|
@ -143,8 +95,33 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
clients = mkOption {
|
clients = mkOption {
|
||||||
description = "Define the client configurations.";
|
description = ''
|
||||||
type = with types; attrsOf (submodule clientConfig);
|
Define the client configurations.
|
||||||
|
|
||||||
|
By default, verifyChain and OCSPaia are enabled and a CAFile is provided from pkgs.cacert.
|
||||||
|
|
||||||
|
See "SERVICE-LEVEL OPTIONS" in <citerefentry><refentrytitle>stunnel</refentrytitle>
|
||||||
|
<manvolnum>8</manvolnum></citerefentry>.
|
||||||
|
'';
|
||||||
|
type = with types; attrsOf (attrsOf (nullOr (oneOf [bool int str])));
|
||||||
|
|
||||||
|
apply = let
|
||||||
|
applyDefaults = c:
|
||||||
|
{
|
||||||
|
CAFile = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||||
|
OCSPaia = true;
|
||||||
|
verifyChain = true;
|
||||||
|
} // c;
|
||||||
|
setCheckHostFromVerifyHostname = c:
|
||||||
|
# To preserve backward-compatibility with the old NixOS stunnel module
|
||||||
|
# definition, allow "verifyHostname" as an alias for "checkHost".
|
||||||
|
c // {
|
||||||
|
checkHost = c.checkHost or c.verifyHostname or null;
|
||||||
|
verifyHostname = null; # Not a real stunnel configuration setting
|
||||||
|
};
|
||||||
|
forceClient = c: c // { client = true; };
|
||||||
|
in mapAttrs (_: c: forceClient (setCheckHostFromVerifyHostname (applyDefaults c)));
|
||||||
|
|
||||||
example = {
|
example = {
|
||||||
foobar = {
|
foobar = {
|
||||||
accept = "0.0.0.0:8080";
|
accept = "0.0.0.0:8080";
|
||||||
|
@ -169,6 +146,11 @@ in
|
||||||
})
|
})
|
||||||
|
|
||||||
(mapAttrsToList verifyChainPathAssert cfg.clients)
|
(mapAttrsToList verifyChainPathAssert cfg.clients)
|
||||||
|
(mapAttrsToList (verifyRequiredField "client" "accept") cfg.clients)
|
||||||
|
(mapAttrsToList (verifyRequiredField "client" "connect") cfg.clients)
|
||||||
|
(mapAttrsToList (verifyRequiredField "server" "accept") cfg.servers)
|
||||||
|
(mapAttrsToList (verifyRequiredField "server" "cert") cfg.servers)
|
||||||
|
(mapAttrsToList (verifyRequiredField "server" "connect") cfg.servers)
|
||||||
];
|
];
|
||||||
|
|
||||||
environment.systemPackages = [ pkgs.stunnel ];
|
environment.systemPackages = [ pkgs.stunnel ];
|
||||||
|
@ -183,36 +165,10 @@ in
|
||||||
${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
|
${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
|
||||||
|
|
||||||
; ----- SERVER CONFIGURATIONS -----
|
; ----- SERVER CONFIGURATIONS -----
|
||||||
${ lib.concatStringsSep "\n"
|
${ generateConfig cfg.servers }
|
||||||
(lib.mapAttrsToList
|
|
||||||
(n: v: ''
|
|
||||||
[${n}]
|
|
||||||
accept = ${toString v.accept}
|
|
||||||
connect = ${toString v.connect}
|
|
||||||
cert = ${v.cert}
|
|
||||||
|
|
||||||
'')
|
|
||||||
cfg.servers)
|
|
||||||
}
|
|
||||||
|
|
||||||
; ----- CLIENT CONFIGURATIONS -----
|
; ----- CLIENT CONFIGURATIONS -----
|
||||||
${ lib.concatStringsSep "\n"
|
${ generateConfig cfg.clients }
|
||||||
(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.CAFile != null) "CAFile = ${v.CAFile}"}
|
|
||||||
${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
|
|
||||||
OCSPaia = yes
|
|
||||||
|
|
||||||
'')
|
|
||||||
cfg.clients)
|
|
||||||
}
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
systemd.services.stunnel = {
|
systemd.services.stunnel = {
|
||||||
|
|
Loading…
Reference in a new issue