swarm-logs-auth: check agent tokens on the log store's query route
A new `auth_request` responder on the log store's host. nginx sends it every `/select/logsql/` request whose `Authorization` is `Bearer swarm-agent.<agent>.<secret>`; it answers 200 when the secret is the one stored at `swarm/agents/<agent>/queue` (`swarm-agent-verify`), 401 otherwise. Every other bearer still goes to authelia, so agents still presenting the hive's shared OIDC client keep working. `auth_request` takes a fixed URI, so the split is a `map` on `$http_authorization` plus an internal location that rewrites to the map's pick. Checked against nginx 1.30.5 with stub upstreams: an agent token only the stub validator accepts gets 200, a token only the stub authelia accepts gets 200, every wrong one 401, and the internal locations 404 from outside. The responder serves a unix socket in its own traverse-only runtime directory and reads the store under its own leaf (`swarm-logs-auth`), whose policy is `swarm/agents/+/queue`, read-only. glue-bao-tls mints the leaf where the store runs; elsewhere the operator places it, and eval refuses a log store host without one. Part of #4459.
This commit is contained in:
parent
e7dbb94f28
commit
1f4736e79b
16 changed files with 515 additions and 9 deletions
|
|
@ -504,6 +504,44 @@ let
|
|||
&& !(lib.hasInfix "$args" q.extraConfig)
|
||||
&& !(lib.hasInfix "?" q.proxyPass);
|
||||
}
|
||||
{
|
||||
# `auth_request` takes a fixed URI, so the split between the agents'
|
||||
# responder and authelia is a map plus a rewrite in an internal
|
||||
# location. Every link of that chain is pinned: the map keys on the
|
||||
# agent-token prefix and falls back to authelia, the query location
|
||||
# names the switch, the switch rewrites to the map's pick, and the
|
||||
# agent target reaches the responder's socket, which the unit binds.
|
||||
name = "the machine query location sends agent bearers to swarm-logs-auth and the rest to authelia";
|
||||
ok =
|
||||
let
|
||||
l = allLocal.services.nginx.virtualHosts."logs.t.local".locations;
|
||||
http = allLocal.services.nginx.appendHttpConfig;
|
||||
unit = allLocal.systemd.services.swarm-logs-auth;
|
||||
in
|
||||
lib.hasInfix "map \$http_authorization \$swarm_logs_auth_target" http
|
||||
&& lib.hasInfix "\"~^Bearer swarm-agent\\.\" /__swarm_logs_agent_auth;" http
|
||||
&& lib.hasInfix "default /__hive_authelia;" http
|
||||
&& lib.hasInfix "auth_request /__swarm_logs_auth;" l."^~ /select/logsql/".extraConfig
|
||||
&& lib.hasInfix "rewrite ^ \$swarm_logs_auth_target last;" l."= /__swarm_logs_auth".extraConfig
|
||||
&& lib.hasInfix "internal;" l."= /__swarm_logs_agent_auth".extraConfig
|
||||
&& l."= /__swarm_logs_agent_auth".proxyPass == "http://unix:/run/swarm-logs-auth/auth.sock:/"
|
||||
&& lib.hasInfix "--socket /run/swarm-logs-auth/auth.sock" unit.serviceConfig.ExecStart
|
||||
&& l ? "= /__hive_authelia";
|
||||
}
|
||||
{
|
||||
# The responder reads the store as itself, with the leaf the store's
|
||||
# host mints for it.
|
||||
name = "swarm-logs-auth presents its own store leaf";
|
||||
ok =
|
||||
let
|
||||
v = allLocal.services.hyperhive.deploy.victorialogs;
|
||||
lc = allLocal.systemd.services.swarm-logs-auth.serviceConfig.LoadCredential;
|
||||
in
|
||||
lib.hasSuffix "/logs-auth.pem" v.authBaoClientCertFile
|
||||
&& lib.hasSuffix "/logs-auth-key.pem" v.authBaoClientKeyFile
|
||||
&& builtins.elem "bao-client.pem:${v.authBaoClientCertFile}" lc
|
||||
&& builtins.elem "bao-client-key.pem:${v.authBaoClientKeyFile}" lc;
|
||||
}
|
||||
{
|
||||
# The absence arm for the case above, and the option's own rule — a
|
||||
# service declares its entry under its own `enable` — made checkable.
|
||||
|
|
|
|||
Loading…
Reference in a new issue