bao: enable cert auth and give the controller a role for its policy
The policy the granting unit already writes grants paths under `auth/cert/certs/*`, and nothing in the tree creates that mount. Every certificate login therefore fails against a path that is not there -- the controller's own, and the per-hive ones it is meant to issue against the same mount. Same unit, same bootstrap token: check whether cert auth is mounted, enable it if not, then write a role binding CN `swarm-controller` to the `swarm-controller` policy. Idempotency is a read rather than a tolerated error. `auth enable` fails on an existing mount, and recognising that would tie a rebuild to an error string no run of this store has ever produced, so the unit asks `bao auth list` and mounts only on absence. That read is why the token policy in setup.md gains `sys/auth`. Each grant came from `bao <cmd> -output-policy`, which prints what a command requires without running it -- the same way controllerPolicyText was derived. Enabling an auth method needs `sudo` on `sys/auth/cert`, which the documented token did not have. Gated on `clientCaFile`, not on the token alone: `swarm-bao-certs` installs `client-ca.pem` only under that condition, and a role's `certificate=` has to name a real CA. The policy write, which needs no CA, is unchanged in that case. Nothing can present a certificate for this role yet -- the only client leaf the tree mints carries CN = the hive's name -- and none of this has been run against a live store. Both are stated in setup.md.
This commit is contained in:
parent
df18d3d4d7
commit
8bd41fd84f
3 changed files with 107 additions and 5 deletions
|
|
@ -150,10 +150,15 @@ let
|
|||
bootstrapTokenDir =
|
||||
if haveBootstrapToken then builtins.dirOf baoDeploy.bootstrapTokenFile else null;
|
||||
|
||||
# The name both ends must agree on: whoever later gives the controller a
|
||||
# cert-auth role attaches this policy by spelling it the same way.
|
||||
# The name both ends must agree on: the cert-auth role below attaches this
|
||||
# policy by spelling it the same way, and is itself named after it.
|
||||
controllerPolicyName = "swarm-controller";
|
||||
|
||||
# The subject the controller's certificate must carry. Cert auth matches on
|
||||
# the CN, so this is an interface rather than a label: a leaf signed by the
|
||||
# right CA but minted with any other subject cannot authenticate.
|
||||
controllerCn = "swarm-controller";
|
||||
|
||||
# Derived with `bao write -output-policy`, which short-circuits the request
|
||||
# and needs no server, rather than written from memory.
|
||||
#
|
||||
|
|
@ -999,7 +1004,7 @@ in
|
|||
# where the store is reachable without a client certificate — which
|
||||
# is the point, since no role exists yet to issue one against.
|
||||
systemd.services.swarm-bao-controller-policy = lib.mkIf haveBootstrapToken {
|
||||
description = "write the swarm controller's bao policy";
|
||||
description = "write the swarm controller's bao policy and cert-auth role";
|
||||
after = [ "openbao.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = [
|
||||
|
|
@ -1034,6 +1039,37 @@ in
|
|||
# than failing on one that already exists.
|
||||
printf '%s' ${lib.escapeShellArg controllerPolicyText} |
|
||||
bao policy write ${lib.escapeShellArg controllerPolicyName} -
|
||||
''
|
||||
+ lib.optionalString (baoDeploy.clientCaFile != null) ''
|
||||
|
||||
# The policy above grants paths under `auth/cert/`, and nothing
|
||||
# in this tree creates that mount. Without this, the grant names
|
||||
# a location that does not exist and every certificate login
|
||||
# fails — the controller's own, and the per-hive ones it later
|
||||
# issues against the same mount.
|
||||
#
|
||||
# Asked rather than attempted: `auth enable` errors on a mount
|
||||
# that already exists, and recognising that would tie a rebuild
|
||||
# to an error string we have never seen this store emit.
|
||||
mounted="$(bao auth list -format=json)"
|
||||
case "$mounted" in
|
||||
*'"cert/"'*) ;;
|
||||
*) bao auth enable cert ;;
|
||||
esac
|
||||
|
||||
# `certificate=` is the CA, so this role trusts every leaf that
|
||||
# CA signed and `allowed_common_names` is the whole of what
|
||||
# narrows it to one identity. ⚠️ The same CA signs each hive's
|
||||
# reader leaf with CN = the hive's name, so a hive named
|
||||
# `${controllerCn}` would satisfy this role.
|
||||
#
|
||||
# Named outside the `hive-*` namespace the policy grants, so the
|
||||
# controller cannot rewrite the role that constrains it.
|
||||
bao write auth/cert/certs/${lib.escapeShellArg controllerPolicyName} \
|
||||
certificate=@${tlsDir}/client-ca.pem \
|
||||
allowed_common_names=${lib.escapeShellArg controllerCn} \
|
||||
token_policies=${lib.escapeShellArg controllerPolicyName} \
|
||||
display_name=${lib.escapeShellArg controllerCn}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue