177 lines
6.9 KiB
Nix
177 lines
6.9 KiB
Nix
{ config, lib, libS, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.services.nginx;
|
|
in
|
|
{
|
|
options.services.nginx = {
|
|
allCompression = libS.mkOpinionatedOption "set all recommended compression options";
|
|
|
|
default404Server = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = lib.mdDoc ''
|
|
Wether to add a default server which always responds with 404.
|
|
This is useful when using a wildcard cname with a wildcard certitificate to not return the first server entry in the config on unknown subdomains
|
|
or to do the same for an old and not fully removed domain.
|
|
'';
|
|
};
|
|
|
|
acmeHost = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = lib.mdDoc ''
|
|
The acme host to use for the default 404 server.
|
|
'';
|
|
};
|
|
};
|
|
|
|
generateDhparams = libS.mkOpinionatedOption "generate more secure, 2048 bits dhparams replacing the default 1024 bits";
|
|
|
|
openFirewall = libS.mkOpinionatedOption "open the firewall port for the http (80) and https (443) default ports";
|
|
|
|
quic = {
|
|
enable = lib.mkEnableOption (lib.mdDoc "quic support in nginx");
|
|
|
|
bpf = libS.mkOpinionatedOption "configure nginx' bpf support which routes quic packets from the same source to the same worker";
|
|
};
|
|
|
|
recommendedDefaults = libS.mkOpinionatedOption "set recommended performance options not grouped into other settings";
|
|
|
|
resolverAddrFromNameserver = libS.mkOpinionatedOption "set resolver address to environment.nameservers";
|
|
|
|
rotateLogsFaster = libS.mkOpinionatedOption "keep logs only for 7 days and rotate them daily";
|
|
|
|
setHSTSHeader = libS.mkOpinionatedOption "add the HSTS header to all virtual hosts";
|
|
|
|
tcpFastOpen = libS.mkOpinionatedOption "enable tcp fast open";
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = cfg.quic.enable && cfg.quic.bpf -> !lib.versionOlder cfg.package.version "1.25.0";
|
|
message = "Setting services.nginx.quic.bpf to true requires nginx version 1.25.0 or newer, but currently \"${cfg.package.version}\" is used!";
|
|
}
|
|
];
|
|
|
|
boot.kernel.sysctl = lib.mkIf cfg.tcpFastOpen {
|
|
# enable tcp fastopen for outgoing and incoming connections
|
|
"net.ipv4.tcp_fastopen" = 3;
|
|
};
|
|
|
|
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ 80 443 ];
|
|
|
|
nixpkgs.overlays = lib.mkIf cfg.tcpFastOpen [
|
|
(final: prev:
|
|
let
|
|
configureFlags = [ "-DTCP_FASTOPEN=23" ];
|
|
in
|
|
{
|
|
nginx = prev.nginx.override { inherit configureFlags; };
|
|
nginxQuic = prev.nginxQuic.override { inherit configureFlags; };
|
|
nginxStable = prev.nginxStable.override { inherit configureFlags; };
|
|
nginxMainline = prev.nginxMainline.override { inherit configureFlags; };
|
|
})
|
|
];
|
|
|
|
services = {
|
|
logrotate.settings.nginx = lib.mkIf cfg.rotateLogsFaster {
|
|
frequency = "daily";
|
|
rotate = 7;
|
|
};
|
|
|
|
# NOTE: do not use mkMerge here to prevent infinite recursions
|
|
nginx = {
|
|
appendConfig = lib.optionalString (cfg.quic.enable && cfg.quic.bpf) /* nginx */ ''
|
|
quic_bpf on;
|
|
'' + lib.optionalString cfg.recommendedDefaults /* nginx */ ''
|
|
worker_processes auto;
|
|
worker_cpu_affinity auto;
|
|
'';
|
|
|
|
commonHttpConfig = lib.optionalString cfg.recommendedDefaults /* nginx */ ''
|
|
error_log syslog:server=unix:/dev/log;
|
|
'' + lib.optionalString cfg.quic.enable /* nginx */''
|
|
quic_retry on;
|
|
'' + lib.optionalString cfg.recommendedZstdSettings /* nginx */ ''
|
|
# TODO: upstream this?
|
|
zstd_types application/x-nix-archive;
|
|
'';
|
|
|
|
commonServerConfig = lib.mkIf cfg.setHSTSHeader /* nginx */ ''
|
|
more_set_headers "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload";
|
|
'';
|
|
|
|
package = lib.mkIf cfg.quic.enable pkgs.nginxQuic; # based on pkgs.nginxMainline
|
|
|
|
recommendedBrotliSettings = lib.mkIf cfg.allCompression true;
|
|
recommendedGzipSettings = lib.mkIf cfg.allCompression true;
|
|
recommendedOptimisation = lib.mkIf cfg.allCompression true;
|
|
recommendedProxySettings = lib.mkIf cfg.allCompression true;
|
|
recommendedTlsSettings = lib.mkIf cfg.allCompression true;
|
|
recommendedZstdSettings = lib.mkIf cfg.allCompression true;
|
|
|
|
resolver.addresses =
|
|
let
|
|
isIPv6 = addr: builtins.match ".*:.*:.*" addr != null;
|
|
escapeIPv6 = addr:
|
|
if isIPv6 addr then
|
|
"[${addr}]"
|
|
else
|
|
addr;
|
|
in
|
|
lib.optionals (cfg.resolverAddrFromNameserver && config.networking.nameservers != [ ]) (map escapeIPv6 config.networking.nameservers);
|
|
sslDhparam = lib.mkIf cfg.generateDhparams config.security.dhparams.params.nginx.path;
|
|
|
|
# NOTE: do not use mkMerge here to prevent infinite recursions
|
|
virtualHosts =
|
|
let
|
|
extraParameters = [
|
|
# net.core.somaxconn is set to 4096
|
|
# see https://www.nginx.com/blog/tuning-nginx/#:~:text=to%20a%20value-,greater%20than%20512,-%2C%20change%20the%20backlog
|
|
"backlog=1024"
|
|
|
|
"deferred"
|
|
"fastopen=256" # requires nginx to be compiled with -DTCP_FASTOPEN=23
|
|
];
|
|
in
|
|
lib.mkIf (cfg.recommendedDefaults || cfg.default404Server.enable || cfg.quic.enable) {
|
|
"_" = {
|
|
kTLS = lib.mkIf cfg.recommendedDefaults true;
|
|
reuseport = lib.mkIf (cfg.recommendedDefaults || cfg.quic.enable) true;
|
|
|
|
default = lib.mkIf cfg.default404Server.enable true;
|
|
forceSSL = lib.mkIf cfg.default404Server.enable true;
|
|
useACMEHost = lib.mkIf cfg.default404Server.enable cfg.default404Server.acmeHost;
|
|
extraConfig = lib.mkIf cfg.default404Server.enable /* nginx */ ''
|
|
return 404;
|
|
'';
|
|
|
|
listen = lib.mkIf cfg.tcpFastOpen (lib.mkDefault [
|
|
{ addr = "0.0.0.0"; port = 80; inherit extraParameters; }
|
|
{ addr = "0.0.0.0"; port = 443; ssl = true; inherit extraParameters; }
|
|
{ addr = "[::]"; port = 80; inherit extraParameters; }
|
|
{ addr = "[::]"; port = 443; ssl = true; inherit extraParameters; }
|
|
]);
|
|
|
|
quic = lib.mkIf cfg.quic.enable true;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
security.dhparams = lib.mkIf cfg.generateDhparams {
|
|
enable = cfg.generateDhparams;
|
|
params.nginx = { };
|
|
};
|
|
|
|
systemd.services.nginx.serviceConfig = lib.mkIf (cfg.quic.enable && cfg.quic.bpf) {
|
|
# NOTE: CAP_BPF is included in CAP_SYS_ADMIN but it is not enough alone
|
|
AmbientCapabilities = [ "CAP_BPF" "CAP_NET_ADMIN" "CAP_SYS_ADMIN" ];
|
|
CapabilityBoundingSet = [ "CAP_BPF" "CAP_NET_ADMIN" "CAP_SYS_ADMIN" ];
|
|
SystemCallFilter = [ "bpf" ];
|
|
};
|
|
};
|
|
}
|