otel: a hive always authenticates — drop the unauthenticated mode
mara, reviewing this PR: "hives always require an identity, swarm controller
and auth is not optional."
So `requireHiveIdentity` is gone rather than defaulted, and with it every
branch that had to describe an unauthenticated collector. The swarm tier now
serves per-hive receivers only, and `/` answers 404 because there is no
swarm-wide inbox to route to. A hive with no credential is a build error, not
a quieter mode.
`hivePortBase` goes too: with per-hive receivers unconditional, `port` IS the
base of the range. That keeps one documented knob instead of adding a second,
and its advice ("move it if something else claims that range") still holds.
Two assertions replace the toggle — an empty hive roster, and a null
`authelia.url`. The second matters because a guessed issuer URL evaluates
cleanly, deploys cleanly, and then refuses every hive at runtime.
⚠️ `cfg.port` is deliberately no longer compared against the derived range in
the collision assertion: it is now the range's first element, so listing it
would make that assertion fire on every config.
This also retires the asymmetry guard added earlier in review — the state it
protected against (auth off on one side, credential still set on the other)
is no longer representable.
This commit is contained in:
parent
7da7915150
commit
9bd2b9e9e6
3 changed files with 107 additions and 192 deletions
|
|
@ -203,26 +203,13 @@ somehow — copy it across and name it:
|
||||||
services.hyperhive.otel.clientSecretFile = "/run/secrets/hive-telemetry.secret";
|
services.hyperhive.otel.clientSecretFile = "/run/secrets/hive-telemetry.secret";
|
||||||
```
|
```
|
||||||
|
|
||||||
Getting that wrong shows up as the hive's collector logging 401s from the swarm
|
**There is no unauthenticated mode.** A hive always presents an identity, so a
|
||||||
tier and no metrics appearing for that hive. When a single host runs both tiers
|
missing credential is a build error rather than a quieter fallback — the
|
||||||
the build catches it instead, because it can see both sides.
|
collector has no anonymous route to accept samples on, and every path it serves
|
||||||
|
belongs to exactly one hive.
|
||||||
|
|
||||||
To accept unauthenticated ingest — every writer on the swarm network already
|
Getting the secret wrong shows up as the hive's collector logging 401s from the
|
||||||
trusted, or a swarm with no authelia:
|
swarm tier and no metrics appearing for that hive.
|
||||||
|
|
||||||
```nix
|
|
||||||
services.hyperhive.swarm.otel.requireHiveIdentity = false;
|
|
||||||
services.hyperhive.otel.clientSecretFile = null; # on each hive that had one
|
|
||||||
```
|
|
||||||
|
|
||||||
Both halves, because a collector that authenticates also addresses its hive's
|
|
||||||
own path, and an unauthenticated swarm tier serves no per-hive paths. Set only
|
|
||||||
the first and that hive's samples 404 instead of arriving. On a host running
|
|
||||||
both tiers the build says so; on a split host it is yours to keep in step.
|
|
||||||
|
|
||||||
⚠️ That reopens the original hole rather than merely skipping a check: while an
|
|
||||||
unauthenticated port is listening, anything that can reach the collector can
|
|
||||||
attribute metrics to any hive.
|
|
||||||
|
|
||||||
### `services.hyperhive.otel.collector.port` — port, default `4318`
|
### `services.hyperhive.otel.collector.port` — port, default `4318`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,11 @@ in
|
||||||
swarmName = "otlphttp/swarm";
|
swarmName = "otlphttp/swarm";
|
||||||
authName = "oauth2client/swarm";
|
authName = "oauth2client/swarm";
|
||||||
|
|
||||||
# Holding a credential IS the condition — see `clientSecretFile`.
|
# A hive always authenticates to the swarm's collector as itself, so
|
||||||
|
# this is not a mode — it is a precondition, and the assertion below
|
||||||
|
# is what enforces it. Kept as a name because several places have to
|
||||||
|
# read "do we have what it takes", and an eval error from a null path
|
||||||
|
# names this file rather than the option an operator has to set.
|
||||||
senderAuth = otel.clientSecretFile != null && hiveName != null;
|
senderAuth = otel.clientSecretFile != null && hiveName != null;
|
||||||
|
|
||||||
# This hive's client id, and also the audience it must ASK for. Both
|
# This hive's client id, and also the audience it must ASK for. Both
|
||||||
|
|
@ -382,60 +386,28 @@ in
|
||||||
|
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
# Only checkable on a host that runs BOTH tiers — which is the
|
# A hive authenticates to the swarm's collector as itself — there
|
||||||
# deployment where it can actually go wrong silently. A remote
|
# is no unauthenticated path to fall back to, so a missing
|
||||||
# hive cannot see the swarm tier's config at all, so its operator
|
# credential is a broken deployment rather than a quieter mode.
|
||||||
# sets the path explicitly and this says nothing.
|
# Caught here because the alternative is a collector that starts
|
||||||
assertion = !(swarmOtelCfg.enable && swarmOtelCfg.requireHiveIdentity) || senderAuth;
|
# cleanly, retries forever, and reports nothing to anyone.
|
||||||
|
assertion = senderAuth;
|
||||||
message = ''
|
message = ''
|
||||||
This host runs the swarm's telemetry collector with
|
services.hyperhive.otel.enable is true but this hive has no
|
||||||
services.hyperhive.swarm.otel.requireHiveIdentity = true, so
|
identity to present to the swarm's collector:
|
||||||
ingest is authenticated per hive — but this hive's own
|
|
||||||
collector has no credential to present:
|
|
||||||
|
|
||||||
services.hyperhive.otel.clientSecretFile = ${
|
services.hyperhive.otel.clientSecretFile = ${
|
||||||
if otel.clientSecretFile == null then "null" else otel.clientSecretFile
|
if otel.clientSecretFile == null then "null" else otel.clientSecretFile
|
||||||
}
|
}
|
||||||
services.hyperhive.hiveName = ${if hiveName == null then "null" else hiveName}
|
services.hyperhive.hiveName = ${if hiveName == null then "null" else hiveName}
|
||||||
|
|
||||||
Its samples would be refused with a 401 by the collector
|
Every hive authenticates as itself — that is what makes the
|
||||||
running beside it. Set both, or set requireHiveIdentity =
|
`hive` label on its metrics mean anything — so both are
|
||||||
false to accept unauthenticated ingest.
|
required.
|
||||||
'';
|
|
||||||
}
|
|
||||||
{
|
|
||||||
# The mirror of the assertion above, and the reason it exists is
|
|
||||||
# that the failure is SILENT rather than loud. With ingest
|
|
||||||
# unauthenticated the swarm tier serves one catch-all location
|
|
||||||
# and passes the URI through unchanged — but this tier still
|
|
||||||
# appends `/<hive>` whenever it holds a credential, so the
|
|
||||||
# receiver is asked for `/<hive>/v1/metrics`, a path it does not
|
|
||||||
# serve. The result is 404s and retries: no 401, no assertion, no
|
|
||||||
# log anywhere saying telemetry stopped.
|
|
||||||
#
|
|
||||||
# Only reachable by overriding one side without the other, since
|
|
||||||
# both defaults derive from `swarm.authelia.enable` and move
|
|
||||||
# together. That is exactly why it is worth a build error — an
|
|
||||||
# operator who flips the escape hatch has no reason to suspect
|
|
||||||
# the sending half.
|
|
||||||
assertion = !(swarmOtelCfg.enable && !swarmOtelCfg.requireHiveIdentity && senderAuth);
|
|
||||||
message = ''
|
|
||||||
This host accepts unauthenticated telemetry ingest
|
|
||||||
(services.hyperhive.swarm.otel.requireHiveIdentity = false),
|
|
||||||
but its own collector still holds a credential:
|
|
||||||
|
|
||||||
services.hyperhive.otel.clientSecretFile = ${
|
On a host that runs the swarm's identity provider, the default
|
||||||
if otel.clientSecretFile == null then "null" else otel.clientSecretFile
|
already points at the secret authelia minted. On a hive that
|
||||||
}
|
does not, copy that hive's secret across and name it here.
|
||||||
|
|
||||||
A collector that authenticates also addresses its hive's own
|
|
||||||
path, and an unauthenticated swarm tier serves no per-hive
|
|
||||||
paths — so this hive's samples would 404 rather than be
|
|
||||||
refused, which no log names as an auth problem.
|
|
||||||
|
|
||||||
Set services.hyperhive.otel.clientSecretFile = null to send
|
|
||||||
unauthenticated too, or drop the requireHiveIdentity
|
|
||||||
override.
|
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,6 @@ let
|
||||||
|
|
||||||
autheliaCfg = hyperhiveCfg.swarm.authelia;
|
autheliaCfg = hyperhiveCfg.swarm.authelia;
|
||||||
|
|
||||||
# Whether ingest is authenticated per hive. Derived rather than declared
|
|
||||||
# so the common case needs no attribute, but an option (below) because
|
|
||||||
# this decides whether the unauthenticated receiver exists at all.
|
|
||||||
hiveAuth = cfg.requireHiveIdentity;
|
|
||||||
|
|
||||||
# `attrNames` is sorted, so this is a function of the hive SET and not of
|
# `attrNames` is sorted, so this is a function of the hive SET and not of
|
||||||
# the order anyone wrote it in.
|
# the order anyone wrote it in.
|
||||||
#
|
#
|
||||||
|
|
@ -59,9 +54,7 @@ let
|
||||||
# renumber the ones after it and silently move a port a running hive was
|
# renumber the ones after it and silently move a port a running hive was
|
||||||
# already sending to.
|
# already sending to.
|
||||||
hivePorts = lib.listToAttrs (
|
hivePorts = lib.listToAttrs (
|
||||||
lib.imap0 (i: h: lib.nameValuePair h (cfg.hivePortBase + i)) (
|
lib.imap0 (i: h: lib.nameValuePair h (cfg.port + i)) (lib.attrNames hyperhiveCfg.swarm.hives)
|
||||||
lib.attrNames hyperhiveCfg.swarm.hives
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
# The swarm's authelia is reached by its gateway name, whose leaf is
|
# The swarm's authelia is reached by its gateway name, whose leaf is
|
||||||
|
|
@ -119,7 +112,17 @@ in
|
||||||
type = lib.types.port;
|
type = lib.types.port;
|
||||||
default = 4319;
|
default = 4319;
|
||||||
description = ''
|
description = ''
|
||||||
Port this collector's OTLP/HTTP receiver listens on.
|
First port of this collector's receiver range. Every hive in
|
||||||
|
{option}`services.hyperhive.swarm.hives` gets its **own**
|
||||||
|
authenticated receiver — that is what makes the `hive` label
|
||||||
|
unforgeable — so the range is one port per hive, starting here, in
|
||||||
|
sorted-name order.
|
||||||
|
|
||||||
|
⚠️ Internal. No client is ever told a port: a hive reaches its own
|
||||||
|
receiver as `https://''${domain}/<hive>`, and the gateway routes on
|
||||||
|
that path. So adding a hive, which renumbers the ones after it, is
|
||||||
|
harmless — nginx is rendered from this same evaluation and moves
|
||||||
|
with it.
|
||||||
|
|
||||||
⚠️ **Deliberately not 4318**, the OTLP/HTTP default, because the
|
⚠️ **Deliberately not 4318**, the OTLP/HTTP default, because the
|
||||||
hive tier already uses it (`services.hyperhive.otel.collector.port`)
|
hive tier already uses it (`services.hyperhive.otel.collector.port`)
|
||||||
|
|
@ -127,7 +130,9 @@ in
|
||||||
listeners claiming one port on one host is not a build failure —
|
listeners claiming one port on one host is not a build failure —
|
||||||
it is a runtime coin toss over which one gets it, with nothing in
|
it is a runtime coin toss over which one gets it, with nothing in
|
||||||
any log saying so. The same collision cost a release when grafana
|
any log saying so. The same collision cost a release when grafana
|
||||||
and the forge both defaulted to 3000.
|
and the forge both defaulted to 3000. The assertions below check
|
||||||
|
the whole derived range against every port this module and the hive
|
||||||
|
tier declare, which is as far as a module can see.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -151,53 +156,6 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
requireHiveIdentity = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = autheliaCfg.enable && autheliaCfg.url != null;
|
|
||||||
defaultText = lib.literalExpression "swarm.authelia.enable && swarm.authelia.url != null";
|
|
||||||
description = ''
|
|
||||||
Authenticate ingest per hive: each hive gets its own receiver,
|
|
||||||
verifying an OIDC token minted for that hive's audience, and the
|
|
||||||
`hive` label is stamped from **which receiver accepted the
|
|
||||||
sample** rather than from anything the sender wrote.
|
|
||||||
|
|
||||||
⚠️ Turning this on **removes the unauthenticated receiver**. That
|
|
||||||
is the point rather than a side effect: while an unauthenticated
|
|
||||||
port still accepts samples, any writer that can reach this
|
|
||||||
collector can still attribute metrics to any hive, and the
|
|
||||||
per-hive receivers are decoration.
|
|
||||||
|
|
||||||
Defaults to whether this swarm has an authelia to mint against.
|
|
||||||
Set it false to keep the open receiver on a swarm where every
|
|
||||||
writer is already trusted — an explicit choice, which is what it
|
|
||||||
should be.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
hivePortBase = lib.mkOption {
|
|
||||||
type = lib.types.port;
|
|
||||||
default = 4330;
|
|
||||||
description = ''
|
|
||||||
First port of the per-hive receiver range; each hive in
|
|
||||||
{option}`services.hyperhive.swarm.hives` takes the next one, in
|
|
||||||
sorted-name order.
|
|
||||||
|
|
||||||
⚠️ Internal. No client is told a port — a hive reaches its own
|
|
||||||
receiver as `https://''${domain}/<hive>`, and the gateway routes
|
|
||||||
the path. So inserting a hive renumbering the ones after it is
|
|
||||||
harmless here: nginx is rendered from this same evaluation and
|
|
||||||
moves with it.
|
|
||||||
|
|
||||||
The range still matters because every swarm container shares the
|
|
||||||
host's network namespace, so a derived port can land on one
|
|
||||||
another service already holds — with no bind error and nothing in
|
|
||||||
any log. The assertions below check this range against every port
|
|
||||||
this module and the hive tier declare — which is as far as a
|
|
||||||
module can see, since a port another module picks is not
|
|
||||||
knowable from here without reading its config.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
domain = lib.mkOption {
|
domain = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "otel.${domainBase}";
|
default = "otel.${domainBase}";
|
||||||
|
|
@ -245,21 +203,14 @@ in
|
||||||
# serves `/v1/metrics` and knows nothing about hives. Without it
|
# serves `/v1/metrics` and knows nothing about hives. Without it
|
||||||
# the receiver sees `/<hive>/v1/metrics` and answers 404 to a
|
# the receiver sees `/<hive>/v1/metrics` and answers 404 to a
|
||||||
# request that authenticated perfectly.
|
# request that authenticated perfectly.
|
||||||
lib.optionalAttrs hiveAuth (
|
lib.mapAttrs' (
|
||||||
lib.mapAttrs' (
|
h: p: lib.nameValuePair "/${h}/" { proxyPass = "http://127.0.0.1:${toString p}/"; }
|
||||||
h: p: lib.nameValuePair "/${h}/" { proxyPass = "http://127.0.0.1:${toString p}/"; }
|
) hivePorts
|
||||||
) hivePorts
|
|
||||||
)
|
|
||||||
// {
|
// {
|
||||||
"/" =
|
# There is no swarm-wide inbox, and a closed door is the honest
|
||||||
if hiveAuth then
|
# description of that. Every route into this collector belongs to
|
||||||
# Not a proxy to a receiver that no longer exists. A closed
|
# exactly one hive.
|
||||||
# door answering 404 is the honest description of this
|
"/".return = "404";
|
||||||
# collector once ingest is per-hive: there is no
|
|
||||||
# swarm-wide inbox any more.
|
|
||||||
{ return = "404"; }
|
|
||||||
else
|
|
||||||
{ proxyPass = "http://127.0.0.1:${toString cfg.port}"; };
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -267,12 +218,12 @@ in
|
||||||
# option exists to allow: its own description names this module as the
|
# option exists to allow: its own description names this module as the
|
||||||
# second consumer, so the queue is not a prerequisite for authenticated
|
# second consumer, so the queue is not a prerequisite for authenticated
|
||||||
# telemetry.
|
# telemetry.
|
||||||
services.hyperhive.swarm.authelia.oidc.hiveIdentities = lib.mkIf hiveAuth true;
|
services.hyperhive.swarm.authelia.oidc.hiveIdentities = true;
|
||||||
|
|
||||||
# The CA bind source is written at runtime by a host unit, so the
|
# The CA bind source is written at runtime by a host unit, so the
|
||||||
# container has to start after it — otherwise nspawn sets up a mount
|
# container has to start after it — otherwise nspawn sets up a mount
|
||||||
# over a file that does not exist yet.
|
# over a file that does not exist yet.
|
||||||
systemd.services."container@${cfg.machine}" = lib.mkIf hiveAuth caTrust.containerOrdering;
|
systemd.services."container@${cfg.machine}" = caTrust.containerOrdering;
|
||||||
|
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
|
|
@ -293,15 +244,30 @@ in
|
||||||
{
|
{
|
||||||
# Without a roster there are no receivers at all, so this
|
# Without a roster there are no receivers at all, so this
|
||||||
# collector would listen on nothing while looking configured.
|
# collector would listen on nothing while looking configured.
|
||||||
assertion = !hiveAuth || hyperhiveCfg.swarm.hives != { };
|
assertion = hyperhiveCfg.swarm.hives != { };
|
||||||
message = ''
|
message = ''
|
||||||
services.hyperhive.swarm.otel.requireHiveIdentity is true but
|
services.hyperhive.swarm.otel.enable is true but
|
||||||
services.hyperhive.swarm.hives is empty: ingest is authenticated
|
services.hyperhive.swarm.hives is empty: ingest is authenticated
|
||||||
per hive, so an empty roster means this collector accepts
|
per hive, so an empty roster means this collector accepts nothing
|
||||||
nothing from anyone.
|
from anyone.
|
||||||
|
|
||||||
List the swarm's hives, or set requireHiveIdentity = false to
|
List the swarm's hives.
|
||||||
keep an unauthenticated receiver.
|
'';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# A hive proves who it is with a token this provider mints, so
|
||||||
|
# there is no version of this collector that runs without one.
|
||||||
|
# Stated as an assertion rather than a fallback because a guessed
|
||||||
|
# issuer URL evaluates cleanly and refuses every hive at runtime.
|
||||||
|
assertion = autheliaCfg.url != null;
|
||||||
|
message = ''
|
||||||
|
services.hyperhive.swarm.otel.enable is true but
|
||||||
|
services.hyperhive.swarm.authelia.url is null: every hive
|
||||||
|
authenticates to this collector as itself, and the token comes
|
||||||
|
from the swarm's identity provider.
|
||||||
|
|
||||||
|
Point authelia.url at the swarm's provider, or enable
|
||||||
|
services.hyperhive.swarm.authelia on the host that runs it.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
@ -309,28 +275,31 @@ in
|
||||||
# coin toss with nothing in any log — the failure this whole
|
# coin toss with nothing in any log — the failure this whole
|
||||||
# comment budget exists to prevent. Checked against every port
|
# comment budget exists to prevent. Checked against every port
|
||||||
# reachable from here; a port some other module picks is not.
|
# reachable from here; a port some other module picks is not.
|
||||||
|
#
|
||||||
|
# ⚠️ `cfg.port` is deliberately absent from `others`: it is the
|
||||||
|
# FIRST element of the derived range, so listing it would make this
|
||||||
|
# assertion fire on every config.
|
||||||
assertion =
|
assertion =
|
||||||
let
|
let
|
||||||
derived = lib.attrValues hivePorts;
|
derived = lib.attrValues hivePorts;
|
||||||
others = [
|
others = [
|
||||||
cfg.port
|
|
||||||
cfg.telemetryPort
|
cfg.telemetryPort
|
||||||
otelCfg.collector.port
|
otelCfg.collector.port
|
||||||
]
|
]
|
||||||
++ lib.optional vmCfg.enable vmCfg.port;
|
++ lib.optional vmCfg.enable vmCfg.port;
|
||||||
all = derived ++ others;
|
all = derived ++ others;
|
||||||
in
|
in
|
||||||
!hiveAuth || lib.length (lib.unique all) == lib.length all;
|
lib.length (lib.unique all) == lib.length all;
|
||||||
message = ''
|
message = ''
|
||||||
services.hyperhive.swarm.otel: the per-hive receiver range
|
services.hyperhive.swarm.otel: the receiver range starting at
|
||||||
starting at hivePortBase (${toString cfg.hivePortBase}, one port
|
port (${toString cfg.port}, one port per hive in
|
||||||
per hive in services.hyperhive.swarm.hives) overlaps another
|
services.hyperhive.swarm.hives) overlaps another port on this
|
||||||
port on this host.
|
host.
|
||||||
|
|
||||||
Every swarm container shares the host's network namespace, so
|
Every swarm container shares the host's network namespace, so
|
||||||
two listeners claiming one port is not a build failure — it is
|
two listeners claiming one port is not a build failure — it is
|
||||||
whichever process started first, silently. Move hivePortBase to
|
whichever process started first, silently. Move
|
||||||
a free range.
|
services.hyperhive.swarm.otel.port to a free range.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -357,7 +326,7 @@ in
|
||||||
}
|
}
|
||||||
# The public hive CA, read-only — only when something in here
|
# The public hive CA, read-only — only when something in here
|
||||||
# actually verifies a swarm-service name.
|
# actually verifies a swarm-service name.
|
||||||
// lib.optionalAttrs hiveAuth caTrust.bindMount;
|
// caTrust.bindMount;
|
||||||
|
|
||||||
config =
|
config =
|
||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
@ -379,13 +348,13 @@ in
|
||||||
# call fails while the unit looks healthy. That is why this is
|
# call fails while the unit looks healthy. That is why this is
|
||||||
# the shared helper — it carries the `Requires` and the
|
# the shared helper — it carries the `Requires` and the
|
||||||
# non-empty check — and not a local `cat`.
|
# non-empty check — and not a local `cat`.
|
||||||
++ lib.optional hiveAuth (
|
++ [
|
||||||
caTrust.trustBundle {
|
(caTrust.trustBundle {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
name = cfg.machine;
|
name = cfg.machine;
|
||||||
consumers = [ "opentelemetry-collector" ];
|
consumers = [ "opentelemetry-collector" ];
|
||||||
}
|
})
|
||||||
);
|
];
|
||||||
|
|
||||||
system.stateVersion = config.system.stateVersion;
|
system.stateVersion = config.system.stateVersion;
|
||||||
networking.firewall.enable = false;
|
networking.firewall.enable = false;
|
||||||
|
|
@ -404,8 +373,8 @@ in
|
||||||
# real sample through both tiers into the store.
|
# real sample through both tiers into the store.
|
||||||
validateConfigFile = true;
|
validateConfigFile = true;
|
||||||
settings = {
|
settings = {
|
||||||
# One receiver per hive when ingest is authenticated, and
|
# One receiver per hive, and that multiplicity is forced
|
||||||
# that multiplicity is forced rather than chosen. The `hive`
|
# rather than chosen. The `hive`
|
||||||
# label has to come from something the sender cannot write,
|
# label has to come from something the sender cannot write,
|
||||||
# and the only such thing here is WHICH RECEIVER accepted
|
# and the only such thing here is WHICH RECEIVER accepted
|
||||||
# the sample: a processor cannot read the token's claims
|
# the sample: a processor cannot read the token's claims
|
||||||
|
|
@ -413,19 +382,15 @@ in
|
||||||
# an auth claim yields nothing — silently, with a healthy
|
# an auth claim yields nothing — silently, with a healthy
|
||||||
# startup), and one receiver holding many credentials never
|
# startup), and one receiver holding many credentials never
|
||||||
# reveals which one matched.
|
# reveals which one matched.
|
||||||
receivers =
|
receivers = lib.mapAttrs' (
|
||||||
if hiveAuth then
|
h: p:
|
||||||
lib.mapAttrs' (
|
lib.nameValuePair "otlp/${h}" {
|
||||||
h: p:
|
protocols.http = {
|
||||||
lib.nameValuePair "otlp/${h}" {
|
endpoint = "127.0.0.1:${toString p}";
|
||||||
protocols.http = {
|
auth.authenticator = "oidc/${h}";
|
||||||
endpoint = "127.0.0.1:${toString p}";
|
};
|
||||||
auth.authenticator = "oidc/${h}";
|
}
|
||||||
};
|
) hivePorts;
|
||||||
}
|
|
||||||
) hivePorts
|
|
||||||
else
|
|
||||||
{ otlp.protocols.http.endpoint = "127.0.0.1:${toString cfg.port}"; };
|
|
||||||
|
|
||||||
exporters =
|
exporters =
|
||||||
lib.optionalAttrs vmCfg.enable {
|
lib.optionalAttrs vmCfg.enable {
|
||||||
|
|
@ -473,7 +438,7 @@ in
|
||||||
# INERT — the collector starts clean and the receiver
|
# INERT — the collector starts clean and the receiver
|
||||||
# naming it authenticates nothing. Derived from the same
|
# naming it authenticates nothing. Derived from the same
|
||||||
# attrset as the receivers so the two cannot disagree.
|
# attrset as the receivers so the two cannot disagree.
|
||||||
service.extensions = lib.optionals hiveAuth (map (h: "oidc/${h}") (lib.attrNames hivePorts));
|
service.extensions = map (h: "oidc/${h}") (lib.attrNames hivePorts);
|
||||||
|
|
||||||
# Fan-out, not a choice: with both configured the same
|
# Fan-out, not a choice: with both configured the same
|
||||||
# samples go upstream AND into the swarm's store. The store
|
# samples go upstream AND into the swarm's store. The store
|
||||||
|
|
@ -482,25 +447,16 @@ in
|
||||||
# `exporterNames` is shared by every pipeline — where a
|
# `exporterNames` is shared by every pipeline — where a
|
||||||
# sample goes is a property of this tier, not of the hive
|
# sample goes is a property of this tier, not of the hive
|
||||||
# that sent it.
|
# that sent it.
|
||||||
service.pipelines =
|
service.pipelines = lib.mapAttrs' (
|
||||||
if hiveAuth then
|
h: _:
|
||||||
lib.mapAttrs' (
|
lib.nameValuePair "metrics/${h}" {
|
||||||
h: _:
|
receivers = [ "otlp/${h}" ];
|
||||||
lib.nameValuePair "metrics/${h}" {
|
processors = [ "resource/${h}" ];
|
||||||
receivers = [ "otlp/${h}" ];
|
exporters = exporterNames;
|
||||||
processors = [ "resource/${h}" ];
|
}
|
||||||
exporters = exporterNames;
|
) hivePorts;
|
||||||
}
|
|
||||||
) hivePorts
|
|
||||||
else
|
|
||||||
{
|
|
||||||
metrics = {
|
|
||||||
receivers = [ "otlp" ];
|
|
||||||
exporters = exporterNames;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs hiveAuth {
|
// {
|
||||||
extensions = lib.mapAttrs' (
|
extensions = lib.mapAttrs' (
|
||||||
h: _:
|
h: _:
|
||||||
lib.nameValuePair "oidc/${h}" {
|
lib.nameValuePair "oidc/${h}" {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue