mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
Adds an nginx-side rate limit on POST /login so a brute-force or DoS attempt can't pin the box on the bcrypt-shaped credential check inside rousette/PAM. Configuration: - Keyed on $binary_remote_addr (per source IP), 5 requests/minute sustained with a burst of 3 nodelay, a real user fumbling their password three times in a row sails through; the 4th attempt in the same window returns 429. - GET /login is unmetered (the keyed map yields the empty string, which limit_req treats as "no key, no limit"), so a 401-driven HX-Redirect bouncing the user back to the login page doesn't eat into the budget. - 32k zone (~500 IPs, the minimum nginx accepts). At cache saturation an attacker rotating addresses still tops out around ~2500 attempts/min, all serialised through bcrypt one at a time. - 429 instead of the default 503 keeps the response out of the /50x.html error_page rewrite (which auto-refreshes and would loop a throttled client straight back to /login). Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
45 lines
1.4 KiB
Plaintext
45 lines
1.4 KiB
Plaintext
# Throttle POST /login so a brute-force attempt can't pin the box on
|
|
# the bcrypt-shaped credential check inside rousette/PAM. GET is left
|
|
# unmetered: page loads after a 401-driven HX-Redirect shouldn't eat
|
|
# into the budget.
|
|
#
|
|
# Zone is 32k (~500 IPs) — the minimum nginx accepts via shared-memory
|
|
# allocation. With the cache full an attacker rotating source addresses
|
|
# still tops out at ~2500 attempts/min total, all serialised through
|
|
# bcrypt one at a time.
|
|
#
|
|
# 429 instead of the default 503 keeps the rate-limit response out of
|
|
# the /50x.html error_page rewrite below (which auto-refreshes — would
|
|
# loop a throttled client straight back to /login).
|
|
map $request_method $webui_login_key {
|
|
POST $binary_remote_addr;
|
|
default "";
|
|
}
|
|
limit_req_zone $webui_login_key zone=webui_login:32k rate=5r/m;
|
|
limit_req_status 429;
|
|
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name _;
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
server_name _;
|
|
include ssl.conf;
|
|
|
|
# 404 also points at /50x.html: the page is a "Loading…" screen
|
|
# with a meta-refresh, so the early-boot window where the Go
|
|
# backend isn't up yet (and any other transient 404 / 5xx) self-
|
|
# recovers as soon as upstream comes back.
|
|
error_page 404 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root html;
|
|
}
|
|
|
|
include /etc/nginx/app/*.conf;
|
|
}
|