refactor(nginx): proxy options

This commit is contained in:
arcnmx 2024-04-18 10:40:35 -07:00
parent c3f3fe1fed
commit 02508ecbd3
18 changed files with 638 additions and 424 deletions

View file

@ -56,7 +56,7 @@
set ${varPrefix}client 1;
}
'';
localModule = {config, ...}: let
localModule = {config, xvars, ...}: let
cfg = config.local;
in {
options.local = with lib.types; {
@ -100,7 +100,7 @@
in mkMerge [
(mkIf cfg.emitDenyGlobal (mkBefore allowDirectives))
(mkIf cfg.emitVars (mkBefore (mkAddrVar "$remote_addr" "$local_")))
(mkIf cfg.emitVars (mkBefore (mkAddrVar "$x_remote_addr" "$x_local_")))
(mkIf (cfg.emitVars && config.xvars.enable) (mkBefore (mkAddrVar (xvars.remote_addr.get) "$x_local_")))
];
};
};
@ -130,13 +130,7 @@
options = with lib.types; {
locations = mkOption {
type = attrsOf (submoduleWith {
modules = [locationModule];
shorthandOnlyDefinesConfig = true;
specialArgs = {
virtualHost = config;
};
});
type = attrsOf (submodule [locationModule]);
};
};
@ -149,13 +143,7 @@
in {
options = with lib.types; {
services.nginx.virtualHosts = mkOption {
type = attrsOf (submoduleWith {
modules = [hostModule];
shorthandOnlyDefinesConfig = true;
specialArgs = {
nixosConfig = config;
};
});
type = attrsOf (submodule [hostModule]);
};
};
}

View file

