98 lines
3.1 KiB
HCL
98 lines
3.1 KiB
HCL
|
|
# ops-mason-build — the authority ops-mason actually needs, and no more.
|
||
|
|
#
|
||
|
|
# SCOPE.md says ops-mason "never touches secret values, even transiently". Until
|
||
|
|
# now that was a promise in a document, kept by whoever was driving. This policy
|
||
|
|
# makes OpenBao enforce it: the builder can create policies, auth roles and KV
|
||
|
|
# path structure, and is denied every read of secret data on any mount.
|
||
|
|
#
|
||
|
|
# Granted through scripts/bao-session.sh as a 45-minute, named token, so an
|
||
|
|
# action in the audit log is attributable to a task rather than to whoever's
|
||
|
|
# operator session happened to be open.
|
||
|
|
|
||
|
|
# --- survey ----------------------------------------------------------------
|
||
|
|
# Phase 2 of the construction process is an existing-structure survey, and the
|
||
|
|
# plan for the state-hub lane was written without one because no session was
|
||
|
|
# available. That omission is why an AppRole was proposed on a cluster that
|
||
|
|
# already had kubernetes auth enabled.
|
||
|
|
path "sys/mounts" {
|
||
|
|
capabilities = ["read", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "sys/auth" {
|
||
|
|
capabilities = ["read", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "sys/policies/acl" {
|
||
|
|
capabilities = ["list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "sys/policies/acl/*" {
|
||
|
|
capabilities = ["create", "read", "update", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- auth roles ------------------------------------------------------------
|
||
|
|
path "auth/kubernetes/role/*" {
|
||
|
|
capabilities = ["create", "read", "update", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "auth/approle/role/*" {
|
||
|
|
capabilities = ["create", "read", "update", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- KV structure, never KV values -----------------------------------------
|
||
|
|
# Metadata carries versions, timestamps and custom_metadata — enough to confirm
|
||
|
|
# a path exists and that a paste-once delivery landed. It does not carry the
|
||
|
|
# value.
|
||
|
|
path "platform/metadata/*" {
|
||
|
|
capabilities = ["read", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "operators/metadata/*" {
|
||
|
|
capabilities = ["read", "list"]
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- verification ----------------------------------------------------------
|
||
|
|
# Minting a short-lived test token and asking what it can reach is how a lane is
|
||
|
|
# proven correctly scoped, positively and negatively.
|
||
|
|
path "auth/token/create" {
|
||
|
|
capabilities = ["create", "update"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "auth/token/revoke" {
|
||
|
|
capabilities = ["update"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "auth/token/lookup-self" {
|
||
|
|
capabilities = ["read"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "sys/capabilities" {
|
||
|
|
capabilities = ["create", "update"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "sys/capabilities-self" {
|
||
|
|
capabilities = ["create", "update"]
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- the line, stated as a denial ------------------------------------------
|
||
|
|
# Explicit deny outranks any grant, including one added here later by mistake.
|
||
|
|
# If ops-mason needs to prove a credential works, the consumer proves it, or a
|
||
|
|
# human does — see plans/state-hub-forge-derivation-read.md §8 for the one time
|
||
|
|
# this line was crossed and why it was recorded rather than glossed.
|
||
|
|
path "platform/data/*" {
|
||
|
|
capabilities = ["deny"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "operators/data/*" {
|
||
|
|
capabilities = ["deny"]
|
||
|
|
}
|
||
|
|
|
||
|
|
path "secret/data/*" {
|
||
|
|
capabilities = ["deny"]
|
||
|
|
}
|
||
|
|
|
||
|
|
# Mount management is deliberately absent. Enabling or tuning a secrets engine
|
||
|
|
# is a railiance-platform act; ops-mason builds inside mounts that already
|
||
|
|
# exist. A grant that needed sys/mounts/* would be a different, broader thing
|
||
|
|
# and should be recognised as such rather than folded in here.
|