maidn-cli/docs/secret-grants.md

150 lines
5 KiB
Markdown

# Application secret grants
Maidn stores secret values in OpenBao. Git contains only references and access
policy. A repository does not get OpenBao access: one named workload identity
gets one reviewed grant.
## Grant classes
| Consumer | OpenBao path | Kubernetes namespace | Intended use |
| --- | --- | --- | --- |
| `build` | declared `apps/<app>/<secret>` entries | `tekton-pipelines` | Read-only dependency credentials |
| `publish` | declared `apps/<app>/<secret>` entries | `tekton-pipelines` | One app's artifact repository credential |
| `runtime` | declared `apps/<app>/<secret>` entries | `<app>-<environment>` | Service runtime credentials |
| shared | `shared/<name>/*` | Granted consumer only | Deliberately shared broker, database, or API credentials |
`build` code is repository-controlled. Anything granted to it is readable by a
pull request author. Do not grant deployment, production, Git write, or
administrator credentials to a build.
## Bootstrap configuration
Declare access in the private bootstrap configuration. This declaration has no
secret values and is reviewed with the platform configuration:
```yaml
secretGrants:
- application: orders-api
consumer: publish
secrets:
- registry
shared:
- internal-npm
- application: orders-api
consumer: runtime
environment: staging
secrets:
- database-staging
shared:
- rabbitmq
- application: orders-api
consumer: runtime
environment: production
secrets:
- database-production
shared:
- rabbitmq
```
MaidnCLI validates application, consumer, environment, application-secret, and
shared-grant names.
It creates one OpenBao policy and Kubernetes-auth role for every declaration.
The role names are deterministic:
```text
maidn-<app>-build
maidn-<app>-publish
maidn-<app>-runtime-<environment>
```
The policy permits only the exact `apps/<app>/<secret>` paths and
`shared/<name>/*` paths listed in its declaration. The CLI stores one property
named `value` at each `apps/<app>/<secret>` or `shared/<group>/<secret>` path.
A shared value is stored once, for example at `shared/rabbitmq/password`, and
each service requiring it declares that shared grant. Do not copy it into
application paths.
## GitOps resources
The application environment manifests create the matching ServiceAccount,
SecretStore, and ExternalSecret. These resources are reviewed GitOps content;
never create them with `kubectl apply`.
For `orders-api` staging, use the matching identity and namespace:
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: maidn-orders-api-runtime-staging
namespace: orders-api-staging
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: openbao-orders-api-staging
namespace: orders-api-staging
spec:
provider:
vault:
server: http://openbao.openbao.svc:8200
path: secret
version: v2
auth:
kubernetes:
mountPath: kubernetes
role: maidn-orders-api-runtime-staging
serviceAccountRef:
name: maidn-orders-api-runtime-staging
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: orders-api-rabbitmq
namespace: orders-api-staging
spec:
refreshInterval: 1h
secretStoreRef:
name: openbao-orders-api-staging
kind: SecretStore
target:
name: orders-api-rabbitmq
creationPolicy: Owner
data:
- secretKey: value
remoteRef:
key: shared/rabbitmq/password
property: value
```
The workload references only `orders-api-rabbitmq` in its own namespace. Each
application/environment needs a separate namespace; do not put runtime
secrets in shared `staging` or `production` namespaces.
## Artifact repositories
Create one credential per application and artifact target. Its upstream
permissions must be limited to the exact package, hosted repository, or object
prefix. Examples: one npm scope, one Maven hosted repository, one OCI image,
or S3 `PutObject` for one prefix.
Use `publish` for credentials needed to upload a completed artifact. Use
`build` only for credentials that a build must read, such as a private package
registry. A custom build upload is an exception: it exposes the token to build
code and therefore requires a narrowly scoped, disposable credential.
## Operations
1. Create the least-privilege upstream credential.
2. Write its value to the declared OpenBao path with `cicd-tool app secret set`
using stdin or `--file` and a least-privilege `--token-file`. Never put a
value or token in YAML, a URL, a command argument, output, or Git.
3. Add the reviewed grant and GitOps resources.
4. Bootstrap or reconcile to create the OpenBao role and policy.
5. Verify the target ExternalSecret becomes Ready without printing its Secret.
6. On revocation, remove the grant and ExternalSecret, revoke the upstream
credential, then restart affected workloads.
See [secrets.md](secrets.md) for encrypted operational-material rules and
[runbooks/credential-rotation.md](runbooks/credential-rotation.md) for rotation.