diff --git a/nix/host-modules/glue-matrix-bao-token.nix b/nix/host-modules/glue-matrix-bao-token.nix index 34a62a80..784d087c 100644 --- a/nix/host-modules/glue-matrix-bao-token.nix +++ b/nix/host-modules/glue-matrix-bao-token.nix @@ -112,6 +112,22 @@ in err="$(mktemp)" trap 'rm -f "$err"' EXIT + # Cert auth is a login, not a transport setting. The `BAO_CLIENT_*` + # variables above only decide which certificate the TLS handshake + # presents; without a token `bao` asks its token helper instead, and + # that is a `sh` this unit's `path` does not carry. `-token-only` + # answers on stdout and skips the helper on both sides. + if ! BAO_TOKEN="$(bao login -method=cert -token-only 2>"$err")"; then + echo "could not log in to swarm-bao with this host's certificate; keeping the token hive-matrix already has." >&2 + if [ -s "$err" ]; then + cat "$err" >&2 + else + echo "bao failed without writing a diagnostic." >&2 + fi + exit 0 + fi + export BAO_TOKEN + if ! token="$(bao kv get -field=value ${lib.escapeShellArg tokenPath} 2>"$err")"; then echo "swarm-bao did not return ${tokenPath}; keeping the token hive-matrix already has." >&2 if [ -s "$err" ]; then diff --git a/nix/host-modules/swarm-secret-publisher.nix b/nix/host-modules/swarm-secret-publisher.nix index 27b68adb..85cf126d 100644 --- a/nix/host-modules/swarm-secret-publisher.nix +++ b/nix/host-modules/swarm-secret-publisher.nix @@ -143,6 +143,18 @@ in script = '' set -euo pipefail + # Cert auth is a login, not a transport setting. The `BAO_CLIENT_*` + # variables above only decide which certificate the TLS handshake + # presents; without a token `bao` asks its token helper instead, and + # that is a `sh` this unit's `path` does not carry. `-token-only` + # answers on stdout and skips the helper on both sides. + # + # Unhandled on purpose: `Restart=on-failure` above is what a store that + # cannot authenticate this host should get. Degrading here would report + # "published 0" as an ordinary quiet day. + BAO_TOKEN="$(bao login -method=cert -token-only)" + export BAO_TOKEN + published=0 skipped=0 diff --git a/nix/module-eval.nix b/nix/module-eval.nix index 50313344..aab24f7e 100644 --- a/nix/module-eval.nix +++ b/nix/module-eval.nix @@ -1089,6 +1089,44 @@ let lib.hasInfix "secret/swarm/hives/h1/queue/agent" s && lib.hasInfix "secret/swarm/hives/h2/queue/agent" s; } + { + # A property of the SET, not of one unit: both of these authenticate by + # certificate, and `BAO_CLIENT_CERT` is transport rather than identity, so + # a script that reaches `bao kv` without a token asks a token helper this + # host does not carry and fails before the store ever answers. `-token-only` + # is what keeps the token off the helper on the way back out. + # + # Ordering, not presence: the login has to come first, so the check is + # that nothing before it is a data command. Comments are stripped because + # both units explain this in prose directly above the code. + name = "the cert-identity bao units log in before their first read or write, and keep the token out of the helper"; + ok = + let + code = + s: + lib.concatStringsSep "\n" ( + lib.filter (l: builtins.match "[[:space:]]*#.*" l == null) (lib.splitString "\n" s) + ); + holdsTokenFirst = + s: + let + c = code s; + in + lib.hasInfix "bao login" c + && lib.hasInfix "-token-only" c + && !(lib.hasInfix "bao kv" (lib.head (lib.splitString "bao login" c))); + in + holdsTokenFirst secretPublisherHere.systemd.services.swarm-secret-publish.script + && holdsTokenFirst baoWithMatrix.systemd.services.swarm-bao-matrix-token.script + # Controls, so a clean verdict above means something. In order: a bare + # read is refused, a read placed before the login is refused, and a + # login that exists only in a comment is refused — that last one is the + # arm the comment-stripping exists for. + && !(holdsTokenFirst "bao kv get -field=value secret/x") + && !(holdsTokenFirst "bao kv get secret/x\nBAO_TOKEN=\"$(bao login -method=cert -token-only)\"") + && !(holdsTokenFirst "# bao login -method=cert -token-only goes here\nbao kv get secret/x") + && holdsTokenFirst "BAO_TOKEN=\"$(bao login -method=cert -token-only)\"\nbao kv get secret/x"; + } { # The doctrine three glue files state, as a property a rewrite has to # keep: a client is defined by holding a certificate the store accepts,