Watch
0
0
Fork
You've already forked hyperhive
0

authelia: allow only the collector to write to the log store's ingest path

This commit is contained in:
atlas 2026-10-17 13:31:11 +02:00
commit 2489830772
3 changed files with 48 additions and 12 deletions

View file

@ -290,8 +290,10 @@ accepts the new value within a minute.
The collector also scrapes this store's own metrics, at
`https://<swarm.victorialogs.domain>/metrics`, so a collector on a host
without the store still gets them. The gateway admits only the collector's
client to that path; any other session gets `deny`.
without the store still gets them. On this name the gateway admits only the
collector's client to `/metrics` and the ingest route
`/insert/opentelemetry/v1/logs`; any other session gets `deny` on both. The
read side stays open to any authenticated caller.
### Telemetry collector (OTEL)

View file

@ -134,6 +134,16 @@ let
}
];
# The log store's ingest routes: the collector's client, nobody else. A
# prefix rather than the one route the vhost serves, so no spelling of it,
# and no route added under `/insert/`, reaches `default_policy`. The rest of
# the name (read API, vmui) stays open to any authenticated caller; its
# `/metrics` is covered by `metricsRules`.
logsStoreRules = collectorOnly {
domain = config.services.hyperhive.swarm.victorialogs.domain;
resources = [ "^/insert/.*" ];
};
# One pair per `swarm.otel.publishedScrapeTargets` entry, read off the same
# URL the collector requests and its token is minted for, so a target
# cannot be published without the rules that confine it to the collector.
@ -1342,6 +1352,7 @@ in
# reasoning for both halves lives.
metricsRules
++ metricsStoreRules
++ logsStoreRules
# The swarm UI on the apex. Not keyed to a deploy flag: its
# vhost is on the swarm controller's host, which need not
# be this one. Guarded on the domain being set: without it

View file

@ -387,8 +387,11 @@ let
rules =
autheliaOnly.containers.swarm-authelia.config.services.authelia.instances.swarm.settings.access_control.rules;
# The metrics store's own name is the case above's; its rules carry
# `resources` for its push route and `/metrics` together.
metricsRules = lib.filter (r: r ? resources && r.domain != "metrics.t.local") rules;
# `resources` for its push route and `/metrics` together. The log
# store's `/insert/` ingest rules are the case below's.
metricsRules = lib.filter (
r: r ? resources && r.domain != "metrics.t.local" && r.resources != [ "^/insert/.*" ]
) rules;
re = lib.head (lib.head metricsRules).resources;
table = [
{
@ -514,10 +517,11 @@ let
&& checksOf locs."^~ /select/logsql/" == [ "auth_request /__swarm_logs_auth;" ];
}
{
# The `deny` that confines the log store's `/metrics` is scoped to that
# path, and comes after the collector's rule: every other path on the
# name still reaches `default_policy`, as it did with no rule at all.
name = "authelia confines the log store's /metrics to the collector and leaves its other paths alone";
# The `deny`s that confine the log store's `/metrics` and its `/insert/`
# ingest prefix are each scoped to their path and come after the
# collector's rule: every other path on the name still reaches
# `default_policy`.
name = "authelia confines the log store's /metrics and ingest to the collector and leaves its other paths alone";
ok =
let
h = autheliaOnly.services.hyperhive;
@ -526,19 +530,38 @@ let
autheliaOnly.containers.swarm-authelia.config.services.authelia.instances.swarm.settings.access_control.rules;
logsRules = lib.filter (r: r.domain == "logs.t.local") rules;
policy = subject: autheliaPolicy autheliaOnly subject "logs.t.local";
ingest = "/insert/opentelemetry/v1/logs";
in
map (r: r.policy) logsRules == [
"one_factor"
"deny"
"one_factor"
"deny"
]
&&
map (r: r.resources) logsRules == [
[ "^/metrics([?].*)?$" ]
[ "^/metrics([?].*)?$" ]
[ "^/insert/.*" ]
[ "^/insert/.*" ]
]
&&
map (r: r.subject or null) logsRules == [
[ collector ]
null
[ collector ]
null
]
&& lib.all (r: r.resources == [ "^/metrics([?].*)?$" ]) logsRules
&& (lib.head logsRules).subject == [ collector ]
&& policy collector "/metrics" == "one_factor"
&& policy "user:someone" "/metrics" == "deny"
&& policy "user:someone" "/metrics?x=1" == "deny"
&& policy collector ingest == "one_factor"
&& policy collector "${ingest}?_msg_field=MESSAGE" == "one_factor"
&& policy "user:someone" ingest == "deny"
&& policy "user:someone" "${ingest}?_msg_field=MESSAGE" == "deny"
&& policy "user:someone" "/insert/jsonline" == "deny"
&& policy "user:someone" "/" == "one_factor"
&& policy "user:someone" "/select/logsql/query" == "one_factor"
&& policy "user:someone" "/insert/opentelemetry/v1/logs" == "one_factor";
&& policy "user:someone" "/select/logsql/query" == "one_factor";
}
{
# Single host: the store is scraped once, by its published name.