bao: drop matrix-ctl's per-hive sender-token grant
swarm-controller is the only minter of swarm/hives/<hive>/matrix/sender-token since #4820, so the swarm-matrix-ctl policy's first stanza granted a write no code performs. The policy keeps its one used stanza, the swarm appservice token that `swarm-matrix-ctl appservice publish` writes. deploy.bao.matrixCtlHiveName only named the hive in the dropped stanza and goes with it. hive-matrix.nix no longer calls the per-hive appservice's as_token hive-c0re's authority: tuwunel loads the registration and creates the sender account, and no client presents that token.
This commit is contained in:
parent
b8ec0f4f85
commit
b58a0d8ba9
3 changed files with 19 additions and 64 deletions
|
|
@ -55,13 +55,13 @@ let
|
||||||
# with no host-side chown or GID pinning.
|
# with no host-side chown or GID pinning.
|
||||||
matrixSecretCredential = "/run/credentials/tuwunel.service/oidc_client_secret";
|
matrixSecretCredential = "/run/credentials/tuwunel.service/oidc_client_secret";
|
||||||
|
|
||||||
# How this hive creates matrix accounts: an appservice registration whose
|
# The hive's appservice registration, whose `url` is null. Null is a legal
|
||||||
# `url` is null. Null is a legal `url` (ruma's `Registration` types it
|
# `url` (ruma's `Registration` types it `Option<String>`), and it is the
|
||||||
# `Option<String>`), and it is the whole point — with no URL the homeserver
|
# whole point — with no URL the homeserver
|
||||||
# never calls out, so there is no HTTP service to run and no daemon to
|
# never calls out, so there is no HTTP service to run and no daemon to
|
||||||
# operate. What the registration delivers is the `as_token`: hive-c0re's
|
# operate. tuwunel loads the registration and creates its sender account
|
||||||
# standing authority to create, and log in as, the accounts named by the
|
# (below). No client presents its `as_token`: `swarm-controller` mints the
|
||||||
# namespace below, with no shared registration secret in the picture.
|
# sender's token with the swarm registration's.
|
||||||
appserviceId = "hyperhive";
|
appserviceId = "hyperhive";
|
||||||
|
|
||||||
# The appservice's own user, and the account the hive acts as. An
|
# The appservice's own user, and the account the hive acts as. An
|
||||||
|
|
|
||||||
|
|
@ -472,36 +472,18 @@ let
|
||||||
# written by the caller — the same trap as the two grants above.
|
# written by the caller — the same trap as the two grants above.
|
||||||
#
|
#
|
||||||
# Not `swarm/services/*` like the publisher's: a homeserver is not entitled
|
# Not `swarm/services/*` like the publisher's: a homeserver is not entitled
|
||||||
# to overwrite Grafana's OIDC client. Each path is spelled to the leaf for
|
# to overwrite Grafana's OIDC client. The path is spelled to the leaf for
|
||||||
# that reason, not for tidiness.
|
# that reason, not for tidiness.
|
||||||
#
|
#
|
||||||
# 🩸 And the leaf now carries a HIVE segment, which is the narrowing that
|
# It is the swarm appservice's token, which matrix-ctl mints inside the
|
||||||
# matters: the credential used to live at `swarm/services/matrix/sender-token`
|
# container and publishes here for the controller. `read` as well as write,
|
||||||
# — one value for the whole swarm, under the `services/*` tree every hive's
|
# unlike either sibling, because publish compares before it writes.
|
||||||
# own policy grants read on. Under `swarm/hives/<name>/` the only read grant
|
|
||||||
# that reaches it is that hive's own stanza, so one hive cannot fetch
|
|
||||||
# another's. `matrixCtlHive` below is the name this principal may write, and
|
|
||||||
# it is one hive rather than a `hives/*` wildcard for the same reason.
|
|
||||||
#
|
|
||||||
# ⚠️ Nothing presenting this identity writes the first stanza's path:
|
|
||||||
# `swarm-controller` is the sender token's only minter. The stanza is unused.
|
|
||||||
#
|
|
||||||
# The second stanza is the swarm appservice's token, which matrix-ctl mints
|
|
||||||
# inside the container and publishes here for the controller. `read` as well
|
|
||||||
# as write, unlike either sibling, because publish compares before it writes.
|
|
||||||
matrixCtlPolicyText = ''
|
matrixCtlPolicyText = ''
|
||||||
path "${credentialMountPath}/data/swarm/hives/${matrixCtlHive}/matrix/sender-token" {
|
|
||||||
capabilities = ["create", "update", "read"]
|
|
||||||
}
|
|
||||||
|
|
||||||
path "${credentialMountPath}/data/${swarmAppserviceTokenLeaf}" {
|
path "${credentialMountPath}/data/${swarmAppserviceTokenLeaf}" {
|
||||||
capabilities = ["create", "update", "read"]
|
capabilities = ["create", "update", "read"]
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# The hive the unused first stanza above names.
|
|
||||||
matrixCtlHive = baoDeploy.matrixCtlHiveName;
|
|
||||||
|
|
||||||
# The swarm appservice token's leaf, the nix half of
|
# The swarm appservice token's leaf, the nix half of
|
||||||
# `swarm_secret_client::matrix::swarm_appservice_token_path`. Under
|
# `swarm_secret_client::matrix::swarm_appservice_token_path`. Under
|
||||||
# `controller/`, the one kind no hive's policy reads: its sender is the
|
# `controller/`, the one kind no hive's policy reads: its sender is the
|
||||||
|
|
@ -780,9 +762,8 @@ let
|
||||||
|
|
||||||
# One reader per hive in the swarm directory. Written from the directory
|
# One reader per hive in the swarm directory. Written from the directory
|
||||||
# rather than from this host's own name because the policy is written where
|
# rather than from this host's own name because the policy is written where
|
||||||
# the STORE is and the reader runs where its hive is — the same split
|
# the STORE is and the reader runs where its hive is, so this host's name is
|
||||||
# `matrixCtlHiveName` above exists to paper over, answered here by naming
|
# not the reader's.
|
||||||
# every hive instead of asking the operator which one.
|
|
||||||
perHiveReaders =
|
perHiveReaders =
|
||||||
{ rolePrefix, cnPrefix, ... }@spec:
|
{ rolePrefix, cnPrefix, ... }@spec:
|
||||||
lib.mapAttrsToList (hiveName: _: {
|
lib.mapAttrsToList (hiveName: _: {
|
||||||
|
|
@ -1611,19 +1592,6 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
matrixCtlHiveName = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = toString hyperhiveCfg.hiveName;
|
|
||||||
defaultText = lib.literalExpression "services.hyperhive.hiveName";
|
|
||||||
example = "pr1ma";
|
|
||||||
description = ''
|
|
||||||
Hive whose matrix sender token the store's matrix-ctl role may write.
|
|
||||||
|
|
||||||
Unused: nothing presenting that identity writes the sender token;
|
|
||||||
`swarm-controller` is its only minter.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
matrixTokenCommonNamePrefix = lib.mkOption {
|
matrixTokenCommonNamePrefix = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "swarm-bao-matrix-token";
|
default = "swarm-bao-matrix-token";
|
||||||
|
|
|
||||||
|
|
@ -422,32 +422,19 @@ let
|
||||||
# the `services/` prefix the publisher holds would be a real loss even
|
# the `services/` prefix the publisher holds would be a real loss even
|
||||||
# though it would read as tidier.
|
# though it would read as tidier.
|
||||||
#
|
#
|
||||||
# ⚠️ `hives` is PLURAL, because the path segment comes from
|
# The leaf is the swarm appservice token, which matrix-ctl mints and
|
||||||
# `Kind::Hive`'s strum serialisation and not from `Kind::label`, which
|
# publishes for the controller. Counted, so a second stanza fails rather
|
||||||
# renders the singular for error text. The singular spelling evaluates,
|
# than riding along beside a correct one.
|
||||||
# deploys, and 403s every read with "permission denied" and nothing else.
|
name = "matrix-ctl's grant is the swarm appservice token, nothing else";
|
||||||
#
|
|
||||||
# 🩸 The hive NAME in the middle is the per-hive half of this credential:
|
|
||||||
# the token used to be one swarm-wide value under `services/matrix/`,
|
|
||||||
# which every hive's own policy granted read on. The negative arms below
|
|
||||||
# are what keep it from drifting back — neither the `services/*` tree nor
|
|
||||||
# a `hives/*` wildcard may appear, since either one hands matrix-ctl (or
|
|
||||||
# a hive) reach beyond the single leaf it owns.
|
|
||||||
#
|
|
||||||
# The one other leaf is the swarm appservice token, which matrix-ctl
|
|
||||||
# mints and publishes for the controller. Counted, so a third stanza
|
|
||||||
# fails rather than riding along beside two correct ones.
|
|
||||||
name = "matrix-ctl's grant is one hive's sender token and the swarm appservice token, nothing else";
|
|
||||||
ok =
|
ok =
|
||||||
let
|
let
|
||||||
s = baoGrantHere.systemd.services.swarm-bao-matrix-ctl-policy.script;
|
s = baoGrantHere.systemd.services.swarm-bao-matrix-ctl-policy.script;
|
||||||
in
|
in
|
||||||
lib.hasInfix "path \"secret/data/swarm/hives/h1/matrix/sender-token\" {" s
|
lib.hasInfix "path \"secret/data/swarm/controller/swarm-controller/matrix/appservice-token\" {\n capabilities = [\"create\", \"update\", \"read\"]\n}" s
|
||||||
&& lib.hasInfix "path \"secret/data/swarm/controller/swarm-controller/matrix/appservice-token\" {\n capabilities = [\"create\", \"update\", \"read\"]\n}" s
|
&& lib.length (lib.splitString "path \"" s) == 2
|
||||||
&& lib.length (lib.splitString "path \"" s) == 3
|
|
||||||
&& !(lib.hasInfix "secret/data/swarm/services" s)
|
&& !(lib.hasInfix "secret/data/swarm/services" s)
|
||||||
&& !(lib.hasInfix "secret/data/swarm/agents" s)
|
&& !(lib.hasInfix "secret/data/swarm/agents" s)
|
||||||
&& !(lib.hasInfix "secret/data/swarm/hives/*" s)
|
&& !(lib.hasInfix "secret/data/swarm/hives" s)
|
||||||
&& !(lib.hasInfix "sys/policies/acl" s);
|
&& !(lib.hasInfix "sys/policies/acl" s);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue