fix(#3517): metrics are not optional, and the endpoint is denied by default
Two review findings, folded together. mara: a swarm integrated auto deployed forge always has metrics, so the toggle is gone. The endpoint follows behindGateway instead, which is the swarm-integrated shape and the condition the protected location lives under. Serving it without that location would put it on a listener openFirewall can expose with nothing in front. argus: /metrics matched no access_control rule, so default_policy one_factor governed it. That is any authenticated subject, which today means any operator and tomorrow any agent. My audience argument covered the Bearer path only; the same endpoint also accepts CookieSession, and a cookie carries no audience at all, so the audience was never what stood between a browser session and this data. The rule is deny rather than a client-scoped allow because the collector's client does not exist yet. Authelia refuses a subject naming an unregistered client, and does so in a preStart validator rather than at build time, so naming one early yields a green nixos-rebuild and dead swarm SSO on the next restart. Denying until the client is registered makes publishing the endpoint safe on its own; registering it is a one-line change from deny to that allow. Rule order is load-bearing: authelia takes the first match.
This commit is contained in:
parent
4e17deada5
commit
b2ee674415
2 changed files with 52 additions and 53 deletions
|
|
@ -42,6 +42,7 @@ let
|
|||
hyperhiveDomain = hyperhiveCfg.domain;
|
||||
swarmDomain = hyperhiveCfg.swarm.domain;
|
||||
uiCfg = hyperhiveCfg.swarm.ui;
|
||||
forgeCfg = hyperhiveCfg.swarm.forge;
|
||||
|
||||
# Group an account must hold to reach operator-only surfaces. Named
|
||||
# here because this module writes the rule that enforces it and
|
||||
|
|
@ -1134,11 +1135,41 @@ in
|
|||
# the account to disagree silently.
|
||||
access_control = {
|
||||
default_policy = "one_factor";
|
||||
rules = lib.optional uiCfg.enable {
|
||||
domain = uiCfg.domain;
|
||||
subject = [ "group:${operatorGroup}" ];
|
||||
policy = "one_factor";
|
||||
};
|
||||
# ⚠️ ORDER MATTERS — authelia takes the FIRST matching rule.
|
||||
# The metrics rule is listed first so it cannot be shadowed
|
||||
# by a broader domain rule added later.
|
||||
rules =
|
||||
# The forge's metrics endpoint. `deny` is deliberate and
|
||||
# is the whole protection right now: the endpoint is
|
||||
# always served (a swarm-integrated forge always has
|
||||
# metrics), and `default_policy` is `one_factor`, which
|
||||
# means *any* authenticated subject — every operator
|
||||
# today, every agent once they hold authelia accounts.
|
||||
#
|
||||
# Being reachable by a Bearer token is not sufficient on
|
||||
# its own: `authn_strategies` on this endpoint also
|
||||
# accepts `CookieSession`, and a cookie carries no
|
||||
# audience, so the audience is not what stands between a
|
||||
# browser session and this data.
|
||||
#
|
||||
# The collector gets in by REPLACING this with a
|
||||
# client-scoped allow (`subject = ["oauth2:client:<id>"]`)
|
||||
# once such a client is registered. Denying until then is
|
||||
# what makes publishing the endpoint safe on its own —
|
||||
# authelia refuses a subject naming a client that is not
|
||||
# registered, and it does so in a `preStart` validator,
|
||||
# so naming one early takes the whole SSO service down on
|
||||
# the next restart rather than failing the build.
|
||||
lib.optional forgeCfg.behindGateway {
|
||||
domain = forgeCfg.domain;
|
||||
resources = [ "^/metrics$" ];
|
||||
policy = "deny";
|
||||
}
|
||||
++ lib.optional uiCfg.enable {
|
||||
domain = uiCfg.domain;
|
||||
subject = [ "group:${operatorGroup}" ];
|
||||
policy = "one_factor";
|
||||
};
|
||||
};
|
||||
|
||||
# The cookie domain is the SWARM's domain, NOT authelia's
|
||||
|
|
|
|||
Loading…
Reference in a new issue