refactor(nginx): ssl preread

This commit is contained in:
arcnmx 2024-04-23 13:18:47 -07:00
parent 418caefe64
commit b0a3da835c
7 changed files with 162 additions and 74 deletions

View file

@ -0,0 +1,103 @@
let
serverModule = {config, nixosConfig, name, gensokyo-zone, lib, ...}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkBefore mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.lists) optional;
inherit (lib.strings) concatStringsSep replaceStrings;
cfg = config.ssl.preread;
inherit (nixosConfig.services) nginx;
in {
options.ssl.preread = with lib.types; {
enable = mkEnableOption "ngx_stream_ssl_preread_module";
upstream = mkOption {
type = str;
default = "$preread_" + replaceStrings [ "'" ] [ "_" ] name;
};
upstreams = mkOption {
type = nullOr (attrsOf str);
};
streamConfig = mkOption {
type = lines;
};
};
config = let
inherit (nginx.stream) upstreams;
mkUpstream = host: upstream: "${host} ${upstreams.${upstream}.name};";
upstreams' = removeAttrs cfg.upstreams [ "default" ];
upstreamLines = mapAttrsToList mkUpstream upstreams'
++ optional (cfg.upstreams ? default) (mkUpstream "default" cfg.upstreams.default);
in {
ssl.preread = {
streamConfig = mkIf (cfg.upstreams != null) ''
map $ssl_preread_server_name ${cfg.upstream} {
hostnames;
${concatStringsSep "\n " upstreamLines}
}
'';
};
proxy = mkIf cfg.enable {
ssl.enable = false;
upstream = mkAlmostOptionDefault cfg.upstream;
};
streamConfig = mkIf cfg.enable "ssl_preread on;";
serverBlock = mkIf cfg.enable (mkOptionDefault (mkBefore cfg.streamConfig));
};
};
in {config, gensokyo-zone, lib, ...}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
cfg = config.services.nginx.ssl.preread;
in {
options.services.nginx = with lib.types; {
ssl.preread = {
enable = mkEnableOption "ssl preread";
listenPort = mkOption {
type = port;
default = 444;
};
serverPort = mkOption {
type = port;
default = 443;
};
serverName = mkOption {
type = str;
default = "preread'https";
};
upstreamName = mkOption {
type = str;
default = "preread'nginx";
};
};
stream.servers = mkOption {
type = attrsOf (submoduleWith {
modules = [serverModule];
shorthandOnlyDefinesConfig = false;
});
};
};
config = {
services.nginx = {
defaultSSLListenPort = mkIf cfg.enable cfg.listenPort;
stream = {
upstreams.${cfg.upstreamName} = mkIf cfg.enable {
ssl.enable = true;
servers.access = {
addr = mkDefault "localhost";
port = mkOptionDefault cfg.listenPort;
};
};
servers.${cfg.serverName} = {
enable = mkIf (!cfg.enable) (mkAlmostOptionDefault false);
listen.https.port = cfg.serverPort;
ssl.preread = {
enable = true;
upstreams.default = mkOptionDefault cfg.upstreamName;
};
};
};
};
};
}

View file

@ -93,6 +93,8 @@ let
url = parseUrl config.proxyPass; url = parseUrl config.proxyPass;
upstream = nginx.upstreams'.${cfg.upstream}; upstream = nginx.upstreams'.${cfg.upstream};
upstreamServer = upstream.servers.${upstream.defaultServerName}; upstreamServer = upstream.servers.${upstream.defaultServerName};
dynamicUpstream = hasPrefix "$" cfg.upstream;
hasUpstream = cfg.upstream != null && !dynamicUpstream && upstream.defaultServerName != null;
recommendedHeaders = { recommendedHeaders = {
Host = if cfg.host == null then xvars.get.proxy_hostport else cfg.host; Host = if cfg.host == null then xvars.get.proxy_hostport else cfg.host;
Referer = xvars.get.referer; Referer = xvars.get.referer;
@ -209,11 +211,11 @@ let
mapNullable (_: url.path) config.proxyPass mapNullable (_: url.path) config.proxyPass
); );
host = mkOptionDefault ( host = mkOptionDefault (
if cfg.upstream != null then assert url.host == upstream.name; upstreamServer.addr if hasUpstream then assert url.host == upstream.name; upstreamServer.addr
else mapNullable (_: url.host) config.proxyPass else mapNullable (_: url.host) config.proxyPass
); );
port = mkOptionDefault ( port = mkOptionDefault (
if cfg.upstream != null && url.port == null then assert url.host == upstream.name; upstreamServer.port if hasUpstream && url.port == null then assert url.host == upstream.name; upstreamServer.port
else mapNullable (_: url.port) config.proxyPass else mapNullable (_: url.port) config.proxyPass
); );
}; };

View file

@ -25,9 +25,6 @@
type = lines; type = lines;
internal = true; internal = true;
}; };
ssl = {
preread.enable = mkEnableOption "ngx_stream_ssl_preread_module";
};
proxy = { proxy = {
ssl = { ssl = {
enable = mkEnableOption "ssl upstream"; enable = mkEnableOption "ssl upstream";
@ -41,12 +38,8 @@
}; };
config = { config = {
proxy.ssl.enable = mkIf config.ssl.preread.enable false;
streamConfig = mkMerge [ streamConfig = mkMerge [
config.extraConfig config.extraConfig
(mkIf config.ssl.preread.enable
"ssl_preread on;"
)
(mkIf config.proxy.ssl.enable (mkIf config.proxy.ssl.enable
"proxy_ssl on;" "proxy_ssl on;"
) )

View file

@ -204,6 +204,7 @@ let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption; inherit (lib.options) mkOption;
inherit (lib.modules) mkIf; inherit (lib.modules) mkIf;
inherit (lib.strings) hasPrefix;
inherit (nixosConfig.services) nginx; inherit (nixosConfig.services) nginx;
in { in {
options = with lib.types; { options = with lib.types; {
@ -217,12 +218,15 @@ let
config = let config = let
proxyUpstream = nginx.stream.upstreams.${config.proxy.upstream}; proxyUpstream = nginx.stream.upstreams.${config.proxy.upstream};
dynamicUpstream = hasPrefix "$" config.proxy.upstream;
hasUpstream = config.proxy.upstream != null && !dynamicUpstream;
proxyPass =
if dynamicUpstream then config.proxy.upstream
else assert proxyUpstream.enable; proxyUpstream.name;
in { in {
proxy = { proxy = {
url = mkIf (config.proxy.upstream != null) (mkAlmostOptionDefault (assert proxyUpstream.enable; url = mkIf (config.proxy.upstream != null) (mkAlmostOptionDefault proxyPass);
proxyUpstream.name ssl.enable = mkIf (hasUpstream && proxyUpstream.ssl.enable) (mkAlmostOptionDefault true);
));
ssl.enable = mkIf (config.proxy.upstream != null && proxyUpstream.ssl.enable) (mkAlmostOptionDefault true);
}; };
}; };
}; };
@ -240,20 +244,27 @@ let
locationModule = {config, nixosConfig, virtualHost, gensokyo-zone, lib, ...}: let locationModule = {config, nixosConfig, virtualHost, gensokyo-zone, lib, ...}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkOptionDefault; inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.strings) hasPrefix;
inherit (nixosConfig.services) nginx; inherit (nixosConfig.services) nginx;
in { in {
imports = [ proxyUpstreamModule ]; imports = [ proxyUpstreamModule ];
config = let config = let
proxyUpstream = nginx.upstreams'.${config.proxy.upstream}; proxyUpstream = nginx.upstreams'.${config.proxy.upstream};
proxyScheme = if proxyUpstream.ssl.enable then "https" else "http"; proxyScheme = if config.proxy.ssl.enabled then "https" else "http";
dynamicUpstream = hasPrefix "$" config.proxy.upstream;
hasUpstream = config.proxy.upstream != null && !dynamicUpstream;
proxyHost =
if dynamicUpstream then config.proxy.upstream
else assert proxyUpstream.enable; proxyUpstream.name;
in { in {
proxy = { proxy = {
upstream = mkOptionDefault virtualHost.proxy.upstream; upstream = mkOptionDefault virtualHost.proxy.upstream;
enable = mkIf (config.proxy.upstream != null && virtualHost.proxy.upstream == null) true; enable = mkIf (config.proxy.upstream != null && virtualHost.proxy.upstream == null) true;
url = mkIf (config.proxy.upstream != null) (mkAlmostOptionDefault (assert proxyUpstream.enable; url = mkIf (config.proxy.upstream != null) (mkAlmostOptionDefault
"${proxyScheme}://${proxyUpstream.name}" "${proxyScheme}://${proxyHost}"
)); );
ssl.enabled = mkAlmostOptionDefault (if hasUpstream then proxyUpstream.ssl.enable else false);
}; };
}; };
}; };

View file

@ -6,7 +6,7 @@
... ...
}: }:
let let
inherit (gensokyo-zone.lib) mkAddress6; inherit (gensokyo-zone.lib) mkAddress6 mapOptionDefaults;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (config.services) tailscale; inherit (config.services) tailscale;
@ -52,13 +52,6 @@ in {
type = str; type = str;
}; };
preread = { preread = {
enable = mkEnableOption "ssl preread" // {
# TODO: default = true;
};
port = mkOption {
type = port;
default = 444;
};
ldapPort = mkOption { ldapPort = mkOption {
type = port; type = port;
default = 637; default = 637;
@ -104,10 +97,10 @@ in {
}; };
config = { config = {
services.nginx = { services.nginx = {
# TODO: ssl.preread.enable = mkDefault true;
access.freeipa = { access.freeipa = {
host = mkOptionDefault (config.lib.access.getAddressFor (config.lib.access.systemForService "freeipa").name "lan"); host = mkOptionDefault (config.lib.access.getAddressFor (config.lib.access.systemForService "freeipa").name "lan");
}; };
defaultSSLListenPort = mkIf access.preread.enable access.preread.port;
stream = let stream = let
prereadConf = { prereadConf = {
upstreams = { upstreams = {
@ -128,28 +121,29 @@ in {
port = mkOptionDefault nginx.stream.servers.ldap.listen.ldaps.port; port = mkOptionDefault nginx.stream.servers.ldap.listen.ldaps.port;
}; };
}; };
nginx = {
ssl.enable = true;
servers.access = {
addr = mkDefault "localhost";
port = mkOptionDefault nginx.defaultSSLListenPort;
};
};
}; };
servers = { servers = {
preread'https = { ${nginx.ssl.preread.serverName} = {
listen = { ssl.preread.upstreams = mapOptionDefaults {
https.port = 443; ${virtualHosts.freeipa.serverName} = "freeipa";
${virtualHosts.freeipa'ca.serverName} = "freeipa";
${nginx.access.ldap.domain} = "ldaps_access";
${nginx.access.ldap.localDomain} = "ldaps_access";
${nginx.access.ldap.intDomain} = "ldaps_access";
${nginx.access.ldap.tailDomain} = "ldaps_access";
}; };
ssl.preread.enable = true;
proxy.url = "$https_upstream";
}; };
preread'ldap = { preread'ldap = {
listen = { listen = {
ldaps.port = 636; ldaps.port = access.ldapPort;
};
ssl.preread = {
enable = true;
upstreams = mapOptionDefaults {
${virtualHosts.freeipa.serverName} = "ldaps";
default = "ldaps_access";
};
}; };
ssl.preread.enable = true;
proxy.url = "$ldap_upstream";
}; };
}; };
}; };
@ -198,37 +192,16 @@ in {
conf.servers = { conf.servers = {
ldap = { ldap = {
listen = { listen = {
ldaps.port = mkIf access.preread.enable (mkDefault access.preread.ldapPort); ldaps.port = mkIf nginx.ssl.preread.enable (mkDefault access.preread.ldapPort);
}; };
ssl.cert.copyFromVhost = mkDefault "freeipa"; ssl.cert.copyFromVhost = mkDefault "freeipa";
}; };
}; };
in mkMerge [ in mkMerge [
conf conf
(mkIf access.preread.enable prereadConf) (mkIf nginx.ssl.preread.enable prereadConf)
(mkIf access.kerberos.enable kerberosConf) (mkIf access.kerberos.enable kerberosConf)
]; ];
streamConfig = let
inherit (nginx.stream) upstreams;
preread = ''
map $ssl_preread_server_name $https_upstream {
hostnames;
${virtualHosts.freeipa.serverName} ${upstreams.freeipa.name};
${virtualHosts.freeipa'ca.serverName} ${upstreams.freeipa.name};
${nginx.access.ldap.domain} ${upstreams.ldaps_access.name};
${nginx.access.ldap.localDomain} ${upstreams.ldaps_access.name};
${nginx.access.ldap.intDomain} ${upstreams.ldaps_access.name};
${nginx.access.ldap.tailDomain} ${upstreams.ldaps_access.name};
default ${upstreams.nginx.name};
}
map $ssl_preread_server_name $ldap_upstream {
hostnames;
${virtualHosts.freeipa.serverName} ${upstreams.ldaps.name};
default ${upstreams.ldaps_access.name};
}
'';
in mkIf access.preread.enable preread;
virtualHosts = let virtualHosts = let
name.shortServer = mkDefault "ipa"; name.shortServer = mkDefault "ipa";
in { in {
@ -292,8 +265,8 @@ in {
access.kerberos.ports.kpasswd access.kerberos.ports.kpasswd
access.kerberos.ports.kadmin access.kerberos.ports.kadmin
]) ])
(mkIf access.preread.enable [ (mkIf nginx.ssl.preread.enable [
636 access.ldapPort
]) ])
]; ];
allowedUDPPorts = mkIf access.kerberos.enable [ allowedUDPPorts = mkIf access.kerberos.enable [

View file

@ -1,5 +1,8 @@
_: { {config, lib, ...}: let
networking.firewall.allowedTCPPorts = [ inherit (lib.modules) mkIf;
cfg = config.services.nginx;
in {
networking.firewall.allowedTCPPorts = mkIf cfg.enable [
443 443
80 80
]; ];

View file

@ -1,13 +1,16 @@
{ {
config, config,
lib, lib,
pkgs,
... ...
}: }: let
with lib; { inherit (lib.modules) mkIf mkDefault;
networking.firewall.interfaces.local.allowedTCPPorts = [ cfg = config.services.nginx;
443 in {
80 networking.firewall.interfaces.local.allowedTCPPorts = let
inherit (cfg.ssl) preread;
in mkIf cfg.enable [
(if preread.enable then preread.serverPort else cfg.defaultSSLListenPort)
cfg.defaultHTTPListenPort
]; ];
services.nginx = { services.nginx = {