Watch
0
0
Fork
You've already forked hyperhive
0

bao: disable the unused approle auth method, declaratively

No Rust ever minted a secret_id; approle was dead attack surface. The
bootstrap step's check-then-enable case becomes check-then-disable: if
approle is mounted, `bao auth disable approle`; otherwise a no-op.

Disabling costs `delete`+`sudo` on `sys/auth/approle`, not
`create`/`update` — verified against `bao auth disable -output-policy`
on a live dev store. The bootstrap policy grant is narrowed to match.

nix/module-eval/bao-grants.nix pins the new shape: the bootstrap policy
may disable approle, and the granter's role unit never enables it.
This commit is contained in:
atlas 2026-09-28 21:54:40 +02:00 • committed by mara
commit 433429ebfd
4 changed files with 41 additions and 11 deletions

View file

@ -14,7 +14,7 @@
# not grant.
# The auth mounts. Reading `sys/auth` is how the unit checks, and `sudo` is
# what enabling one costs.
# what enabling or disabling one costs.
path "sys/auth" {
capabilities = ["read"]
}
@ -23,8 +23,11 @@ path "sys/auth/cert" {
capabilities = ["create", "update", "sudo"]
}
# Nothing mints an approle secret_id; the bootstrap step disables the mount
# rather than enabling it. `delete` is what `bao auth disable` costs, not
# `create`/`update` (verified with `bao auth disable -output-policy`).
path "sys/auth/approle" {
capabilities = ["create", "update", "sudo"]
capabilities = ["delete", "sudo"]
}
path "sys/auth/oidc" {

View file

@ -2687,9 +2687,11 @@ in
*) bao auth enable cert ;;
esac
# Nothing mints a secret_id; disabled rather than left unmounted,
# since a store from before this change may already have it.
case "$mounted" in
*'"approle/"'*) ;;
*) bao auth enable approle ;;
*'"approle/"'*) bao auth disable approle ;;
*) ;;
esac
# The UI's OIDC login; `swarm-bao-operator-viewer-policy` writes its

View file

@ -200,8 +200,9 @@ let
# One `bao …` invocation → the path and capabilities it needs, as
# `bao <cmd> -output-policy` reports them. Path-specific `sudo` (bao's
# root-protected paths, e.g. `pki/root` for a delete) does not follow from
# the verb, so only `auth enable` is checked for it. A verb not listed here
# needs a path no grant has, so it fails the case until it is taught.
# the verb, so only `auth enable`/`auth disable` are checked for it. A verb
# not listed here needs a path no grant has, so it fails the case until it
# is taught.
baoCallNeeds =
words:
let
@ -234,6 +235,11 @@ let
need "sys/auth" [ "read" ]
else if a 0 == "auth" && a 1 == "enable" then
need "sys/auth/${a 2}" (cu ++ [ "sudo" ])
else if a 0 == "auth" && a 1 == "disable" then
need "sys/auth/${a 2}" [
"delete"
"sudo"
]
else if a 0 == "write" then
need (a 1) cu
else if a 0 == "read" then
@ -1286,6 +1292,24 @@ let
]
&& grantFor bootstrapGrants "sys/policies/acl/swarm-controller" == null;
}
{
# Nothing mints a secret_id, so the bootstrap policy's approle grant is
# for tearing the mount down, not standing it up: `delete`+`sudo`
# (verified against `bao auth disable -output-policy`), not
# `create`/`update`. The granter's unit disables an existing mount and
# never enables one.
name = "the bootstrap policy may disable approle, and the granter's unit never enables it";
ok =
let
g = baoGrantHere.systemd.services.swarm-bao-granter-role.script;
in
(grantFor bootstrapGrants "sys/auth/approle").caps == [
"delete"
"sudo"
]
&& lib.hasInfix "bao auth disable approle" g
&& !(lib.hasInfix "bao auth enable approle" g);
}
{
# The controller's whole reach on any PKI mount: one role's issue
# endpoint. It cannot rewrite the role, sign a CSR of its choosing or
@ -1409,9 +1433,9 @@ let
&& grantFor bootstrapGrants "sys/auth/oidc" != null;
}
{
# Asked before attempted, like the cert and approle mounts, so re-running
# the step on a store that has the mount is a no-op; listed, or the UI's
# login page shows no OIDC tab.
# Asked before attempted, like the cert mount, so re-running the step
# on a store that has the mount is a no-op; listed, or the UI's login
# page shows no OIDC tab.
name = "the granter's role unit enables the oidc mount once, listed on the UI's login page";
ok =
let