refactor(nginx): proxy.ssl options

This commit is contained in:
arcnmx 2024-04-26 12:24:28 -07:00
parent 12671b3539
commit 1f9e9acde4
3 changed files with 176 additions and 135 deletions

View file

@ -1,16 +1,19 @@
let
serverModule = {config, name, options, gensokyo-zone, lib, ...}: let
proxyModule = {config, name, options, gensokyo-zone, lib, ...}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter;
inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault;
inherit (lib.strings) optionalString;
cfg = config.proxy;
in {
options = with lib.types; {
proxy = {
enable = mkEnableOption "proxy_pass";
transparent.enable = mkEnableOption "proxy_bind transparent";
ssl = {
enable = mkEnableOption "ssl upstream";
verify = mkEnableOption "proxy_ssl_verify";
bind = {
enable = mkEnableOption "proxy_bind";
transparent = mkEnableOption "proxy_bind transparent";
address = mkOption {
type = str;
};
};
url = mkOption {
type = str;
@ -18,23 +21,29 @@ let
};
};
config = {
proxy = {
bind.address = mkIf cfg.bind.transparent (mkOptionDefault "$remote_addr");
};
extraConfig = mkIf cfg.enable (mkMerge [
(mkIf cfg.bind.enable (mkAfter (
"proxy_bind ${cfg.bind.address}" + optionalString cfg.bind.transparent " transparent" + ";"
)))
]);
};
};
serverModule = {config, name, options, gensokyo-zone, lib, ...}: let
inherit (lib.modules) mkIf mkAfter;
cfg = config.proxy;
in {
imports = [ proxyModule ];
config = let
warnProxy = lib.warnIf (!cfg.enable && options.proxy.url.isDefined) "nginx.stream.servers.${name}.proxy.url set without proxy.enable";
in {
streamConfig = warnProxy (mkMerge [
(mkIf cfg.transparent.enable ''
proxy_bind $remote_addr transparent;
'')
(mkIf cfg.ssl.enable
"proxy_ssl on;"
)
(mkIf (cfg.ssl.enable && cfg.ssl.verify)
"proxy_ssl_verify on;"
)
(mkIf cfg.enable (mkAfter
"proxy_pass ${cfg.url};"
))
]);
streamConfig = warnProxy (mkIf cfg.enable (mkAfter
"proxy_pass ${cfg.url};"
));
};
};
locationModule = { config, nixosConfig, name, virtualHost, xvars, gensokyo-zone, lib, ... }: let
@ -47,9 +56,10 @@ let
inherit (nixosConfig.services) nginx;
cfg = config.proxy;
in {
imports = [ proxyModule ];
options = with lib.types; {
proxy = {
enable = mkEnableOption "proxy";
enabled = mkOption {
type = bool;
readOnly = true;
@ -58,9 +68,6 @@ let
type = bool;
default = true;
};
url = mkOption {
type = str;
};
path = mkOption {
type = str;
};
@ -70,21 +77,6 @@ let
websocket.enable = mkEnableOption "websocket proxy" // {
default = cfg.inheritServerDefaults && virtualHost.proxy.websocket.enable;
};
ssl = {
enabled = mkOption {
type = bool;
};
verify = mkEnableOption "proxy_ssl_verify";
sni = mkEnableOption "proxy_ssl_server_name" // {
default = cfg.ssl.host != null;
};
host = mkOption {
type = nullOr str;
default = null;
example = "xvars.get.proxy_host";
# $upstream_last_server_name is commercial-only :<
};
};
parsed = {
scheme = mkOption {
type = nullOr str;
@ -213,9 +205,6 @@ let
enabled = mkOptionDefault (config.proxyPass != null);
path = mkIf (hasPrefix "/" name) (mkOptionDefault name);
url = mkIf (cfg.inheritServerDefaults && virtualHost.proxy.url != null) (mkOptionDefault virtualHost.proxy.url);
ssl = {
enabled = mkOptionDefault (cfg.parsed.scheme == "https");
};
headers = {
enableRecommended = mkOptionDefault (
if cfg.enable && (!cfg.inheritServerDefaults || virtualHost.proxy.headers.enableRecommended != false) then true
@ -266,9 +255,6 @@ let
(mkIf (cfg.headers.rewriteReferer.enable) (mkJustBefore rewriteReferer))
(mkIf (cfg.redirect.enable) (mkBefore redirect))
(mkIf (emitHeaders) (mkJustAfter setHeaders))
(mkIf (cfg.ssl.enabled && cfg.ssl.sni) "proxy_ssl_server_name on;")
(mkIf (cfg.ssl.enabled && cfg.ssl.host != null) "proxy_ssl_name ${cfg.ssl.host};")
(mkIf (cfg.ssl.enabled && cfg.ssl.verify) "proxy_ssl_verify on;")
(mkIf cfg.websocket.enable "proxy_cache_bypass $http_upgrade;")
] ++ hideHeaders));
};

View file

@ -1,68 +1,50 @@
{
config,
lib,
inputs,
...
}: let
inherit (inputs.self.lib.lib) mkAlmostOptionDefault mkAlmostDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.trivial) warnIf;
inherit (config.services) nginx;
forceRedirectConfig = { virtualHost, xvars }: ''
if (${xvars.get.scheme} = http) {
return ${toString virtualHost.redirectCode} https://${xvars.get.host}$request_uri;
}
'';
locationModule = { config, virtualHost, xvars, ... }: let
cfg = config.ssl;
emitForce = cfg.force && !virtualHost.ssl.forced;
in {
options.ssl = {
force = mkEnableOption "redirect to SSL";
};
config = {
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig { inherit xvars virtualHost; });
};
};
sslModule = { config, name, ... }: let
let
sslModule = { config, nixosConfig, gensokyo-zone, lib, ... }: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (nixosConfig.services) nginx;
cfg = config.ssl;
in {
options.ssl = with lib.types; {
enable = mkOption {
type = bool;
options = with lib.types; {
ssl = {
enable = mkOption {
type = bool;
};
force = mkOption {
# TODO: "force-nonlocal"? exceptions for tailscale?
type = enum [ false true "only" "reject" ];
default = false;
};
forced = mkOption {
type = bool;
readOnly = true;
};
cert = {
name = mkOption {
type = nullOr str;
default = null;
};
keyPath = mkOption {
type = nullOr path;
default = null;
};
path = mkOption {
type = nullOr path;
default = null;
};
copyFromVhost = mkOption {
type = nullOr str;
default = null;
};
copyFromStreamServer = mkOption {
type = nullOr str;
default = null;
};
};
};
force = mkOption {
# TODO: "force-nonlocal"? exceptions for tailscale?
type = enum [ false true "only" "reject" ];
default = false;
};
forced = mkOption {
type = bool;
readOnly = true;
};
cert = {
name = mkOption {
type = nullOr str;
default = null;
};
keyPath = mkOption {
type = nullOr path;
default = null;
};
path = mkOption {
type = nullOr path;
default = null;
};
copyFromVhost = mkOption {
type = nullOr str;
default = null;
};
copyFromStreamServer = mkOption {
type = nullOr str;
default = null;
proxy.ssl = {
enabled = mkOption {
type = bool;
};
};
};
@ -85,7 +67,104 @@
};
};
};
hostModule = { config, name, xvars, ... }: let
sslProxyModule = { config, lib, ... }: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter;
inherit (config) proxy;
cfg = proxy.ssl;
in {
options.proxy.ssl = with lib.types; {
enable = mkOption {
type = bool;
};
verify = mkEnableOption "proxy_ssl_verify";
sni = mkEnableOption "proxy_ssl_server_name" // {
default = cfg.host != null;
};
host = mkOption {
type = nullOr str;
default = null;
example = "xvars.get.proxy_host";
description = "proxy_ssl_name";
# $upstream_last_server_name is commercial-only :<
};
};
config = {
extraConfig = mkIf (proxy.enable && cfg.enable) (mkMerge [
(mkIf cfg.verify "proxy_ssl_verify on;")
(mkIf cfg.sni "proxy_ssl_server_name on;")
(mkIf (cfg.host != null) (mkAfter "proxy_ssl_name ${cfg.host};"))
]);
};
};
streamServerModule = { config, nixosConfig, gensokyo-zone, lib, ... }: let
inherit (gensokyo-zone.lib) mkAlmostDefault;
inherit (lib.options) mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault;
cfg = config.ssl;
in {
imports = [ sslModule sslProxyModule ];
options = with lib.types; {
ssl = {
kTLS = mkEnableOption "kTLS support" // {
default = true;
};
};
};
config = let
inherit (config) proxy;
cert = nixosConfig.security.acme.certs.${cfg.cert.name};
conf.ssl.cert = {
path = mkIf (cfg.cert.name != null) (mkAlmostDefault "${cert.directory}/fullchain.pem");
keyPath = mkIf (cfg.cert.name != null) (mkAlmostDefault "${cert.directory}/key.pem");
};
conf.proxy.ssl.enable = mkOptionDefault false;
#confSsl.listen.ssl = { ssl = true; };
confSsl.extraConfig = mkMerge [
(mkIf (cfg.cert.path != null) "ssl_certificate ${cfg.cert.path};")
(mkIf (cfg.cert.keyPath != null) "ssl_certificate_key ${cfg.cert.keyPath};")
(mkIf cfg.kTLS "ssl_conf_command Options KTLS;")
];
confProxy.extraConfig = mkIf proxy.ssl.enable "proxy_ssl on;";
in mkMerge [
conf
(mkIf cfg.enable confSsl)
(mkIf proxy.enable confProxy)
];
};
in {
config,
gensokyo-zone,
lib,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.trivial) warnIf;
inherit (lib.strings) hasPrefix;
inherit (config.services) nginx;
forceRedirectConfig = { virtualHost, xvars }: ''
if (${xvars.get.scheme} = http) {
return ${toString virtualHost.redirectCode} https://${xvars.get.host}$request_uri;
}
'';
locationModule = { config, virtualHost, xvars, ... }: let
cfg = config.ssl;
emitForce = cfg.force && !virtualHost.ssl.forced;
in {
imports = [ sslProxyModule ];
options.ssl = {
force = mkEnableOption "redirect to SSL";
};
config = {
proxy.ssl.enable = mkOptionDefault (hasPrefix "https://" config.proxyPass);
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig { inherit xvars virtualHost; });
};
};
hostModule = { config, xvars, ... }: let
cfg = config.ssl;
emitForce = cfg.forced && config.proxied.enabled;
in {
@ -124,30 +203,6 @@
extraConfig = mkIf emitForce (forceRedirectConfig { virtualHost = config; inherit xvars; });
};
};
upstreamServerModule = { config, nixosConfig, ... }: let
cfg = config.ssl;
in {
imports = [ sslModule ];
options.ssl = with lib.types; {
kTLS = mkEnableOption "kTLS support" // {
default = true;
};
};
config = {
ssl.cert = let
cert = nixosConfig.security.acme.certs.${cfg.cert.name};
in {
path = mkIf (cfg.cert.name != null) (mkAlmostDefault "${cert.directory}/fullchain.pem");
keyPath = mkIf (cfg.cert.name != null) (mkAlmostDefault "${cert.directory}/key.pem");
};
#listen.ssl = mkIf cfg.enable { ssl = true; };
extraConfig = mkIf cfg.enable (mkMerge [
(mkIf (cfg.cert.path != null) "ssl_certificate ${cfg.cert.path};")
(mkIf (cfg.cert.keyPath != null) "ssl_certificate_key ${cfg.cert.keyPath};")
(mkIf cfg.kTLS "ssl_conf_command Options KTLS;")
]);
};
};
in {
options.services.nginx = with lib.types; {
virtualHosts = mkOption {
@ -158,7 +213,7 @@ in {
};
stream.servers = mkOption {
type = attrsOf (submoduleWith {
modules = [ upstreamServerModule ];
modules = [ streamServerModule ];
shorthandOnlyDefinesConfig = false;
});
};

View file

@ -252,7 +252,7 @@ let
config = let
proxyUpstream = nginx.upstreams'.${config.proxy.upstream};
proxyScheme = if config.proxy.ssl.enabled then "https" else "http";
proxyScheme = if config.proxy.ssl.enable then "https" else "http";
dynamicUpstream = hasPrefix "$" config.proxy.upstream;
hasUpstream = config.proxy.upstream != null && !dynamicUpstream;
proxyHost =
@ -265,7 +265,7 @@ let
url = mkIf (config.proxy.upstream != null) (mkAlmostOptionDefault
"${proxyScheme}://${proxyHost}"
);
ssl.enabled = mkAlmostOptionDefault (if hasUpstream then proxyUpstream.ssl.enable else false);
ssl.enable = mkAlmostOptionDefault (if hasUpstream then proxyUpstream.ssl.enable else false);
};
};
};