keep the matrix appservice registration in bao, with the host file as the last copy
This commit is contained in:
parent
fb6cd36b5f
commit
d5c5862654
2 changed files with 1014 additions and 273 deletions
|
|
@ -137,11 +137,12 @@ let
|
|||
#
|
||||
# The homeserver's one appservice registration, and the swarm's identity on
|
||||
# it: `swarm-controller` creates every agent's account and every hive's
|
||||
# `@hive-<hive>` sender with its token. Minted INSIDE this container by
|
||||
# `swarm-matrix-ctl appservice render` and published to the store by
|
||||
# `appservice publish`, which is why it is gated on matrix-ctl's store
|
||||
# identity: with nobody to publish it, a registration here would be an
|
||||
# admin credential nobody reads.
|
||||
# `@hive-<hive>` sender with its token. The store holds its tokens;
|
||||
# `swarm-matrix-ctl appservice sync` keeps this container's copy in step with
|
||||
# them, which is why it is gated on matrix-ctl's store identity: with nobody
|
||||
# to sync it, a registration here would be an admin credential nobody reads.
|
||||
# `appservice render` minted a copy here only for a host that has none, and
|
||||
# renders the one the homeserver starts from.
|
||||
#
|
||||
# Its sender is promoted to homeserver admin at boot (`admin_execute`
|
||||
# below), so its token goes only to a store path no hive's policy reaches.
|
||||
|
|
@ -1002,9 +1003,11 @@ in
|
|||
# below is `requiredBy` it and local only.
|
||||
++ lib.optional ctlActive "${swarmAppserviceCredentialId}:${swarmAppserviceDir}/swarm.yaml";
|
||||
|
||||
# The swarm registration, minted and rendered before the homeserver
|
||||
# loads it. No network and no store: this is on tuwunel's start
|
||||
# path, and a store outage must not keep the homeserver down.
|
||||
# The swarm registration, rendered before the homeserver loads it from
|
||||
# the tokens in the state dir: the last copy of the store's that
|
||||
# `swarm-matrix-appservice-sync` took, or a minted pair on a host with
|
||||
# none. No network and no store: this is on tuwunel's start path, and
|
||||
# a store outage must not keep the homeserver down.
|
||||
systemd.services.swarm-matrix-appservice-render = lib.mkIf ctlActive {
|
||||
description = "render the swarm's appservice registration";
|
||||
before = [ "tuwunel.service" ];
|
||||
|
|
@ -1027,21 +1030,31 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
# Hand the swarm registration's token to swarm-controller, through
|
||||
# the store, under matrix-ctl's identity; the write lands at a path
|
||||
# only matrix-ctl and the controller are granted (./swarm-bao.nix).
|
||||
# The store holds the swarm registration; this keeps the state dir
|
||||
# the render above reads in step with it, and publishes the
|
||||
# `as_token` to the path `swarm-controller` reads, under matrix-ctl's
|
||||
# identity (the grants are in ./swarm-bao.nix). A store without the
|
||||
# registration is seeded from the state dir, so nothing rotates.
|
||||
#
|
||||
# ⚠️ Not `before`/`requiredBy` tuwunel, and nothing orders tuwunel
|
||||
# after it: the homeserver starts from the state dir's last copy
|
||||
# whether or not the store answers. When the registration is newer than
|
||||
# tuwunel's last start, this restarts tuwunel onto it.
|
||||
#
|
||||
# `Restart=on-failure` covers a sealed store or one still starting;
|
||||
# the start timeout is bounded for the reason
|
||||
# the timer below repeats the run, which a unit without `RemainAfterExit` allows.
|
||||
# The start timeout is bounded for the reason
|
||||
# ./swarm-secret-publisher.nix states: a sealed store answers on the
|
||||
# port and never answers the read.
|
||||
systemd.services.swarm-matrix-appservice-publish = lib.mkIf ctlActive {
|
||||
description = "publish the swarm's appservice token to the swarm secret store";
|
||||
systemd.services.swarm-matrix-appservice-sync = lib.mkIf ctlActive {
|
||||
description = "keep the swarm's appservice registration in step with the swarm secret store";
|
||||
after = [ "swarm-matrix-appservice-render.service" ];
|
||||
requires = [ "swarm-matrix-appservice-render.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = [ pkgs.systemd ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${deployCfg.matrix.ctlPackage}/bin/swarm-matrix-ctl appservice publish";
|
||||
ExecStart = "${deployCfg.matrix.ctlPackage}/bin/swarm-matrix-ctl appservice sync";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 30;
|
||||
TimeoutStartSec = 60;
|
||||
|
|
@ -1053,12 +1066,19 @@ in
|
|||
BAO_CLIENT_KEY = deployCfg.matrix.ctlBaoClientKeyFile;
|
||||
MATRIX_APPSERVICE_CERT_ROLE = ctlCertRole;
|
||||
MATRIX_APPSERVICE_DIR = swarmAppserviceDir;
|
||||
MATRIX_APPSERVICE_SENDER = swarmSenderLocalpart;
|
||||
MATRIX_APPSERVICE_USER_REGEX = appserviceUserRegex;
|
||||
MATRIX_APPSERVICE_RESTART_UNIT = "tuwunel.service";
|
||||
}
|
||||
// lib.optionalAttrs (deployCfg.bao.serverCaFile != null) {
|
||||
BAO_CACERT = deployCfg.bao.serverCaFile;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.swarm-matrix-appservice-sync = lib.mkIf ctlActive (
|
||||
storeRefreshTimer "re-read the swarm's appservice registration from the swarm secret store"
|
||||
);
|
||||
|
||||
environment.systemPackages = [ deployCfg.matrix.package ];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ let
|
|||
matrixCtlPolicyName = "swarm-matrix-ctl";
|
||||
matrixCtlCn = baoDeploy.matrixCtlCommonName;
|
||||
|
||||
# Three paths, each spelled to the leaf, and every narrowing in them is
|
||||
# Four paths, each spelled to the leaf, and every narrowing in them is
|
||||
# load-bearing.
|
||||
#
|
||||
# `secret/data/` is KV v2's ACL prefix, inserted by the engine rather than
|
||||
|
|
@ -628,18 +628,27 @@ let
|
|||
# to overwrite Grafana's OIDC client. The paths are spelled to the leaf for
|
||||
# that reason, not for tidiness.
|
||||
#
|
||||
# The first 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.
|
||||
# The first is the swarm appservice's token, which matrix-ctl derives from
|
||||
# the registration and publishes here for the controller. `read` as well as
|
||||
# write, unlike either sibling, because sync compares before it writes.
|
||||
#
|
||||
# The second is the homeserver's own OIDC client secret, read-only:
|
||||
# The second is the registration's tokens: the store's copy, which matrix-ctl
|
||||
# seeds from the container's state dir and then follows. `create` and `read`
|
||||
# only, never `update`: a registration that exists is replaced by an
|
||||
# operator, not by a unit.
|
||||
#
|
||||
# The third is the homeserver's own OIDC client secret, read-only:
|
||||
# `swarm-bao-matrix-oidc` on the homeserver's host reads it under this leaf.
|
||||
# The third, also read-only, is the client secret of the journal forwarder in
|
||||
# The fourth, also read-only, is the client secret of the journal forwarder in
|
||||
# the homeserver's container.
|
||||
matrixCtlPolicyText = ''
|
||||
path "${credentialMountPath}/data/${swarmAppserviceTokenLeaf}" {
|
||||
capabilities = ["create", "update", "read"]
|
||||
}
|
||||
|
||||
path "${credentialMountPath}/data/${swarmAppserviceRegistrationLeaf}" {
|
||||
capabilities = ["create", "read"]
|
||||
}
|
||||
''
|
||||
+ readStanza "${credentialMountPath}/data/swarm/services/${hyperhiveCfg.swarm.matrix.sso.clientId}/oidc/client"
|
||||
+ readStanza "${credentialMountPath}/data/swarm/services/${hyperhiveCfg.swarm.matrix.otel.clientId}/oidc/client";
|
||||
|
|
@ -650,6 +659,11 @@ let
|
|||
# homeserver's admin.
|
||||
swarmAppserviceTokenLeaf = "swarm/controller/swarm-controller/matrix/appservice-token";
|
||||
|
||||
# The swarm appservice registration's leaf, the nix half of
|
||||
# `swarm_secret_client::matrix::swarm_appservice_registration_path`. Only
|
||||
# matrix-ctl reaches it; the controller reads the token above, not this.
|
||||
swarmAppserviceRegistrationLeaf = "swarm/controller/swarm-controller/matrix/appservice-registration";
|
||||
|
||||
# The controller's own OIDC client secret, the nix half of
|
||||
# `swarm_secret_client::queue::controller_client_path`. The publisher writes
|
||||
# it; the controller reads it and holds no other copy.
|
||||
|
|
@ -1902,12 +1916,13 @@ in
|
|||
example = "swarm-matrix-ctl.svc";
|
||||
description = ''
|
||||
Subject the store's matrix-ctl cert-auth role accepts — the
|
||||
identity the matrix container's `swarm-matrix-appservice-publish`
|
||||
unit presents when it publishes the swarm appservice's token.
|
||||
identity the matrix container's `swarm-matrix-appservice-sync`
|
||||
unit presents when it syncs the swarm appservice's registration and
|
||||
publishes its token.
|
||||
|
||||
A **third** identity rather than reuse of either sibling above. Its
|
||||
grant is that credential itself, plus read on two OIDC client
|
||||
secrets: the homeserver's, fetched by `swarm-bao-matrix-oidc`, and
|
||||
grant is that credential and the registration it derives from, plus
|
||||
read on two OIDC client secrets: the homeserver's, fetched by `swarm-bao-matrix-oidc`, and
|
||||
that of the journal forwarder in the homeserver's container. The
|
||||
point of the whole arrangement is that the
|
||||
process holding the appservice token is not the hive, so handing it
|
||||
|
|
|
|||
Loading…
Reference in a new issue