Watch
0
0
Fork
You've already forked hyperhive
0

swarm-logs-auth: rate-limit verification and cache positive verdicts

Any request on logs.<swarm> carrying a well-formed swarm-agent bearer
made swarm-logs-auth do a bao cert login and KV read before it could
refuse, with no cap on the rate.

nginx now applies limit_req (zone swarm_logs_agent_auth, keyed by
client address, 5r/s burst 20 nodelay by default) on the internal
location that forwards to swarm-logs-auth, answering 403 when over,
since auth_request passes through only 401/403 and swarm-logs retries
a 401. The authelia path and the query location carry no limit.

swarm-logs-auth remembers bearers it has granted, keyed by SHA-256 of
the bearer, for --verdict-ttl seconds (deploy.victorialogs.authVerdictTtl,
60 by default), holding at most 4096 and evicting the oldest. Denials
are never remembered. A replaced or deleted queue secret therefore
keeps working at the log store for up to the TTL after its last check.

Closes #5190
This commit is contained in:
atlas 2026-10-13 09:06:20 +02:00 • committed by mara
commit 8be4030d03
9 changed files with 367 additions and 51 deletions

View file

@ -142,6 +142,46 @@ in
'';
};
options.services.hyperhive.deploy.victorialogs.authVerdictTtl = lib.mkOption {
type = lib.types.ints.unsigned;
default = 60;
description = ''
Seconds `swarm-logs-auth` grants an agent bearer it has verified again
without reading the secret store. A denial is never remembered.
This is also how long a re-minted or deleted queue secret keeps working
at the log store's query route after its last successful check. `0`
checks the store on every query.
'';
};
options.services.hyperhive.deploy.victorialogs.authRate = lib.mkOption {
type = lib.types.str;
default = "5r/s";
example = "30r/m";
description = ''
nginx `limit_req` rate, per client address, of `/select/logsql/`
requests carrying a `swarm-agent.` bearer, which nginx checks with
`swarm-logs-auth`. Each such request that the responder has not granted
within {option}`services.hyperhive.deploy.victorialogs.authVerdictTtl`
costs a secret store login and read, whether the bearer is right or not.
Counts every agent query, granted or not, so it is sized for the agents
that share one address querying at once, not for one agent. Requests
over the rate and its burst are answered 403.
'';
};
options.services.hyperhive.deploy.victorialogs.authBurst = lib.mkOption {
type = lib.types.ints.positive;
default = 20;
description = ''
Requests a client address may make above
{option}`services.hyperhive.deploy.victorialogs.authRate` before nginx
answers 403, served without delay.
'';
};
config = lib.mkIf deployCfg.victorialogs.enable {
assertions = [
{
@ -170,6 +210,7 @@ in
"~^Bearer swarm-agent\." /__swarm_logs_agent_auth;
default /__hive_authelia;
}
limit_req_zone $binary_remote_addr zone=swarm_logs_agent_auth:1m rate=${deployCfg.victorialogs.authRate};
'';
# This store publishes its own health as prometheus metrics on the same
@ -280,10 +321,18 @@ in
};
# The `Authorization` header rides the subrequest unchanged.
#
# Any well-formed agent bearer the responder has not recently granted
# costs a secret store login and read, so the rate per client address
# is capped here, ahead of the responder. 403 when over: the only
# statuses `auth_request` passes through are 401 and 403, and
# `swarm-logs` retries a 401.
"= /__swarm_logs_agent_auth" = {
proxyPass = "http://unix:${logsAuthSocket}:/";
extraConfig = ''
internal;
limit_req zone=swarm_logs_agent_auth burst=${toString deployCfg.victorialogs.authBurst} nodelay;
limit_req_status 403;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
'';
@ -331,6 +380,7 @@ in
"--socket ${logsAuthSocket}"
# The role ./swarm-bao.nix writes on the store's `cert` mount.
"--store-cert-role swarm-logs-auth"
"--verdict-ttl ${toString deployCfg.victorialogs.authVerdictTtl}"
];
# Paths only: `argv` is world-readable via /proc/<pid>/cmdline.
LoadCredential = lib.mapAttrsToList (id: path: "${id}:${path}") logsAuthStoreFiles;