feat(#3517): a named capability for authelia's bearer-authz scope
A scraper reaches a service published behind the gateway by presenting an access token to authelia's authz endpoint, which requires the client to carry authelia.bearer.authz. Nothing could express that: scopes are derived from kind, and a machine client rendered an empty list. A named capability rather than a free-form scopes list, for the reason the derivation exists — authelia refuses some scope/grant combinations outright, openid with client_credentials among them, and a list makes those expressible again. The two assertions carry their weight: authelia checks the same obligations, but in its preStart validator, so a violation builds and deploys cleanly and then fails to restart with swarm SSO attached to it.
This commit is contained in:
parent
32c5973956
commit
da88d450dd
1 changed files with 77 additions and 1 deletions
|
|
@ -217,7 +217,7 @@ let
|
|||
if c.kind == "machine" then
|
||||
''
|
||||
printf -- ' grant_types: ["client_credentials"]\n'
|
||||
printf -- ' scopes: []\n'
|
||||
printf -- ' scopes: [${lib.optionalString c.bearerAuthz "authelia.bearer.authz"}]\n'
|
||||
''
|
||||
else
|
||||
''
|
||||
|
|
@ -575,6 +575,34 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
bearerAuthz = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
example = true;
|
||||
description = ''
|
||||
Grant this client the `authelia.bearer.authz` scope, so it
|
||||
may present its access token to authelia's authz endpoint
|
||||
and be authorised by an `access_control` rule — how a
|
||||
scraper reaches a service published behind the gateway.
|
||||
|
||||
A named capability rather than a free-form `scopes` list,
|
||||
for the same reason `kind` derives the rest: authelia
|
||||
refuses some scope/grant combinations outright (`openid`
|
||||
with `client_credentials` among them), and a list would
|
||||
make those combinations expressible again. This admits the
|
||||
one value that is legal here and nothing else.
|
||||
|
||||
::: {.note}
|
||||
Setting this obliges two other options, and the assertions
|
||||
below enforce it. Authelia checks the same thing, but only
|
||||
in its `preStart` validator — which means a violation
|
||||
builds and deploys cleanly and then fails to restart,
|
||||
taking swarm SSO down. The assertions move that to
|
||||
evaluation, where a wrong value costs nothing.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
accessTokenSignedResponseAlg = lib.mkOption {
|
||||
type = lib.types.nullOr (
|
||||
lib.types.enum [
|
||||
|
|
@ -820,6 +848,54 @@ in
|
|||
+ "services.hyperhive.swarm.hives; rename the hive or the "
|
||||
+ "colliding client.";
|
||||
}
|
||||
# The two obligations `authelia.bearer.authz` carries. Authelia
|
||||
# enforces both itself — but in its `preStart` validator, so a
|
||||
# violation produces a green `nixos-rebuild switch` and an authelia
|
||||
# that then refuses to come back up, taking swarm SSO with it.
|
||||
# Asserting here moves the same failure to evaluation, where a
|
||||
# wrong value costs a build and nothing else.
|
||||
{
|
||||
assertion = lib.all (c: !c.bearerAuthz || c.audience != [ ]) cfg.oidc.clients;
|
||||
message =
|
||||
"services.hyperhive.swarm.authelia.oidc.clients: "
|
||||
+ lib.concatStringsSep ", " (
|
||||
map (c: "client '${c.id}'") (lib.filter (c: c.bearerAuthz && c.audience == [ ]) cfg.oidc.clients)
|
||||
)
|
||||
+ " sets bearerAuthz but declares no audience. The audience is "
|
||||
+ "what authorises a bearer token at a given URL, so without "
|
||||
+ "one the scope grants access to nothing and authelia refuses "
|
||||
+ "the configuration outright.";
|
||||
}
|
||||
{
|
||||
assertion = lib.all (
|
||||
c:
|
||||
!c.bearerAuthz
|
||||
|| lib.elem c.tokenEndpointAuthMethod [
|
||||
"client_secret_basic"
|
||||
"client_secret_jwt"
|
||||
"private_key_jwt"
|
||||
]
|
||||
) cfg.oidc.clients;
|
||||
message =
|
||||
"services.hyperhive.swarm.authelia.oidc.clients: "
|
||||
+ lib.concatStringsSep ", " (
|
||||
map (c: "client '${c.id}' (tokenEndpointAuthMethod = ${toString c.tokenEndpointAuthMethod})") (
|
||||
lib.filter (
|
||||
c:
|
||||
c.bearerAuthz
|
||||
&& !lib.elem c.tokenEndpointAuthMethod [
|
||||
"client_secret_basic"
|
||||
"client_secret_jwt"
|
||||
"private_key_jwt"
|
||||
]
|
||||
) cfg.oidc.clients
|
||||
)
|
||||
)
|
||||
+ ". A confidential client carrying authelia.bearer.authz must "
|
||||
+ "authenticate with client_secret_basic, client_secret_jwt or "
|
||||
+ "private_key_jwt. Notably client_secret_post is refused, and "
|
||||
+ "it is what an OAuth2 client library may reach for first.";
|
||||
}
|
||||
];
|
||||
|
||||
# Authelia's own gateway surface: the vhost that fronts it and the
|
||||
|
|
|
|||
Loading…
Reference in a new issue