feat(nginx): add rate limiting with per-domain overrides
- Global rate limit: 10 req/s with burst of 20 - Connection limit: 30 concurrent per IP - Per-domain override support (requests, burst, enable/disable) - SearXNG gets higher limits (20/40) to tolerate bot traffic - Returns 429 when rate limited
This commit is contained in:
parent
2bc375ab86
commit
790501d290
2 changed files with 65 additions and 2 deletions
|
|
@ -101,11 +101,19 @@
|
|||
myModules.nginx = {
|
||||
enable = true;
|
||||
email = "info@ashisgreat.xyz";
|
||||
rateLimit = {
|
||||
enable = true;
|
||||
requests = 10;
|
||||
burst = 20;
|
||||
};
|
||||
domains = {
|
||||
"search.ashisgreat.xyz" = {
|
||||
port = 8888;
|
||||
# SearXNG sets its own CSP in settings.yml — omit at Nginx level to avoid conflicts
|
||||
contentSecurityPolicy = null;
|
||||
# Search engine — slightly more permissive for bot traffic
|
||||
rateLimit.requests = 20;
|
||||
rateLimit.burst = 40;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Nginx Reverse Proxy Module
|
||||
# Provides: Nginx with automatic Let's Encrypt certificates and security headers
|
||||
# Provides: Nginx with automatic Let's Encrypt certificates, security headers, and rate limiting
|
||||
#
|
||||
# Usage:
|
||||
# myModules.nginx = {
|
||||
|
|
@ -32,6 +32,28 @@ in
|
|||
description = "Email address for Let's Encrypt registration";
|
||||
};
|
||||
|
||||
rateLimit = {
|
||||
enable = lib.mkEnableOption "Nginx rate limiting";
|
||||
|
||||
zone = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "10m";
|
||||
description = "Size of the shared memory zone for rate limiting";
|
||||
};
|
||||
|
||||
requests = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 10;
|
||||
description = "Number of requests allowed per second (burst applies on top)";
|
||||
};
|
||||
|
||||
burst = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 20;
|
||||
description = "Maximum burst of requests allowed beyond the rate";
|
||||
};
|
||||
};
|
||||
|
||||
domains = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
|
|
@ -52,6 +74,26 @@ in
|
|||
description = "Content-Security-Policy header value. Set to null to omit.";
|
||||
};
|
||||
|
||||
rateLimit = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.bool;
|
||||
default = null;
|
||||
description = "Enable rate limiting for this vhost. Defaults to global rateLimit.enable.";
|
||||
};
|
||||
|
||||
requests = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.int;
|
||||
default = null;
|
||||
description = "Requests per second for this vhost. Defaults to global rateLimit.requests.";
|
||||
};
|
||||
|
||||
burst = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.int;
|
||||
default = null;
|
||||
description = "Burst size for this vhost. Defaults to global rateLimit.burst.";
|
||||
};
|
||||
};
|
||||
|
||||
extraLocations = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
|
|
@ -98,6 +140,14 @@ in
|
|||
recommendedProxySettings = true;
|
||||
recommendedTlsSettings = true;
|
||||
|
||||
# Rate limiting zones (one per domain for per-domain limits)
|
||||
commonHttpConfig = lib.optionalString cfg.rateLimit.enable ''
|
||||
# Global rate limiting zone
|
||||
limit_req_zone $binary_remote_addr zone=global:10m rate=${toString cfg.rateLimit.requests}r/s;
|
||||
# Limit connection flooding
|
||||
limit_conn_zone $binary_remote_addr zone=connlimit:10m;
|
||||
'';
|
||||
|
||||
virtualHosts = lib.mapAttrs' (domain: opts: {
|
||||
name = domain;
|
||||
value = {
|
||||
|
|
@ -127,7 +177,12 @@ in
|
|||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString opts.port}";
|
||||
extraConfig = opts.extraConfig;
|
||||
extraConfig = opts.extraConfig + lib.optionalString (if opts.rateLimit.enable != null then opts.rateLimit.enable else cfg.rateLimit.enable) ''
|
||||
# Rate limiting
|
||||
limit_req zone=global burst=${toString (if opts.rateLimit.burst != null then opts.rateLimit.burst else cfg.rateLimit.burst)} nodelay;
|
||||
limit_conn connlimit 30;
|
||||
limit_req_status 429;
|
||||
'';
|
||||
};
|
||||
} // lib.mapAttrs' (locPath: locOpts: {
|
||||
name = locPath;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue