Documentation
¶
Overview ¶
Package secretaccess derives OpenBao JWT auth mounts, roles and ACL policies from the set of secret names each Kubernetes namespace reads from the store.
It is pure: no I/O, no clock, no randomness. The same Request always yields the same Document, byte for byte, so the two programs that apply it (forge's OpenBao converge Job for a self-run store, and a hosted control plane for its own store) cannot disagree about who may read what. Applying the Document to a live OpenBao is the caller's job; Mount, Role and Policy each carry the API path and payload to write.
Model ¶
One JWT auth mount per cluster (auth/jwt-<cluster id>), trusting that cluster's own service-account issuer. One role per (env, cluster, namespace), bound to the namespace's reliant-secrets service account and to the cluster's audience, issuing 10-minute batch tokens carrying exactly one policy. That policy lists, by exact path, the names the namespace's ExternalSecrets reference:
path "secret/data/tenants/<org>/<env>/STRIPE_SECRET_KEY" {
capabilities = ["read"]
}
No globs, no list, no metadata: a compromised namespace can read the values it already receives and nothing else. The one escape is a WildcardRead, which needs a stated reason and is confined to a single env's directory.
Changing the name set safely ¶
Apply Widen (the union of the old and new shapes) before the workloads that need the new names roll out, then Derive the new shape alone once they converge, and delete whatever Removed reports. Narrowing never races a rollout that still needs the old name.
Consuming it ¶
Callers declare their own narrow interface for the thing that writes to OpenBao (typically one method, put-policy, and one, put-role). Nothing here performs a network call, so there is no client to mock.
Index ¶
Constants ¶
const ( // DefaultKVMount is the KV-v2 mount the component creates. DefaultKVMount = "secret" // DefaultRoot is the first path segment under the mount for values that // are delivered to a cluster. Host-process local envs use "local". DefaultRoot = "tenants" // DefaultServiceAccount is the service account in each namespace whose // projected token logs in. Role subjects are pinned to it. DefaultServiceAccount = "reliant-secrets" // TokenTTL is the lifetime of every issued token. It is also the maximum: // batch tokens are not renewable, ESO logs in again. TokenTTL = 10 * time.Minute // TokenType is the OpenBao token type roles issue. TokenType = "batch" )
const MountPrefix = "jwt-"
MountPrefix starts every JWT auth mount name; the cluster id follows.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Binding ¶
type Binding struct {
Cluster string
Namespace string
// Names are the secret names the namespace's ExternalSecrets reference,
// each granted by exact path.
Names []string
// Wildcards are the escape from exact paths; each needs a reason.
Wildcards []WildcardRead
}
Binding is the set of names one namespace of one cluster reads.
type Cluster ¶
type Cluster struct {
// ID names the cluster and its mount (auth/jwt-<ID>).
ID string
// Audience is the token audience the cluster's SecretStores request and
// the roles require. It pins a registration: a token minted for another
// audience is refused.
Audience string
// Trust says how OpenBao verifies the cluster's tokens. Leave it zero when
// the applier fills the mount's config itself (for instance a converge
// Job that fetches the cluster's JWKS); a non-zero Trust is emitted as
// the mount's config.
Trust Trust
}
Cluster is one registered cluster: the identity of its auth mount and the issuer that vouches for its tokens.
type Document ¶
Document is the full set of objects a Request needs in OpenBao, sorted so the same Request always yields an identical Document.
type Mount ¶
Mount is one cluster's JWT auth method.
func (Mount) ConfigPath ¶
ConfigPath is the API path of the mount's verification config (PUT; payload Mount.ConfigPayload).
func (Mount) ConfigPayload ¶
ConfigPayload is the verification config, or nil when the cluster declared no Trust and the applier supplies the mount's config itself.
func (Mount) EnablePath ¶
EnablePath is the API path that enables the mount (PUT; payload Mount.EnablePayload). OpenBao answers 400 "path is already in use" when the mount exists, so an applier that reruns lists sys/auth first and enables only the mounts that are missing; the config, role and policy writes are idempotent overwrites.
func (Mount) EnablePayload ¶
EnablePayload is the body of the enable call.
type Policy ¶
Policy is one ACL policy.
func (Policy) Path ¶
Path is the API path of the policy (PUT; payload Policy.Payload).
type Request ¶
type Request struct {
Scope Scope
// ServiceAccount is the account roles bind to. Default
// [DefaultServiceAccount].
ServiceAccount string
Clusters []Cluster
Bindings []Binding
}
Request is everything Derive needs: one env, the clusters that deliver it, and what each namespace reads.
func Widen ¶
Widen returns the union of two requests for the same env: every cluster and every namespace of either, with the names of a namespace present in both merged. Deriving the union before a promotion and the new request alone after it keeps a rollout that still needs an old name from losing access.
type Role ¶
type Role struct {
// Mount is the auth mount name ("jwt-<cluster>").
Mount string
Name string
BoundAudiences []string
// BoundSubject is the exact service-account subject:
// system:serviceaccount:<namespace>:<service account>.
BoundSubject string
Policies []string
}
Role is one namespace's login role on a cluster's mount. It carries the same name as its policy.
func (Role) Path ¶
Path is the API path of the role (PUT; payload Role.Payload).
func (Role) Payload ¶
Payload is the role's body: a JWT role bound to one audience and one subject that issues short-lived batch tokens (verified against OpenBao 2.6.2). The token also carries OpenBao's built-in default policy, which only lets it look itself up; ESO's Vault provider relies on that, so it is kept.
type Rule ¶
type Rule struct {
Path string
Capabilities []string
// WildcardReadReason is set on a rule whose Path ends in "*". It is
// written into the policy as a comment.
WildcardReadReason string
}
Rule grants capabilities on one path.
type Scope ¶
type Scope struct {
// Mount is the KV-v2 mount. Default [DefaultKVMount].
Mount string
// Root is "tenants" (delivered to a cluster, the default) or "local".
Root string
Org string
Env string
}
Scope addresses one env's directory in the KV mount: <Mount>/data/<Root>/<Org>/<Env>/<NAME>. For a self-run store Org is the project and Env the env name.
func (Scope) PolicyName ¶
PolicyName is the name of the policy, and of the role that carries it, for one namespace of one cluster:
env-<env>-<cluster>-<hash>
env and cluster are short readable labels (see label); hash is eight hex digits of a digest of the whole (org, env, cluster, namespace) tuple, so two bindings that share both labels still get distinct names. Policies are global in OpenBao, which is why the env is part of the name.
func (Scope) SecretPath ¶
SecretPath is the KV-v2 API path of one secret's current version, the path a policy grants and a reader requests. name is not validated.
type Trust ¶
type Trust struct {
// OIDCDiscoveryURL is the issuer's discovery base (GKE publishes it).
OIDCDiscoveryURL string
// BoundIssuer is the required "iss" claim. Defaults to OIDCDiscoveryURL.
BoundIssuer string
// PublicKeys are PEM keys, for clusters whose issuer is not reachable from
// OpenBao.
PublicKeys []string
}
Trust is the mount's verification config. At most one of OIDCDiscoveryURL and PublicKeys may be set.
type WildcardRead ¶
type WildcardRead struct {
// Pattern is a name prefix ending in a single "*" ("STRIPE_*", or "*" for
// the whole env). It cannot leave the env's directory.
Pattern string
// Reason is why exact names are not enough.
Reason string
}
WildcardRead grants read on every name matching Pattern within the env's own directory. It exists for the rare consumer whose names are not known ahead of time; every use is a standing exception, so Reason is mandatory and is written into the policy as a comment.