Founder decision 2026-10-18, "build, but never log in": a plan-scoped grant
that writes workload-kv-read-* policies, external-secrets-* and AppRole role
definitions, and create-only placeholder KV under platform/{workloads,suppliers},
and denies role-id, secret-id, token creation and all value reads, so the
MASON-IN-0004 chain stays broken. Adds bao-session.sh build, policy tests,
INTENT/AGENTS alignment and MASON-WP-0015. Not installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 278674@bnt-lap001
Assistant-Session: 64827407-e196-4ce3-b70c-76e7b537493f
133 lines
5.9 KiB
HCL
133 lines
5.9 KiB
HCL
# ops-mason-construct — build security structure; never become what it builds.
|
|
#
|
|
# Founder decision 2026-10-18 (MASON-IN-0005, supersedes the read-only
|
|
# resolution of MASON-IN-0004): ops-mason is the builder (INTENT.md, phase 4).
|
|
# It creates, changes and tears down policies, auth roles and KV structure
|
|
# for an approved construction plan. ops-warden routes to what it built.
|
|
#
|
|
# How the MASON-IN-0004 escalation stays closed: that chain was
|
|
# write policy -> bind role -> read role-id -> mint secret-id -> log in.
|
|
# This grant keeps the first two steps (the job) and holds none of the steps
|
|
# that turn a role into a login: no role-id, no secret-id, no token creation,
|
|
# no KV value reads. ops-mason can build a lane but cannot use one. The
|
|
# remaining risk, a built policy broader than its plan, is caught after the
|
|
# fact by a post-build check, policy text vs plan file (MASON-WP-0015-T04), and the
|
|
# audit log (display_name claude-*).
|
|
#
|
|
# Granted only by the founder's own OIDC session through
|
|
# GRANT_POLICY=ops-mason-construct ./scripts/bao-session.sh grant <plan-task>
|
|
# as a 45-minute named token, after the plan is approved. Survey-only sessions
|
|
# keep using ops-mason-survey.
|
|
#
|
|
# Path priority (same note as ops-mason-survey.hcl): within one policy the most
|
|
# specific path wins, and a `+` segment loses to a literal one. The denies below
|
|
# are literal or more specific than the grants they bound.
|
|
#
|
|
# allowed_parameters: OpenBao glob support on list-valued parameters must be
|
|
# confirmed by the negative tests in MASON-WP-0015-T03 before this is relied on.
|
|
|
|
# --- everything the survey grant can read -----------------------------------
|
|
path "sys/mounts" { capabilities = ["read", "list"] }
|
|
path "sys/auth" { capabilities = ["read", "list"] }
|
|
path "sys/policies/acl" { capabilities = ["list"] }
|
|
path "auth/approle/role" { capabilities = ["list"] }
|
|
path "auth/netkingdom/role/*" { capabilities = ["read", "list"] }
|
|
path "auth/jwt/role/*" { capabilities = ["read", "list"] }
|
|
path "auth/keycape-services/role/*" { capabilities = ["read", "list"] }
|
|
path "operators/metadata/*" { capabilities = ["read", "list"] }
|
|
path "sys/capabilities" { capabilities = ["update"] }
|
|
path "sys/capabilities-self" { capabilities = ["update"] }
|
|
path "auth/token/lookup-self" { capabilities = ["read"] }
|
|
path "auth/token/revoke-self" { capabilities = ["update"] }
|
|
|
|
# Every policy stays readable for the survey and for verification.
|
|
path "sys/policies/acl/*" {
|
|
capabilities = ["read"]
|
|
}
|
|
|
|
# --- build: workload read policies ------------------------------------------
|
|
# The lane convention (railiance-platform/openbao/policies). Content is not
|
|
# constrainable in OpenBao; the post-build check compares it with the plan.
|
|
path "sys/policies/acl/workload-kv-read-*" {
|
|
capabilities = ["create", "read", "update", "delete"]
|
|
}
|
|
|
|
# --- build: Kubernetes auth roles for External Secrets ----------------------
|
|
# Role may carry only workload-kv-read-* policies; legacy `policies` refused.
|
|
path "auth/kubernetes/role/external-secrets-*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
allowed_parameters = {
|
|
"token_policies" = ["workload-kv-read-*"]
|
|
"*" = []
|
|
}
|
|
denied_parameters = {
|
|
"policies" = []
|
|
}
|
|
}
|
|
|
|
path "auth/kubernetes/role/*" {
|
|
capabilities = ["read", "list"]
|
|
}
|
|
|
|
# --- build: AppRole role definitions (never credentials) --------------------
|
|
# Definition only. role-id and secret-id are denied below, so an AppRole
|
|
# lane's credential delivery stays a founder act.
|
|
path "auth/approle/role/+" {
|
|
capabilities = ["create", "read", "update", "delete"]
|
|
allowed_parameters = {
|
|
"token_policies" = ["workload-kv-read-*"]
|
|
"*" = []
|
|
}
|
|
denied_parameters = {
|
|
"policies" = []
|
|
}
|
|
}
|
|
|
|
# --- build: KV structure on the platform mount ------------------------------
|
|
# create without read/update: a new path can get placeholder values (`xxx`),
|
|
# an existing value can be neither read nor overwritten.
|
|
path "platform/data/workloads/*" {
|
|
capabilities = ["create"]
|
|
}
|
|
|
|
path "platform/data/suppliers/*" {
|
|
capabilities = ["create"]
|
|
}
|
|
|
|
# Describe, inventory and tear down paths (custom_metadata, metadata delete).
|
|
path "platform/metadata/workloads/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
|
|
path "platform/metadata/suppliers/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
|
|
path "platform/metadata/*" {
|
|
capabilities = ["read", "list"]
|
|
}
|
|
|
|
# --- the grant cannot widen itself or every token ---------------------------
|
|
path "sys/policies/acl/ops-mason-construct" { capabilities = ["deny"] }
|
|
path "sys/policies/acl/ops-mason-survey" { capabilities = ["deny"] }
|
|
path "sys/policies/acl/default" { capabilities = ["deny"] }
|
|
path "sys/policies/acl/root" { capabilities = ["deny"] }
|
|
|
|
# --- never become what it builds ---------------------------------------------
|
|
path "auth/approle/role/+/role-id" { capabilities = ["deny"] }
|
|
path "auth/approle/role/+/secret-id" { capabilities = ["deny"] }
|
|
path "auth/approle/role/+/secret-id/*" { capabilities = ["deny"] }
|
|
path "auth/approle/role/+/secret-id-accessor/*" { capabilities = ["deny"] }
|
|
path "auth/approle/role/+/custom-secret-id" { capabilities = ["deny"] }
|
|
path "auth/token/create" { capabilities = ["deny"] }
|
|
path "auth/token/create/*" { capabilities = ["deny"] }
|
|
path "auth/token/create-orphan" { capabilities = ["deny"] }
|
|
|
|
# --- never read a value --------------------------------------------------------
|
|
path "platform/data/*" { capabilities = ["deny"] }
|
|
path "operators/data/*" { capabilities = ["deny"] }
|
|
path "secret/data/*" { capabilities = ["deny"] }
|
|
|
|
# Not granted, on purpose: sys/mounts/* and sys/auth/* (no new mounts or auth
|
|
# methods), auth/token/revoke (other tokens), operators/ and secret/ writes,
|
|
# any policy outside workload-kv-read-*, any role outside the two shapes above.
|