feat: add auth-capability lanes and pilot closeout
Add the warden-sign auth-capability lane, AppRole handoff, verification guards, docs, and tests. Point the whynot-design pilot at the canonical decision and add the real publish closeout preflight/runbook.
This commit is contained in:
parent
a621fbaffd
commit
6382139890
27 changed files with 1455 additions and 107 deletions
|
|
@ -143,28 +143,49 @@ class OpenBaoClient:
|
|||
if enable.returncode != 0 and "already in use" not in enable.stderr:
|
||||
raise BackendError(f"could not enable approle: {enable.stderr.strip()}")
|
||||
|
||||
def write_approle(self, role_name: str, policies: list[str], ttl: str = "30m") -> None:
|
||||
self._run_ok(
|
||||
[
|
||||
"write",
|
||||
f"auth/approle/role/{role_name}",
|
||||
f"token_policies={','.join(policies)}",
|
||||
f"token_ttl={ttl}",
|
||||
f"token_max_ttl={ttl}",
|
||||
"secret_id_num_uses=0",
|
||||
"token_num_uses=0",
|
||||
]
|
||||
)
|
||||
def write_approle(
|
||||
self,
|
||||
role_name: str,
|
||||
policies: list[str],
|
||||
ttl: str = "30m",
|
||||
*,
|
||||
max_ttl: str | None = None,
|
||||
secret_id_ttl: str | None = None,
|
||||
secret_id_num_uses: int = 0,
|
||||
token_num_uses: int = 0,
|
||||
) -> None:
|
||||
args = [
|
||||
"write",
|
||||
f"auth/approle/role/{role_name}",
|
||||
f"token_policies={','.join(policies)}",
|
||||
f"token_ttl={ttl}",
|
||||
f"token_max_ttl={max_ttl or ttl}",
|
||||
f"secret_id_num_uses={secret_id_num_uses}",
|
||||
f"token_num_uses={token_num_uses}",
|
||||
]
|
||||
if secret_id_ttl:
|
||||
args.append(f"secret_id_ttl={secret_id_ttl}")
|
||||
self._run_ok(args)
|
||||
|
||||
def approle_exists(self, role_name: str) -> bool:
|
||||
proc = self._run(["read", f"auth/approle/role/{role_name}"])
|
||||
return proc.returncode == 0
|
||||
|
||||
def read_approle_role_id(self, role_name: str) -> str:
|
||||
return self._run_ok(
|
||||
["read", "-field=role_id", f"auth/approle/role/{role_name}/role-id"]
|
||||
).strip()
|
||||
|
||||
def create_approle_secret_id(self, role_name: str) -> str:
|
||||
return self._run_ok(
|
||||
["write", "-field=secret_id", "-f", f"auth/approle/role/{role_name}/secret-id"]
|
||||
).strip()
|
||||
|
||||
def approle_login_token(self, role_name: str) -> str:
|
||||
"""Login as the approle and return a scoped child token. Used only for
|
||||
verification / exec delivery; never logged."""
|
||||
role_id = self._run_ok(
|
||||
["read", "-field=role_id", f"auth/approle/role/{role_name}/role-id"]
|
||||
).strip()
|
||||
secret_id = self._run_ok(
|
||||
["write", "-field=secret_id", "-f", f"auth/approle/role/{role_name}/secret-id"]
|
||||
).strip()
|
||||
role_id = self.read_approle_role_id(role_name)
|
||||
secret_id = self.create_approle_secret_id(role_name)
|
||||
token = self._run_ok(
|
||||
[
|
||||
"write",
|
||||
|
|
@ -176,6 +197,28 @@ class OpenBaoClient:
|
|||
).strip()
|
||||
return token
|
||||
|
||||
def token_capabilities(self, path: str, *, token: str) -> list[str]:
|
||||
"""Return token capabilities for a path without returning any secret value."""
|
||||
client = OpenBaoClient(addr=self.addr, token=token, bao_bin=self.bao_bin)
|
||||
out = client._run_ok(["token", "capabilities", "-format=json", path])
|
||||
try:
|
||||
data = json.loads(out)
|
||||
except json.JSONDecodeError:
|
||||
return [line.strip() for line in out.splitlines() if line.strip()]
|
||||
if isinstance(data, list):
|
||||
return [str(item) for item in data]
|
||||
if isinstance(data, dict):
|
||||
caps = data.get("capabilities", [])
|
||||
if isinstance(caps, list):
|
||||
return [str(item) for item in caps]
|
||||
return []
|
||||
|
||||
def delete_policy(self, name: str) -> None:
|
||||
self._run_ok(["policy", "delete", name])
|
||||
|
||||
def delete_approle(self, role_name: str) -> None:
|
||||
self._run_ok(["delete", f"auth/approle/role/{role_name}"])
|
||||
|
||||
# -- KV v2 -------------------------------------------------------------
|
||||
|
||||
def kv_mount_exists(self, mount: str) -> bool:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue