Watch
0
0
Fork
You've already forked hyperhive
0

authelia: a user outside a rule's group is denied instead of falling through to one_factor

authelia takes the first matching rule, and a rule with a `subject` does
not match a subject outside it. The swarm domain's and the bao UI's
`group:admins` rules had nothing after them, so an account in no group
skipped them and landed on `default_policy = one_factor`: any
authenticated account reached the swarm UI, its `/api/` (by cookie or by
bearer) and the bao UI.

Each of the two group rules is now an `operatorsOnly` pair: the group
allow, then a subject-less `deny` for the same domain, the shape
`collectorOnly` already gives the collector's metrics rules.
`default_policy` stays `one_factor`, which the forge and chat rely on.

module-eval-swarm-otel-identity gains two cases on `allLocal`: an
account in no group is refused the swarm domain (four paths) and the bao
UI while `group:admins` passes and the forge stays open; and a
predicate, `autheliaSubjectRulesClosed` in lib.nix, holds over the
rendered list: every subject-scoped allow is closed by a later
subject-less `deny` on its domain with no subject-less allow in between,
and nothing follows a domain-wide `deny` on its domain.

Fixes #5120
This commit is contained in:
atlas 2026-10-14 21:27:13 +02:00 • committed by mara
commit 4a9acd1d0c
4 changed files with 194 additions and 15 deletions

View file

@ -144,6 +144,50 @@ let
otelSettings =
machine: machine.containers.swarm-otel.config.services.opentelemetry-collector.settings;
autheliaRules =
machine:
machine.containers.swarm-authelia.config.services.authelia.instances.swarm.settings.access_control.rules;
# Whether authelia's `rules`, read first-match-wins, leave no subject-scoped
# allow open to other subjects, and leave no rule unreachable.
#
# A rule with a `subject` does not match a subject outside it, so that
# subject falls through to `default_policy` unless a later subject-less
# `deny` on the same domain, with no `resources` or the same ones, catches
# it first. Walking forward from the allow, the first subject-less rule on
# its domain that is either an allow or such a `deny` must be the `deny`.
# A subject-less `deny` on narrower `resources` refuses everyone it
# matches, so it is passed over. Rules naming a subject are passed over too.
#
# A subject-less `deny` with no `resources` matches every request on its
# domain, so no rule for that domain may follow it.
# ⚠️ True of a list with no subject rule at all; a case using it also
# asserts that the rules it reads contain the ones it means.
autheliaSubjectRulesClosed =
rules:
let
n = lib.length rules;
at = lib.elemAt rules;
after = i: map at (lib.range (i + 1) (n - 1));
anyone = r: !(r ? subject);
closes = r: d: d.policy == "deny" && (!(d ? resources) || d.resources == (r.resources or null));
closed =
i:
let
r = at i;
stop = lib.findFirst (
d: d.domain == r.domain && anyone d && (d.policy != "deny" || closes r d)
) null (after i);
in
stop != null && closes r stop;
endsDomain = d: anyone d && d.policy == "deny" && !(d ? resources);
nothingAfter = i: lib.all (r: r.domain != (at i).domain) (after i);
in
lib.all (
i:
(anyone (at i) || (at i).policy == "deny" || closed i) && (!(endsDomain (at i)) || nothingAfter i)
) (lib.range 0 (n - 1));
agentHarness = machine: machine.systemd.services.hive-agent;
# The PRIORITY → severity operator both journald receivers attach, read
@ -221,6 +265,8 @@ in
inherit bridgePortsOrNone;
inherit baoSettings;
inherit otelSettings;
inherit autheliaRules;
inherit autheliaSubjectRulesClosed;
inherit agentHarness;
inherit journaldSeverityParser;
inherit carriesJournaldSeverity;