Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/host-modules/swarm-bao-service.nix
atlas 627964f72d drop port-collision checks against swarm.otel.telemetryPort
nothing at runtime: eval no longer asserts against a port the swarm
collector no longer binds in the host netns.
2026-10-24 13:35:17 +02:00

151 lines
6.1 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# The swarm's secret store as every hive sees it: the names it is reached on,
# its port, its container, and the client ids it is registered under,
# identical on every host. What the host running it decides, and the
# container itself, are in ./swarm-bao.nix.
{
lib,
config,
...
}:
let
cfg = config.services.hyperhive.swarm.bao;
swarmDomain = config.services.hyperhive.swarm.domain;
# Total on a null swarm domain for the same reason every sibling module is:
# the required-domain assertion in hive-network.nix should be what an operator
# sees, not a coercion error from here.
domainBase = if swarmDomain == null then "invalid" else swarmDomain;
in
{
options.services.hyperhive.swarm.bao = {
machine = lib.mkOption {
type = lib.types.str;
readOnly = true;
default = "swarm-bao";
description = ''
Container name. Read-only: the name appears in host paths and in
`machinectl`, so it is a fact other modules may read rather than a knob.
'';
};
domain = lib.mkOption {
type = lib.types.str;
default = "bao.${domainBase}";
defaultText = lib.literalExpression ''"bao.''${services.hyperhive.swarm.domain}"'';
description = ''
Name the store is reached on. A **sibling** of the swarm's other
service names, not a child of any hive domain: an authority whose
`nameConstraints` permit one hive's domain cannot issue for a sibling
of it, so the shape of this name decides which authorities could ever
sign for the store. That is a property of the name, not a choice of
issuer — this module makes no such choice.
'';
};
ui.domain = lib.mkOption {
type = lib.types.str;
default = "bao-ui.${domainBase}";
defaultText = lib.literalExpression ''"bao-ui.''${services.hyperhive.swarm.domain}"'';
description = ''
Name the gateway serves the store's browser UI on, to members of
authelia's `admins` group only. Swarm-wide because authelia's host
writes the access rule for it and the store's host serves it.
Must differ from {option}`services.hyperhive.swarm.bao.domain`: that
name is the mutual-TLS endpoint every reader dials, and it has no
vhost.
'';
};
ui.oidc.clientId = lib.mkOption {
type = lib.types.str;
readOnly = true;
default = "swarm-bao-ui";
description = ''
OAuth2 client id the store's `oidc` auth method logs browser users
in as, at authelia.
Swarm-wide and read-only because two hosts have to agree on it:
authelia registers the client (`glue-bao-ui-oidc-client.nix`) and
mints its secret, and the store's host reads that secret back out of
the store under a path composed from this id.
'';
};
ui.oidc.redirectUri = lib.mkOption {
type = lib.types.str;
readOnly = true;
default = "https://${cfg.ui.domain}/ui/vault/auth/oidc/oidc/callback";
defaultText = lib.literalExpression ''"https://''${services.hyperhive.swarm.bao.ui.domain}/ui/vault/auth/oidc/oidc/callback"'';
description = ''
Where authelia sends the browser back to after an OIDC login, and the
URI both authelia and the store's `oidc` role match **exactly**.
The format is the OpenBao UI's own route,
`/ui/vault/auth/<mount>/oidc/callback`, with the mount `oidc`. The
UI composes it from the page's origin, so it only matches when the
gateway serves the UI on port 443.
'';
};
port = lib.mkOption {
type = lib.types.port;
default = 8200;
description = ''
TCP port the store listens on. Upstream's own default, kept so an
operator reading OpenBao documentation finds what they expect.
Swarm-wide because a client has to know it to reach the store, and
the same port on every listener: which *addresses* the store answers
on is the running host's business
({option}`services.hyperhive.deploy.bao.extraListenAddresses`), but
which port it answers on is something the whole swarm agrees.
'';
};
otel.clientId = lib.mkOption {
type = lib.types.str;
readOnly = true;
default = "swarm-bao-collector";
description = ''
OAuth2 client id the collector inside the store's container
authenticates as, and — self-referentially, the shape every hive's
client already uses — the audience it asks its token for.
**Its own, not the swarm collector's and not
`swarm-controller`'s.** One identity per principal: this forwarder
runs wherever the store runs, which is not where either of those
two runs, and the receiver it pushes to
(`swarm.otel.storeProducerName`) admits this id alone.
Swarm-wide and read-only because three hosts have to agree on it:
authelia registers the client
(`glue-swarm-bao-otel-oidc-client.nix`), the swarm collector checks
the audience (`swarm-otel.nix`), and the store's host reads the
minted secret back out of the store under a path composed from it.
Two spellings present as a healthy-looking 401.
'';
};
otel.telemetryPort = lib.mkOption {
type = lib.types.port;
default = 8890;
description = ''
Port the collector inside the store's container serves its **own**
metrics on — queue depth, refused and dropped samples, exporter
failures. How you find out that telemetry is being lost, so it is
worth keeping rather than switching off.
⚠️ **Deliberately not 8888.** 8888 is the collector
binary's built-in default, which the hive tier
({option}`services.hyperhive.otel.telemetryPort`) already binds. This
container runs with `privateNetwork = false`, so both share the
host's network namespace whenever they are co-located — and unlike
the OTLP ports this one appears nowhere in either config when it is
left undeclared, so nothing that compares configured ports can see
the clash. The second collector to start simply dies with
`bind: address already in use`.
'';
};
};
}