mirror of
https://github.com/gensokyo-zone/infrastructure.git
synced 2026-02-09 20:39:18 -08:00
feat(nginx): stream options
This commit is contained in:
parent
071b4aa9ca
commit
ad185929c2
3 changed files with 362 additions and 54 deletions
|
|
@ -6,11 +6,12 @@
|
|||
}: let
|
||||
inherit (inputs.self.lib.lib) mkAlmostOptionDefault;
|
||||
inherit (lib.options) mkOption mkEnableOption;
|
||||
inherit (lib.modules) mkIf mkMerge mkOptionDefault mkForce;
|
||||
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault mkForce;
|
||||
inherit (lib.attrsets) attrValues mapAttrs;
|
||||
inherit (lib.lists) filter concatMap;
|
||||
inherit (lib.lists) optional filter concatMap;
|
||||
inherit (lib.strings) hasPrefix hasInfix;
|
||||
inherit (config.services) nginx;
|
||||
listenModule = { config, virtualHost, ... }: {
|
||||
listenModule = { config, virtualHost, listenKind, ... }: {
|
||||
options = with lib.types; {
|
||||
enable = mkEnableOption "this port" // {
|
||||
default = true;
|
||||
|
|
@ -40,19 +41,61 @@
|
|||
type = bool;
|
||||
default = false;
|
||||
};
|
||||
listenParameters = mkOption {
|
||||
type = listOf str;
|
||||
internal = true;
|
||||
};
|
||||
listenConfigs = mkOption {
|
||||
type = listOf (separatedString " ");
|
||||
internal = true;
|
||||
};
|
||||
listenDirectives = mkOption {
|
||||
type = lines;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
config = {
|
||||
enable = mkIf (config.ssl && !virtualHost.ssl.enable) (mkForce false);
|
||||
port = mkOptionDefault (
|
||||
enable = mkMerge [
|
||||
(mkIf (config.ssl && !virtualHost.ssl.enable) (mkForce false))
|
||||
(mkIf (listenKind == "streamServer" && !config.ssl && virtualHost.ssl.enable && virtualHost.ssl.force != false) (mkForce false))
|
||||
];
|
||||
port = mkIf (listenKind == "virtualHost") (mkOptionDefault (
|
||||
if config.ssl then nginx.defaultSSLListenPort else nginx.defaultHTTPListenPort
|
||||
);
|
||||
));
|
||||
addresses = mkMerge [
|
||||
(mkOptionDefault virtualHost.listenAddresses')
|
||||
(mkIf (config.addr != null) (mkAlmostOptionDefault [ config.addr ]))
|
||||
];
|
||||
listenParameters = mkOptionDefault (
|
||||
optional config.ssl "ssl"
|
||||
++ optional virtualHost.default or false "default_server"
|
||||
++ optional virtualHost.reuseport or false "reuseport"
|
||||
++ optional config.proxyProtocol or false "proxy_protocol"
|
||||
++ config.extraParameters
|
||||
);
|
||||
listenConfigs = let
|
||||
# TODO: handle quic listener..?
|
||||
mkListenHost = { addr, port }: let
|
||||
addr' = if hasInfix ":" addr && !hasPrefix "[" addr then "[${addr}]" else addr;
|
||||
host =
|
||||
if addr != null then "${addr'}:${toString port}"
|
||||
else toString port;
|
||||
in assert port != null; host;
|
||||
mkDirective = addr: let
|
||||
host = mkListenHost { inherit addr; inherit (config) port; };
|
||||
in mkMerge (
|
||||
[ (mkBefore host) ]
|
||||
++ config.listenParameters
|
||||
);
|
||||
in mkOptionDefault (map (mkDirective) config.addresses);
|
||||
listenDirectives = mkMerge (map (conf: mkOptionDefault "listen ${conf};") config.listenConfigs);
|
||||
};
|
||||
};
|
||||
hostModule = { config, ... }: let
|
||||
listenType = { specialArgs, modules ? [ ] }: lib.types.submoduleWith {
|
||||
inherit specialArgs;
|
||||
modules = [ listenModule ] ++ modules;
|
||||
};
|
||||
hostModule = { nixosConfig, config, ... }: let
|
||||
cfg = attrValues config.listen';
|
||||
enabledCfg = filter (port: port.enable) cfg;
|
||||
mkListen = listen: addr: let
|
||||
|
|
@ -65,10 +108,11 @@
|
|||
in {
|
||||
options = with lib.types; {
|
||||
listen' = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [ listenModule ];
|
||||
type = attrsOf (listenType {
|
||||
specialArgs = {
|
||||
inherit nixosConfig;
|
||||
virtualHost = config;
|
||||
listenKind = "virtualHost";
|
||||
};
|
||||
});
|
||||
default = { };
|
||||
|
|
@ -89,13 +133,60 @@
|
|||
));
|
||||
};
|
||||
};
|
||||
streamServerModule = { nixosConfig, config, ... }: let
|
||||
enabledListen = filter (port: port.enable) (attrValues config.listen);
|
||||
in {
|
||||
options = with lib.types; {
|
||||
listen = mkOption {
|
||||
type = attrsOf (listenType {
|
||||
specialArgs = {
|
||||
inherit nixosConfig;
|
||||
virtualHost = config;
|
||||
streamServer = config;
|
||||
listenKind = "streamServer";
|
||||
};
|
||||
});
|
||||
default = { };
|
||||
};
|
||||
listenAddresses = mkOption {
|
||||
type = nullOr (listOf str);
|
||||
default = null;
|
||||
};
|
||||
listenAddresses' = mkOption {
|
||||
type = listOf str;
|
||||
internal = true;
|
||||
description = "listenAddresses or defaultListenAddresses if empty";
|
||||
};
|
||||
reuseport = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "only required on one host";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
enable = mkIf (config.listen != { } && enabledListen == [ ]) (mkForce false);
|
||||
listenAddresses' = mkOptionDefault (
|
||||
if config.listenAddresses != null then config.listenAddresses else nginx.defaultListenAddresses
|
||||
);
|
||||
streamConfig = mkIf (config.listen != { }) (mkMerge (
|
||||
map (listen: mkBefore listen.listenDirectives) enabledListen
|
||||
));
|
||||
};
|
||||
};
|
||||
in {
|
||||
options = with lib.types; {
|
||||
services.nginx.virtualHosts = mkOption {
|
||||
options.services.nginx = with lib.types; {
|
||||
virtualHosts = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [ hostModule ];
|
||||
shorthandOnlyDefinesConfig = true;
|
||||
});
|
||||
};
|
||||
stream.servers = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [ streamServerModule ];
|
||||
shorthandOnlyDefinesConfig = false;
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
inherit (inputs.self.lib.lib) mkAlmostOptionDefault;
|
||||
inherit (lib.options) mkOption mkEnableOption;
|
||||
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
|
||||
inherit (lib.attrsets) mapAttrsToList;
|
||||
inherit (lib.trivial) warnIf;
|
||||
inherit (config.services.nginx) virtualHosts;
|
||||
inherit (config.services) nginx;
|
||||
forceRedirectConfig = virtualHost: ''
|
||||
if ($x_scheme = http) {
|
||||
return ${toString virtualHost.redirectCode} https://$x_forwarded_host$request_uri;
|
||||
|
|
@ -26,42 +27,73 @@
|
|||
extraConfig = mkIf emitForce (forceRedirectConfig virtualHost);
|
||||
};
|
||||
};
|
||||
sslModule = { config, name, ... }: let
|
||||
cfg = config.ssl;
|
||||
in {
|
||||
options.ssl = with lib.types; {
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
config = {
|
||||
ssl = {
|
||||
enable = mkOptionDefault (cfg.cert.name != null || cfg.cert.keyPath != null);
|
||||
forced = mkOptionDefault (cfg.force != false && cfg.force != "reject");
|
||||
cert = let
|
||||
mkCopyCert = copyCert: {
|
||||
name = mkDefault copyCert.name;
|
||||
keyPath = mkAlmostOptionDefault copyCert.keyPath;
|
||||
path = mkAlmostOptionDefault copyCert.path;
|
||||
};
|
||||
copyCertVhost = mkCopyCert nginx.virtualHosts.${cfg.cert.copyFromVhost}.ssl.cert;
|
||||
copyCertStreamServer = mkCopyCert nginx.stream.servers.${cfg.cert.copyFromStreamServer}.ssl.cert;
|
||||
in mkMerge [
|
||||
(mkIf (cfg.cert.copyFromVhost != null) copyCertVhost)
|
||||
(mkIf (cfg.cert.copyFromStreamServer != null) copyCertStreamServer)
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
hostModule = { config, name, ... }: let
|
||||
cfg = config.ssl;
|
||||
emitForce = cfg.forced && config.proxied.enabled;
|
||||
in {
|
||||
imports = [ sslModule ];
|
||||
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 = {
|
||||
enable = mkEnableOption "ssl cert via name.shortServer";
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
locations = mkOption {
|
||||
|
|
@ -73,22 +105,11 @@
|
|||
};
|
||||
config = {
|
||||
ssl = {
|
||||
enable = mkOptionDefault (cfg.cert.name != null || cfg.cert.keyPath != null);
|
||||
forced = mkOptionDefault (cfg.force != false && cfg.force != "reject");
|
||||
cert = let
|
||||
certConfig.name = mkIf cfg.cert.enable (warnIf (config.name.shortServer == null) "ssl.cert.enable set but name.shortServer is null" (
|
||||
mkAlmostOptionDefault config.name.shortServer
|
||||
));
|
||||
copyCert = virtualHosts.${cfg.cert.copyFromVhost}.ssl.cert;
|
||||
otherCertConfig = mkIf (cfg.cert.copyFromVhost != null) {
|
||||
name = mkDefault copyCert.name;
|
||||
keyPath = mkAlmostOptionDefault copyCert.keyPath;
|
||||
path = mkAlmostOptionDefault copyCert.path;
|
||||
};
|
||||
in mkMerge [
|
||||
certConfig
|
||||
otherCertConfig
|
||||
];
|
||||
in certConfig;
|
||||
};
|
||||
addSSL = mkIf (cfg.enable && (cfg.force == false || emitForce)) (mkDefault true);
|
||||
forceSSL = mkIf (cfg.enable && cfg.force == true && !emitForce) (mkDefault true);
|
||||
|
|
@ -102,13 +123,45 @@
|
|||
extraConfig = mkIf emitForce (forceRedirectConfig config);
|
||||
};
|
||||
};
|
||||
upstreamServerModule = { config, nixosConfig, ... }: let
|
||||
cfg = config.ssl;
|
||||
in {
|
||||
imports = [ sslModule ];
|
||||
config = {
|
||||
ssl.cert = let
|
||||
cert = nixosConfig.security.acme.certs.${cfg.cert.name};
|
||||
in {
|
||||
path = mkIf (cfg.cert.name != null) (mkAlmostOptionDefault "${cert.directory}/fullchain.pem");
|
||||
keyPath = mkIf (cfg.cert.name != null) (mkAlmostOptionDefault "${cert.directory}/key.pem");
|
||||
};
|
||||
#listen.ssl = mkIf cfg.enable { ssl = true; };
|
||||
extraConfig = mkMerge [
|
||||
(mkIf (cfg.cert.path != null) "ssl_certificate ${cfg.cert.path};")
|
||||
(mkIf (cfg.cert.keyPath != null) "ssl_certificate_key ${cfg.cert.keyPath};")
|
||||
];
|
||||
};
|
||||
};
|
||||
in {
|
||||
options = with lib.types; {
|
||||
services.nginx.virtualHosts = mkOption {
|
||||
options.services.nginx = with lib.types; {
|
||||
virtualHosts = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [ hostModule ];
|
||||
shorthandOnlyDefinesConfig = true;
|
||||
});
|
||||
};
|
||||
stream.servers = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [ upstreamServerModule ];
|
||||
shorthandOnlyDefinesConfig = false;
|
||||
});
|
||||
};
|
||||
};
|
||||
config.systemd.services.nginx = let
|
||||
mapStreamServer = server: mkIf (server.enable && server.ssl.enable && server.ssl.cert.name != null) {
|
||||
wants = [ "acme-finished-${server.ssl.cert.name}.target" ];
|
||||
after = [ "acme-selfsigned-${server.ssl.cert.name}.service" ];
|
||||
before = [ "acme-${server.ssl.cert.name}.service" ];
|
||||
};
|
||||
streamServerCerts = mapAttrsToList (_: mapStreamServer) nginx.stream.servers;
|
||||
in mkIf nginx.enable (mkMerge streamServerCerts);
|
||||
}
|
||||
|
|
|
|||
164
modules/nixos/nginx/stream.nix
Normal file
164
modules/nixos/nginx/stream.nix
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit (lib.options) mkOption mkEnableOption;
|
||||
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault;
|
||||
inherit (lib.attrsets) mapAttrsToList;
|
||||
inherit (lib.lists) optional;
|
||||
inherit (lib.strings) hasPrefix hasInfix;
|
||||
cfg = config.services.nginx.stream;
|
||||
upstreamServerModule = {config, name, ...}: {
|
||||
options = with lib.types; {
|
||||
enable = mkEnableOption "upstream server" // {
|
||||
default = true;
|
||||
};
|
||||
addr = mkOption {
|
||||
type = str;
|
||||
default = name;
|
||||
};
|
||||
port = mkOption {
|
||||
type = port;
|
||||
};
|
||||
server = mkOption {
|
||||
type = str;
|
||||
example = "unix:/tmp/backend3";
|
||||
};
|
||||
settings = mkOption {
|
||||
type = attrsOf (oneOf [ int str ]);
|
||||
default = { };
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = str;
|
||||
default = "";
|
||||
};
|
||||
serverConfig = mkOption {
|
||||
type = separatedString " ";
|
||||
internal = true;
|
||||
};
|
||||
serverDirective = mkOption {
|
||||
type = str;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
config = let
|
||||
settings = mapAttrsToList (key: value: "${key}=${toString value}") config.settings;
|
||||
in {
|
||||
server = let
|
||||
addr = if hasInfix ":" config.addr && ! hasPrefix "[" config.addr then "[${config.addr}]" else config.addr;
|
||||
in mkOptionDefault "${addr}:${toString config.port}";
|
||||
serverConfig = mkMerge (
|
||||
[ (mkBefore config.server) ]
|
||||
++ settings
|
||||
++ optional (config.extraConfig != "") config.extraConfig
|
||||
);
|
||||
serverDirective = mkOptionDefault "server ${config.serverConfig};";
|
||||
};
|
||||
};
|
||||
upstreamModule = {config, name, nixosConfig, ...}: {
|
||||
options = with lib.types; let
|
||||
upstreamServer = submoduleWith {
|
||||
modules = [ upstreamServerModule ];
|
||||
specialArgs = {
|
||||
inherit nixosConfig;
|
||||
upstream = config;
|
||||
};
|
||||
};
|
||||
in {
|
||||
enable = mkEnableOption "upstream block" // {
|
||||
default = true;
|
||||
};
|
||||
name = mkOption {
|
||||
type = str;
|
||||
default = name;
|
||||
};
|
||||
servers = mkOption {
|
||||
type = attrsOf upstreamServer;
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = lines;
|
||||
default = "";
|
||||
};
|
||||
streamConfig = mkOption {
|
||||
type = lines;
|
||||
internal = true;
|
||||
};
|
||||
upstreamBlock = mkOption {
|
||||
type = lines;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
streamConfig = mkMerge (
|
||||
mapAttrsToList (_: server: mkIf server.enable server.serverDirective) config.servers
|
||||
++ [ config.extraConfig ]
|
||||
);
|
||||
upstreamBlock = mkOptionDefault ''
|
||||
upstream ${config.name} {
|
||||
${config.streamConfig}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
serverModule = {config, ...}: {
|
||||
options = with lib.types; {
|
||||
enable = mkEnableOption "stream server block" // {
|
||||
default = true;
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = lines;
|
||||
default = "";
|
||||
};
|
||||
streamConfig = mkOption {
|
||||
type = lines;
|
||||
internal = true;
|
||||
};
|
||||
serverBlock = mkOption {
|
||||
type = lines;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
streamConfig = mkMerge [
|
||||
config.extraConfig
|
||||
];
|
||||
serverBlock = mkOptionDefault ''
|
||||
server {
|
||||
${config.streamConfig}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
in {
|
||||
options.services.nginx.stream = with lib.types; {
|
||||
servers = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [serverModule];
|
||||
shorthandOnlyDefinesConfig = false;
|
||||
specialArgs = {
|
||||
nixosConfig = config;
|
||||
};
|
||||
});
|
||||
default = { };
|
||||
};
|
||||
upstreams = mkOption {
|
||||
type = attrsOf (submoduleWith {
|
||||
modules = [upstreamModule];
|
||||
shorthandOnlyDefinesConfig = false;
|
||||
specialArgs = {
|
||||
nixosConfig = config;
|
||||
};
|
||||
});
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
config.services.nginx = {
|
||||
streamConfig = mkMerge (
|
||||
mapAttrsToList (_: upstream: mkIf upstream.enable upstream.upstreamBlock) cfg.upstreams
|
||||
++ mapAttrsToList (_: server: mkIf server.enable server.serverBlock) cfg.servers
|
||||
);
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue