nix-config/modules/nixos-modules/server/reverseProxy/reverseProxy.nix

176 lines
6.4 KiB
Nix

{
lib,
config,
...
}: {
options.services.reverseProxy = {
enable = lib.mkEnableOption "turn on the reverse proxy";
openFirewall = lib.mkEnableOption "open the firewall";
refuseUnmatchedDomains = lib.mkOption {
type = lib.types.bool;
description = "refuse connections for domains that don't match any configured virtual hosts";
default = true;
};
ports = {
http = lib.mkOption {
type = lib.types.port;
description = "HTTP port for the reverse proxy";
default = 80;
};
https = lib.mkOption {
type = lib.types.port;
description = "HTTPS port for the reverse proxy";
default = 443;
};
};
acme = {
enable = lib.mkOption {
type = lib.types.bool;
description = "enable ACME certificate management";
default = true;
};
email = lib.mkOption {
type = lib.types.str;
description = "email address for ACME certificate registration";
};
};
services = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
target = lib.mkOption {
type = lib.types.str;
description = "what url will all traffic to this application be forwarded to";
};
domain = lib.mkOption {
type = lib.types.str;
description = "what is the default subdomain to be used for this application to be used for";
default = name;
};
extraDomains = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "extra domains that should be configured for this domain";
default = [];
};
settings = {
certificateRenewal.enable = lib.mkOption {
type = lib.types.bool;
description = "auto renew certificates";
default = true;
};
forceSSL.enable = lib.mkOption {
type = lib.types.bool;
description = "auto renew certificates";
default = true;
};
proxyHeaders = {
enable = lib.mkEnableOption "should we proxy headers";
timeout = lib.mkOption {
type = lib.types.int;
default = 60;
};
};
proxyWebsockets.enable = lib.mkEnableOption "should the default config proxy websockets";
forwardHeaders.enable = lib.mkEnableOption "should the default config contain forward headers";
noSniff.enable = lib.mkEnableOption "should the no sniff flags be set";
proxyBuffering.enable = lib.mkOption {
type = lib.types.bool;
description = "should proxy buffering be enabled";
default = true;
};
maxBodySize = lib.mkOption {
type = lib.types.nullOr lib.types.int;
description = "";
default = null;
};
};
};
}));
};
};
config = let
httpPort = config.services.reverseProxy.ports.http;
httpsPort = config.services.reverseProxy.ports.https;
in
lib.mkIf config.services.reverseProxy.enable {
security.acme = lib.mkIf config.services.reverseProxy.acme.enable {
acceptTerms = true;
defaults.email = config.services.reverseProxy.acme.email;
};
services.nginx = {
enable = true;
virtualHosts = lib.mkMerge (
(lib.optionals config.services.reverseProxy.refuseUnmatchedDomains [
{
"_" = {
default = true;
serverName = "_";
locations."/" = {
extraConfig = ''
return 444;
'';
};
};
}
])
++ lib.lists.flatten (
lib.attrsets.mapAttrsToList (
name: service: let
hostConfig = {
forceSSL = service.settings.forceSSL.enable;
enableACME = service.settings.certificateRenewal.enable;
locations = {
"/" = {
proxyPass = service.target;
proxyWebsockets = service.settings.proxyWebsockets.enable;
recommendedProxySettings = service.settings.forwardHeaders.enable;
extraConfig = let
# Client upload size configuration
maxBodySizeConfig =
lib.optionalString (service.settings.maxBodySize != null)
"client_max_body_size ${toString service.settings.maxBodySize}M;";
# Security header configuration
noSniffConfig =
lib.optionalString service.settings.noSniff.enable
"add_header X-Content-Type-Options nosniff;";
# Proxy buffering configuration
proxyBufferingConfig =
lib.optionalString (!service.settings.proxyBuffering.enable)
"proxy_buffering off;";
# Proxy timeout configuration
proxyTimeoutConfig =
lib.optionalString service.settings.proxyHeaders.enable
''
proxy_read_timeout ${toString service.settings.proxyHeaders.timeout}s;
proxy_connect_timeout ${toString service.settings.proxyHeaders.timeout}s;
proxy_send_timeout ${toString service.settings.proxyHeaders.timeout}s;
'';
in
maxBodySizeConfig + noSniffConfig + proxyBufferingConfig + proxyTimeoutConfig;
};
};
};
in (
[
{
${service.domain} = hostConfig;
}
]
++ builtins.map (domain: {${domain} = hostConfig;})
service.extraDomains
)
)
config.services.reverseProxy.services
)
);
};
networking.firewall.allowedTCPPorts = lib.mkIf config.services.reverseProxy.openFirewall [
httpPort
httpsPort
];
};
}