@ -1,57 +1,27 @@
{
config,
lib,
inputs,
...
}: let
inherit (inputs.self.lib.lib) mkAlmostAfter mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkDefault mkOptionDefault;
inherit (lib.strings) optionalString splitString match;
inherit (lib.attrsets) attrValues;
inherit (lib.lists) length head /*optional*/ any;
inherit (lib.trivial) mapNullable;
#inherit (config) networking;
inherit (config.services) nginx;
schemeForUrl = url: let
parts = splitString ":" url;
in if length parts == 1 then null else head parts;
pathForUrl = url: let
parts = match ''[^:]+://[^/]+(.*)'' url;
in if parts == null then null else head parts;
hostForUrl = url: let
parts = match ''[^:]+://([^/]+).*'' url;
in if parts == null then null else head parts;
xHeadersDefaults = ''
set $x_scheme $scheme;
set $x_forwarded_for $remote_addr;
set $x_remote_addr $remote_addr;
set $x_forwarded_host $host;
set $x_forwarded_server $host;
set $x_host $host;
set $x_referer $http_referer;
set $x_proxy_host $x_host;
'';
xHeadersProxied = ''
set $x_forwarded_for $proxy_add_x_forwarded_for;
inherit (inputs.self.lib.lib) mkJustBefore mkAlmostOptionDefault orderJustBefore;
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkOrder mkDefault mkOptionDefault;
xHeadersProxied = { xvars }: ''
${xvars.init "forwarded_for" "$proxy_add_x_forwarded_for"}
if ($http_x_forwarded_proto) {
set $x_scheme $http_x_forwarded_proto;
${xvars.init "scheme" "$http_x_forwarded_proto"}
}
if ($http_x_real_ip) {
set $x_remote_addr $http_x_real_ip;
${xvars.init "remote_addr" "$http_x_real_ip"}
}
if ($http_x_forwarded_host) {
set $x_forwarded_host $http_x_forwarded_host;
${xvars.init "host" "$http_x_forwarded_host"}
}
if ($http_x_forwarded_server) {
set $x_forwarded_server $http_x_forwarded_server;
}
if ($x_referer ~ "^https?://([^/]*)/(.*)$") {
set $x_referer_host $1;
set $x_referer_path $2;
${xvars.init "forwarded_server" "$http_x_forwarded_server"}
}
'';
locationModule = { config, virtualHost, ... }: let
locationModule = { config, virtualHost, xvars, ... }: let
cfg = config.proxied;
in {
options = with lib.types; {
@ -64,94 +34,30 @@
type = bool;
readOnly = true;
};
xvars.enable = mkEnableOption "$x_variables";
redirectScheme = mkEnableOption "redirect to X-Forwarded-Proto" // {
default = cfg.enabled;
};
rewriteReferer = mkEnableOption "rewrite Referer header" // {
default = cfg.enabled;
};
};
proxy = {
enabled = mkOption {
type = bool;
readOnly = true;
};
scheme = mkOption {
type = nullOr str;
};
path = mkOption {
type = nullOr str;
};
host = mkOption {
type = nullOr str;
};
headers.enableRecommended = mkOption {
type = enum [ true false "nixpkgs" ];
};
};
};
config = let
emitVars = cfg.enabled && !virtualHost.proxied.enabled;
emitRedirectScheme = config.proxy.enabled && cfg.redirectScheme;
emitRefererRewrite = config.proxy.enabled && cfg.rewriteReferer;
emitHeaders = config.proxy.enabled && config.proxy.headers.enableRecommended == true;
in {
proxied = {
enabled = mkOptionDefault (virtualHost.proxied.enabled || cfg.enable != false);
xvars.enable = mkIf (cfg.enabled || emitRedirectScheme || emitHeaders) true;
};
proxy = {
enabled = mkOptionDefault (config.proxyPass != null);
headers.enableRecommended = mkOptionDefault (
if !virtualHost.recommendedProxySettings then false
else if cfg.enabled then true
else "nixpkgs"
);
scheme = mkOptionDefault (
mapNullable schemeForUrl config.proxyPass
);
path = mkOptionDefault (
mapNullable pathForUrl config.proxyPass
);
host = mkOptionDefault (
mapNullable hostForUrl config.proxyPass
);
headers = {
enableRecommended = mkIf cfg.enabled (mkAlmostOptionDefault true);
rewriteReferer.enable = mkIf cfg.enabled (mkAlmostOptionDefault true);
};
redirect.enable = mkIf cfg.enabled (mkAlmostOptionDefault true);
};
recommendedProxySettings = mkMerge [
(mkAlmostOptionDefault (config.proxy.headers.enableRecommended == "nixpkgs"))
];
xvars.enable = mkIf cfg.enabled true;
extraConfig = mkMerge [
(mkIf emitVars (
mkBefore xHeadersProxied
mkJustBefore (xHeadersProxied { inherit xvars; })
))
(mkIf emitRedirectScheme ''
proxy_redirect ${config.proxy.scheme}://$host/ $x_scheme://$host/;
'')
(mkIf emitRefererRewrite ''
if ($x_referer_host = $host) {
set $x_referer "${config.proxy.scheme}://${config.proxy.host}/$x_referer_path";
}
'')
(mkIf emitHeaders (mkAlmostAfter ''
if ($x_proxy_host = "") {
set $x_proxy_host $proxy_host;
}
if ($x_proxy_host = "") {
set $x_proxy_host ${config.proxy.host};
}
proxy_set_header Host $x_proxy_host;
proxy_set_header Referer $x_referer;
proxy_set_header X-Real-IP $x_remote_addr;
proxy_set_header X-Forwarded-For $x_forwarded_for;
proxy_set_header X-Forwarded-Proto $x_scheme;
proxy_set_header X-Forwarded-Host $x_forwarded_host;
proxy_set_header X-Forwarded-Server $x_forwarded_server;
''))
];
};
};
hostModule = { config, ... }: let
hostModule = { config, xvars, ... }: let
cfg = config.proxied;
in {
options = with lib.types; {
@ -164,13 +70,6 @@
type = bool;
default = cfg.enable != false;
};
xvars.enable = mkEnableOption "$x_variables" // {
default = cfg.enabled;
};
};
recommendedProxySettings = mkOption {
type = bool;
default = nginx.recommendedProxySettings;
};
locations = mkOption {
type = attrsOf (submoduleWith {
@ -181,23 +80,17 @@
};
config = {
proxied = {
xvars.enable = mkIf (any (loc: loc.proxied.xvars.enable) (attrValues config.locations)) true;
};
xvars.enable = mkIf cfg.enabled true;
local.denyGlobal = mkIf (cfg.enable == "cloudflared") (mkDefault true);
extraConfig = mkIf cfg.xvars.enable (mkBefore ''
${xHeadersDefaults}
${optionalString cfg.enabled xHeadersProxied}
'');
extraConfig = mkIf (cfg.enabled && config.xvars.enable) (
mkOrder (orderJustBefore + 25) (xHeadersProxied { inherit xvars; })
);
};
};
in {
options = with lib.types; {
services.nginx.virtualHosts = mkOption {
type = attrsOf (submoduleWith {
modules = [ hostModule ];
shorthandOnlyDefinesConfig = true;
});
type = attrsOf (submodule [hostModule]);
};
};
}

View file

@ -0,0 +1,214 @@
let
locationModule = { config, name, virtualHost, xvars, gensokyo-zone, lib, ... }: let
inherit (gensokyo-zone.lib) mkJustBefore mkJustAfter mkAlmostOptionDefault mapOptionDefaults coalesce parseUrl;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkOptionDefault;
inherit (lib.attrsets) filterAttrs mapAttrsToList;
inherit (lib.strings) hasPrefix removeSuffix concatStringsSep;
inherit (lib.trivial) mapNullable;
cfg = config.proxy;
in {
options = with lib.types; {
proxy = {
enable = mkEnableOption "proxy";
enabled = mkOption {
type = bool;
readOnly = true;
};
url = mkOption {
type = str;
};
path = mkOption {
type = str;
};
host = mkOption {
type = nullOr str;
};
websocket.enable = mkEnableOption "websocket proxy" // {
default = virtualHost.proxy.websocket.enable;
};
parsed = {
scheme = mkOption {
type = nullOr str;
};
path = mkOption {
type = nullOr str;
};
host = mkOption {
type = nullOr str;
};
hostport = mkOption {
type = nullOr str;
};
port = mkOption {
type = nullOr int;
};
};
headers = {
enableRecommended = mkOption {
type = enum [ true false "nixpkgs" ];
};
rewriteReferer.enable = mkEnableOption "rewrite referer host";
set = mkOption {
type = attrsOf (nullOr str);
};
};
redirect = {
enable = mkEnableOption "proxy_redirect";
fromHost = mkOption {
type = str;
default = xvars.get.host;
example = "xvars.get.proxy_host";
};
fromScheme = mkOption {
type = str;
default = xvars.get.scheme;
example = "xvars.get.proxy_scheme";
};
};
};
};
config = let
emitHeaders = setHeaders' != { };
url = parseUrl config.proxyPass;
recommendedHeaders = {
Host = if cfg.host == null then xvars.get.proxy_host else cfg.host;
Referer = xvars.get.referer;
X-Real-IP = xvars.get.remote_addr;
X-Forwarded-For = xvars.get.forwarded_for;
X-Forwarded-Proto = xvars.get.scheme;
X-Forwarded-Host = xvars.get.host;
X-Forwarded-Server = xvars.get.forwarded_server;
};
initProxyVars = ''
${xvars.init "proxy_scheme" cfg.parsed.scheme}
${xvars.init "proxy_host" "$proxy_host"}
if (${xvars.get.proxy_host} = "") {
${xvars.init "proxy_host" cfg.parsed.hostport}
}
'';
hostHeader = coalesce [
cfg.headers.set.Host or null
cfg.host
xvars.get.proxy_host
];
rewriteReferer = ''
set $x_set_referer ${xvars.get.referer};
if (${xvars.get.referer_host} = $host) {
set $x_set_referer ${config.proxy.parsed.scheme}://${hostHeader}${xvars.get.referer_path};
}
'';
redirect = ''
proxy_redirect ${cfg.redirect.fromScheme}://${cfg.redirect.fromHost}/ ${xvars.get.scheme}://${xvars.get.host}/;
'';
setHeaders' = filterAttrs (_: header: header != null) cfg.headers.set;
setHeaders = concatStringsSep "\n" (mapAttrsToList (
name: value: "proxy_set_header ${name} ${xvars.escapeString value};"
) setHeaders');
in {
proxy = {
enabled = mkOptionDefault (config.proxyPass != null);
path = mkIf (hasPrefix "/" name) (mkOptionDefault name);
url = mkIf (virtualHost.proxy.url != null) (mkOptionDefault virtualHost.proxy.url);
headers = {
enableRecommended = mkOptionDefault (
if cfg.enable && virtualHost.proxy.headers.enableRecommended != false then true
else virtualHost.proxy.headers.enableRecommended
);
set = mkMerge [
(mkOptionDefault { })
(mkIf (cfg.headers.enableRecommended == true) (mapOptionDefaults recommendedHeaders))
(mkIf (cfg.host != null) {
Host = mkIf (cfg.headers.enableRecommended != "nixpkgs") (mkAlmostOptionDefault cfg.host);
})
(mkIf cfg.headers.rewriteReferer.enable {
Referer = mkAlmostOptionDefault "$x_set_referer";
})
(mkIf cfg.websocket.enable (mapOptionDefaults {
Upgrade = "$http_upgrade";
Connection = "upgrade";
}))
];
};
host = mkOptionDefault (
if virtualHost.proxy.host != null then virtualHost.proxy.host
else if cfg.headers.enableRecommended == false then null
else xvars.get.host
);
parsed = {
scheme = mkOptionDefault (
mapNullable (_: url.scheme) config.proxyPass
);
path = mkOptionDefault (
mapNullable (_: url.path) config.proxyPass
);
host = mkOptionDefault (
mapNullable (_: url.host) config.proxyPass
);
hostport = mkOptionDefault (
mapNullable (_: url.hostport) config.proxyPass
);
port = mkOptionDefault (
mapNullable (_: url.port) config.proxyPass
);
};
};
proxyPass = mkIf cfg.enable (mkAlmostOptionDefault (removeSuffix "/" cfg.url + cfg.path));
recommendedProxySettings = mkAlmostOptionDefault (cfg.headers.enableRecommended == "nixpkgs");
extraConfig = mkMerge [
(mkIf (cfg.enabled && virtualHost.xvars.enable) (mkJustBefore initProxyVars))
(mkIf (cfg.enabled && cfg.headers.rewriteReferer.enable) (mkJustBefore rewriteReferer))
(mkIf (cfg.enabled && cfg.redirect.enable) (mkBefore redirect))
(mkIf (cfg.enabled && emitHeaders) (mkJustAfter setHeaders))
];
};
};
hostModule = { config, nixosConfig, lib, ... }: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) attrValues;
inherit (lib.lists) any;
inherit (nixosConfig.services) nginx;
cfg = config.proxy;
in {
options = with lib.types; {
proxy = {
host = mkOption {
type = nullOr str;
default = null;
};
url = mkOption {
type = nullOr str;
default = null;
};
websocket.enable = mkEnableOption "websocket proxy";
headers.enableRecommended = mkOption {
type = enum [ true false "nixpkgs" ];
default = if nginx.recommendedProxySettings then "nixpkgs" else false;
};
};
locations = mkOption {
type = attrsOf (submoduleWith {
modules = [ locationModule ];
shorthandOnlyDefinesConfig = true;
});
};
};
config = let
needsReferer = loc: loc.proxy.enabled && loc.proxy.headers.rewriteReferer.enable;
in {
xvars.parseReferer = mkIf (any needsReferer (attrValues config.locations)) true;
};
};
in {
lib,
...
}: let
inherit (lib.options) mkOption;
in {
options = with lib.types; {
services.nginx.virtualHosts = mkOption {
type = attrsOf (submodule [hostModule]);
};
};
}

