nix: follow-ups from the service/deploy split
- swarm.victoriametrics.port: the description states why the listener is loopback-only instead of pointing "below" at text in another file. - authelia: the deploy half derives its unit and instance names from swarm.authelia.unit, so the instance is spelled once, in swarm-authelia-service.nix. - authelia: the tokenEndpointAuthMethod and bearerAuthz descriptions name the obligations the authelia host asserts instead of "below"; the swarm-authelia.nix header describes that file only. - module-eval-swarm-otel-core: swarm.otel.audience must contain both store push URLs the collector's exporters use and request tokens for. Refs #3742
This commit is contained in:
parent
960cf9ce30
commit
dc5c2d8d83
4 changed files with 50 additions and 28 deletions
|
|
@ -12,8 +12,8 @@ let
|
||||||
swarmDomain = config.services.hyperhive.swarm.domain;
|
swarmDomain = config.services.hyperhive.swarm.domain;
|
||||||
|
|
||||||
# The unit name upstream's `services.authelia.instances.<name>` derives,
|
# The unit name upstream's `services.authelia.instances.<name>` derives,
|
||||||
# which `unit` publishes. ./swarm-authelia.nix binds the same two names for
|
# which `unit` publishes. ./swarm-authelia.nix reads its instance name back
|
||||||
# the instance it declares, and the two must agree.
|
# from `unit`, so this is the one place the instance is spelled.
|
||||||
instance = "swarm";
|
instance = "swarm";
|
||||||
unitName = "authelia-${instance}";
|
unitName = "authelia-${instance}";
|
||||||
in
|
in
|
||||||
|
|
@ -240,9 +240,10 @@ in
|
||||||
method must be *stated*: omitting it is refused with
|
method must be *stated*: omitting it is refused with
|
||||||
`must be configured as 'client_secret_basic', … but it's
|
`must be configured as 'client_secret_basic', … but it's
|
||||||
configured as` an empty string. The sentence above is
|
configured as` an empty string. The sentence above is
|
||||||
true of an ordinary client and false of that one, which
|
true of an ordinary client and false of that one: the
|
||||||
is exactly how a reviewer reads this option and concludes
|
host that runs authelia asserts at evaluation that a
|
||||||
the assertion below is wrong.
|
`bearerAuthz` client states `client_secret_basic`,
|
||||||
|
`client_secret_jwt` or `private_key_jwt`.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -290,12 +291,14 @@ in
|
||||||
one value that is legal here and nothing else.
|
one value that is legal here and nothing else.
|
||||||
|
|
||||||
::: {.note}
|
::: {.note}
|
||||||
Setting this obliges two other options, and the assertions
|
Setting this obliges two other options: a non-empty
|
||||||
below enforce it. Authelia checks the same thing, but only
|
`audience`, and a `tokenEndpointAuthMethod` of
|
||||||
|
`client_secret_basic`, `client_secret_jwt` or
|
||||||
|
`private_key_jwt`. Authelia checks the same thing, but only
|
||||||
in its `preStart` validator — which means a violation
|
in its `preStart` validator — which means a violation
|
||||||
builds and deploys cleanly and then fails to restart,
|
builds and deploys cleanly and then fails to restart,
|
||||||
taking swarm SSO down. The assertions move that to
|
taking swarm SSO down. The host that runs authelia asserts
|
||||||
evaluation, where a wrong value costs nothing.
|
both at evaluation, where a wrong value costs nothing.
|
||||||
:::
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
# The swarm's SSO provider: one authelia for the whole swarm, in a
|
# The swarm's SSO provider: one authelia for the whole swarm, in a
|
||||||
# `swarm-authelia` nixos-container.
|
# `swarm-authelia` nixos-container on the host with `deploy.authelia.enable`
|
||||||
#
|
# (`deploy.allSwarmServices` defaults it on, see
|
||||||
# Two halves, and only one of them is conditional:
|
# ./swarm-required-services.nix). What every hive sees of it, `url` and the
|
||||||
#
|
# OIDC client register included, is in ./swarm-authelia-service.nix.
|
||||||
# - the CLIENT pointer (`url`) exists on every hive, because a hive
|
|
||||||
# that doesn't run authelia still has to know where to send people.
|
|
||||||
# - the CONTAINER only exists where the swarm's shared services live.
|
|
||||||
# `deploy.allSwarmServices` asserts this module's `enable`
|
|
||||||
# (see ./swarm-required-services.nix); a hive is a client by default.
|
|
||||||
#
|
#
|
||||||
# The file backend is the right shape, not a placeholder for LDAP: what
|
# The file backend is the right shape, not a placeholder for LDAP: what
|
||||||
# makes a directory necessary is the size of the subject set, and this
|
# makes a directory necessary is the size of the subject set, and this
|
||||||
|
|
@ -91,12 +86,10 @@ let
|
||||||
|
|
||||||
# Upstream's `services.authelia.instances.<name>` derives the unit,
|
# Upstream's `services.authelia.instances.<name>` derives the unit,
|
||||||
# user, group and StateDirectory from the instance name
|
# user, group and StateDirectory from the instance name
|
||||||
# (`authelia` + `-<name>`). Naming them here rather than repeating the
|
# (`authelia` + `-<name>`). Both are read back from `swarm.authelia.unit`,
|
||||||
# literal keeps the generator unit below and the module in step.
|
# so the instance declared below is the one that option names.
|
||||||
# ./swarm-authelia-service.nix binds the same two names for `unit`;
|
unitName = lib.removeSuffix ".service" cfg.unit;
|
||||||
# the two must agree.
|
instance = lib.removePrefix "authelia-" unitName;
|
||||||
instance = "swarm";
|
|
||||||
unitName = "authelia-${instance}";
|
|
||||||
stateDir = "/var/lib/${unitName}";
|
stateDir = "/var/lib/${unitName}";
|
||||||
|
|
||||||
caTrust = import ./lib/hive-ca-trust.nix {
|
caTrust = import ./lib/hive-ca-trust.nix {
|
||||||
|
|
@ -790,7 +783,7 @@ in
|
||||||
../container-modules/swarm-container.nix
|
../container-modules/swarm-container.nix
|
||||||
(import ../container-modules/swarm-container-resolver.nix {
|
(import ../container-modules/swarm-container-resolver.nix {
|
||||||
inherit (networkCfg) bridgeIp;
|
inherit (networkCfg) bridgeIp;
|
||||||
dnsConsumers = [ "authelia-${instance}.service" ];
|
dnsConsumers = [ cfg.unit ];
|
||||||
})
|
})
|
||||||
caBundleModule
|
caBundleModule
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,11 @@ in
|
||||||
type = lib.types.port;
|
type = lib.types.port;
|
||||||
default = 8428;
|
default = 8428;
|
||||||
description = ''
|
description = ''
|
||||||
Port VictoriaMetrics listens on, bound to loopback only (see
|
Port VictoriaMetrics listens on, bound to loopback only on the
|
||||||
below). Upstream's own default, kept so an operator reading
|
host that runs it: its write endpoint is unauthenticated, so the
|
||||||
VictoriaMetrics documentation finds what they expect.
|
gateway's vhost there is the only route to it. Upstream's own
|
||||||
|
default, kept so an operator reading VictoriaMetrics
|
||||||
|
documentation finds what they expect.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,30 @@ let
|
||||||
&& lib.hasInfix "metrics.t.local" m
|
&& lib.hasInfix "metrics.t.local" m
|
||||||
&& lib.hasInfix "logs.t.local" l;
|
&& lib.hasInfix "logs.t.local" l;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
# `swarm.otel.audience` is built in ../host-modules/swarm-otel-service.nix,
|
||||||
|
# the push URLs the exporters use in ../host-modules/swarm-otel.nix. A URL
|
||||||
|
# the registration lacks is refused at the token endpoint with
|
||||||
|
# `invalid_target`, so every push fails while the collector reports
|
||||||
|
# healthy.
|
||||||
|
name = "the collector's registered audiences include both store push URLs it requests";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
s = otelSettings otelBaoWithAuthelia;
|
||||||
|
registered = otelBaoWithAuthelia.services.hyperhive.swarm.otel.audience;
|
||||||
|
withoutQuery = url: lib.head (lib.splitString "?" url);
|
||||||
|
pushUrls = [
|
||||||
|
s.exporters."otlphttp/victoriametrics".metrics_endpoint
|
||||||
|
(withoutQuery s.exporters."otlphttp/victorialogs".logs_endpoint)
|
||||||
|
];
|
||||||
|
requested = map (n: s.extensions."oauth2client/${n}".endpoint_params.audience) [
|
||||||
|
"victoriametrics"
|
||||||
|
"victorialogs"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
lib.all (u: builtins.elem u registered) pushUrls
|
||||||
|
&& lib.sort lib.lessThan requested == lib.sort lib.lessThan pushUrls;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
# `_HOSTNAME` cannot separate machines on its own: a hostname is a
|
# `_HOSTNAME` cannot separate machines on its own: a hostname is a
|
||||||
# config value two of them can share, and then every stream for a unit
|
# config value two of them can share, and then every stream for a unit
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue