diff --git a/modules/nixos/nginx/proxy.nix b/modules/nixos/nginx/proxy.nix index 79e886fa..a6e8055e 100644 --- a/modules/nixos/nginx/proxy.nix +++ b/modules/nixos/nginx/proxy.nix @@ -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)); }; diff --git a/modules/nixos/nginx/ssl.nix b/modules/nixos/nginx/ssl.nix index 226ffbd7..3f170dbf 100644 --- a/modules/nixos/nginx/ssl.nix +++ b/modules/nixos/nginx/ssl.nix @@ -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; }); }; diff --git a/modules/nixos/nginx/upstream.nix b/modules/nixos/nginx/upstream.nix index afeeae13..b4e75289 100644 --- a/modules/nixos/nginx/upstream.nix +++ b/modules/nixos/nginx/upstream.nix @@ -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); }; }; };