View file

@ -10,9 +10,11 @@
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.trivial) warnIf;
inherit (config.services) nginx;
forceRedirectConfig = virtualHost: ''
if ($x_scheme = http) {
return ${toString virtualHost.redirectCode} https://$x_forwarded_host$request_uri;
forceRedirectConfig = virtualHost: let
xvars = virtualHost.xvars.lib;
in ''
if (${xvars.get.scheme} = http) {
return ${toString virtualHost.redirectCode} https://${xvars.get.host}$request_uri;
}
'';
locationModule = { config, virtualHost, ... }: let
@ -23,7 +25,7 @@
force = mkEnableOption "redirect to SSL";
};
config = {
proxied.xvars.enable = mkIf emitForce true;
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig virtualHost);
};
};
@ -119,7 +121,7 @@
sslCertificate = mkIf (cfg.cert.path != null) (mkAlmostOptionDefault cfg.cert.path);
sslCertificateKey = mkIf (cfg.cert.keyPath != null) (mkAlmostOptionDefault cfg.cert.keyPath);
proxied.xvars.enable = mkIf emitForce true;
xvars.enable = mkIf emitForce true;
extraConfig = mkIf emitForce (forceRedirectConfig config);
};
};

View file

@ -8,11 +8,11 @@
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkAfter mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) toLower replaceStrings;
inherit (lib.strings) toLower replaceStrings removePrefix;
inherit (config) networking;
inherit (config.services) vouch-proxy nginx tailscale;
inherit (nginx) vouch;
locationModule = {config, virtualHost, ...}: {
locationModule = {config, virtualHost, xvars, ...}: {
options.vouch = with lib.types; {
requireAuth = mkEnableOption "require auth to access this location";
setProxyHeader = mkOption {
@ -33,20 +33,19 @@
(mkBefore virtualHost.vouch.auth.lua.accessLogic)
];
};
proxied.xvars.enable = mkIf (enableVouchTail || virtualHost.vouch.auth.lua.enable) true;
xvars.enable = mkIf (enableVouchTail || virtualHost.vouch.auth.lua.enable) true;
proxy.headers.set.X-Vouch-User = mkOptionDefault "$auth_resp_x_vouch_user";
extraConfig = assert virtualHost.vouch.enable; mkMerge [
(mkIf (!virtualHost.vouch.requireAuth) virtualHost.vouch.auth.requestDirective)
(allowOrigin vouch.url)
(allowOrigin vouch.authUrl)
(mkIf enableVouchLocal (allowOrigin vouch.localUrl))
(mkIf enableVouchTail (allowOrigin "$x_scheme://${vouch.tailDomain}"))
(mkIf config.vouch.setProxyHeader ''
proxy_set_header X-Vouch-User $auth_resp_x_vouch_user;
'')
(mkIf enableVouchLocal (allowOrigin "sso.local.${networking.domain}"))
(mkIf enableVouchTail (allowOrigin "${xvars.get.scheme}://${vouch.tailDomain}"))
];
};
};
hostModule = {config, ...}: let
hostModule = {config, xvars, ...}: let
cfg = config.vouch;
mkHeaderVar = header: toLower (replaceStrings [ "-" ] [ "_" ] header);
mkUpstreamVar = header: "\$upstream_http_${mkHeaderVar header}";
@ -113,7 +112,7 @@
if ngx.ctx.auth_res ~= nil and ngx.ctx.auth_res.status == ngx.HTTP_UNAUTHORIZED then
local vouch_url = ngx.var["vouch_url"] or "${vouch.url}"
local query_args = ngx.encode_args {
url = string.format("%s://%s%s", ngx.var.x_scheme, ngx.var.x_forwarded_host, ngx.var.request_uri),
url = string.format("%s://%s%s", ngx.var.${removePrefix "$" xvars.get.scheme}, ngx.var.${removePrefix "$" xvars.get.host}, ngx.var.request_uri),
["X-Vouch-Token"] = ngx.ctx.auth_res.header["X-Vouch-Token"] or "",
error = ngx.ctx.auth_res.header["X-Vouch-Error"] or "",
-- ["vouch-failcount"] is now a session variable and shouldn't be needed anymore
@ -145,13 +144,13 @@
};
extraConfig = let
localVouchUrl = ''
if ($x_forwarded_host ~ "\.local\.${networking.domain}$") {
if (${xvars.get.host} ~ "\.local\.${networking.domain}$") {
set $vouch_url ${vouch.localUrl};
}
'';
tailVouchUrl = ''
if ($x_forwarded_host ~ "\.tail\.${networking.domain}$") {
set $vouch_url $x_scheme://${vouch.tailDomain};
if (${xvars.get.host} ~ "\.tail\.${networking.domain}$") {
set $vouch_url ${xvars.get.scheme}://${vouch.tailDomain};
}
'';
setVouchUrl = [
@ -170,33 +169,32 @@
mkBefore "auth_request_set \$${authVar} ${mkUpstreamVar header};"
)) cfg.auth.variables
));
proxied.xvars.enable = mkIf cfg.enable true;
xvars.enable = mkIf cfg.enable true;
locations = mkIf cfg.enable {
"/" = mkIf cfg.requireAuth {
vouch.requireAuth = mkAlmostOptionDefault true;
};
${cfg.auth.errorLocation} = mkIf (cfg.auth.errorLocation != null) {
proxied.xvars.enable = true;
xvars.enable = true;
extraConfig = ''
return 302 $vouch_url/login?url=$x_scheme://$x_forwarded_host$request_uri&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
return 302 $vouch_url/login?url=${xvars.get.scheme}://${xvars.get.host}$request_uri&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
'';
};
${cfg.auth.requestLocation} = { config, ... }: {
proxyPass = "${vouch.proxyOrigin}/validate";
proxy.headers.enableRecommended = false;
proxied.rewriteReferer = false;
extraConfig = let
${cfg.auth.requestLocation} = { config, xvars, ... }: {
proxy = {
enable = true;
url = vouch.proxyOrigin;
# nginx-proxied vouch must use X-Forwarded-Host, but vanilla vouch requires Host
vouchProxyHost = if vouch.doubleProxy.enable
host = if vouch.doubleProxy.enable
then (if cfg.localSso.enable then vouch.doubleProxy.localServerName else vouch.doubleProxy.serverName)
else "$x_forwarded_host";
in ''
proxy_set_header Host ${vouchProxyHost};
proxy_set_header X-Forwarded-Host $x_forwarded_host;
proxy_set_header Referer $x_referer;
proxy_set_header X-Forwarded-Proto $x_scheme;
else xvars.get.host;
headers = {
set.Content-Length = "";
rewriteReferer.enable = false;
};
};
extraConfig = ''
proxy_pass_request_body off;
proxy_set_header Content-Length "";
'';
};
};

View file

@ -1,30 +0,0 @@
{lib, ...}: let
inherit (lib.modules) mkIf;
inherit (lib.options) mkOption mkEnableOption;
wsModule = {config, ...}: {
options = with lib.types; {
proxy.websocket.enable = mkEnableOption "websocket proxy";
};
config = mkIf config.proxy.websocket.enable {
extraConfig = ''
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
'';
};
};
hostModule = {config, ...}: {
imports = [wsModule];
options = with lib.types; {
locations = mkOption {
type = attrsOf (submodule wsModule);
};
};
};
in {
options = with lib.types; {
services.nginx.virtualHosts = mkOption {
type = attrsOf (submodule hostModule);
};
};
}

View file

@ -0,0 +1,110 @@
let
locationModule = { config, virtualHost, lib, ... }: let
inherit (lib.options) mkEnableOption;
cfg = config.xvars;
in {
options.xvars = with lib.types; {
enable = mkEnableOption "$x_variables";
};
config = let
in {
};
};
hostModule = { config, nixosConfig, gensokyo-zone, xvars, lib, ... }: let
inherit (gensokyo-zone.lib) mkJustBefore;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault;
inherit (lib.strings) concatStringsSep;
inherit (lib.attrsets) attrValues filterAttrs mapAttrs mapAttrsToList;
inherit (lib.lists) any;
cfg = config.xvars;
escapeString = value: if value == "" then ''""'' else value;
in {
options = with lib.types; {
xvars = {
enable = mkEnableOption "$x_variables";
parseReferer = mkEnableOption "$x_referer_{scheme,host,path}";
defaults = mkOption {
type = attrsOf (nullOr str);
default = rec {
scheme = "$scheme";
forwarded_for = remote_addr;
remote_addr = "$remote_addr";
forwarded_server = host;
host = "$host";
referer = "$http_referer";
proxy_host = null;
proxy_scheme = null;
};
};
lib = mkOption {
type = attrs;
};
};
locations = mkOption {
type = attrsOf (submoduleWith {
modules = [ locationModule ];
shorthandOnlyDefinesConfig = true;
specialArgs = {
inherit nixosConfig gensokyo-zone xvars;
virtualHost = config;
};
});
};
};
config = let
defaults = concatStringsSep "\n" (mapAttrsToList (
name: value: "set $x_${name} ${escapeString value};"
) (filterAttrs (_: value: value != null) cfg.defaults));
parseReferer = ''
if (${xvars.get.referer} ~ "^(https?)://([^/]*)(/.*)$") {
${xvars.init "referer_scheme" "$1"}
${xvars.init "referer_host" "$2"}
${xvars.init "referer_path" "$3"}
}
'';
in {
xvars = {
enable = mkMerge [
(mkIf (any (loc: loc.xvars.enable) (attrValues config.locations)) true)
(mkIf cfg.parseReferer true)
];
defaults = mkIf cfg.parseReferer (mkOptionDefault {
referer_scheme = null;
referer_host = null;
referer_path = null;
});
lib = {
get = mapAttrs (name: default: if cfg.enable then "$x_${name}" else assert default != null; default) cfg.defaults;
init = name: value: assert cfg.enable && cfg.defaults ? ${name}; "set $x_${name} ${escapeString value};";
inherit escapeString;
};
};
extraConfig = mkMerge [
(mkIf cfg.enable (mkJustBefore defaults))
(mkIf (cfg.enable && cfg.parseReferer) (mkJustBefore parseReferer))
];
_module.args.xvars = config.xvars.lib;
};
};
in {
config,
lib,
gensokyo-zone,
...
}: let
inherit (lib.options) mkOption;
in {
options = with lib.types; {
services.nginx.virtualHosts = mkOption {
type = attrsOf (submoduleWith {
modules = [ hostModule ];
shorthandOnlyDefinesConfig = true;
specialArgs = {
inherit gensokyo-zone;
nixosConfig = config;
};
});
};
};
}