SECRETS_MIGRATION_GUIDE
Secrets Migration Guide: Canonical URIs and Pointers (OpenBao, File/CSI, DB)
Audience: Developers and DevOps. Purpose: replace plain .env secrets with secure pointers and canonical Secret URIs that resolve at runtime through the service’s VaultService PEP.
TL;DR — What to use where
-
Platform/service secrets (rotatable, centralized) → OpenBao KVv2
- Pointer:
openbao+kv2://secret/<path>#<KEY>[?version=N] - Examples: encryption keys, third‑party API keys, service bus connection strings.
- Pointer:
-
Bootstrap secrets (must exist before app can call Vault) → File (Docker Secret / K8s CSI)
- Pointer:
file:primary:<name>(defaults to/run/secrets/<name>) - Examples: DB connection string for Alembic, early SMTP password in dev.
- Pointer:
-
App‑managed OAuth/API credentials (user‑created in UI/API) → Encrypted DB credential
- Pointer:
db:credentials:<uuid> - Examples: Jira/ServiceNow OAuth clients created via
/api/crud/credentials(Connections page).
- Pointer:
-
Local‑only prototyping → YAML (dev)
- Pointer:
yaml://secret/<path>#<KEY>
- Pointer:
Canonical Secret URIs — quick grammar
- Format:
provider[+engine]://mount/path#fragment?params - Lowercase provider/engine;
mountis first path segment; case‑sensitive path. - KVv2 version pin:
...?version=N(never auto‑forward when pinned). - Examples:
openbao+kv2://secret/crud#CREDENTIAL_ENCRYPTION_KEYopenbao+kv2://secret/azure/msgraph#CLIENT_SECRET?version=3file:primary:db-conn-stringdb:credentials:0c1c2a22-1b71-44a2-8b2a-1b4d4a8f8d06
How resolution works (overview)
- All pointers flow through
VaultService(PEP) for parse/normalize, tenant‑guard checks, PDP grant enforcement, provider fetch, and audits. - Providers included:
openbao(KVv2),hashicorp(KVv2),file,db,yaml(dev).
Compose/K8s prerequisites
- Set once (already present in
docker-compose-authzen4.ymlforcrud-service):VAULT_URL,VAULT_TOKEN,FILE_MOUNT_PATH=/run/secretsTENANT_ID=dev,TENANT_ALLOWED_MOUNTS=secret- Provide a non‑leaky salt:
SECRET_TENANT_SALT=...
- Kubernetes CSI: mount secrets under a path (e.g.,
/mnt/secrets-store) and setFILE_MOUNT_PATH=/mnt/secrets-storein the workload.
Migration steps (from .env to pointers)
-
Inventory all secret‑bearing variables in your
.env. -
Classify each as one of:
- Bootstrap (needed before the app can reach Vault)
- Platform/service (rotatable, centralized)
- App‑managed OAuth/API credential (created by users)
-
Bootstrap → Docker Secret / K8s CSI (File provider)
- Place secret material in
CRUDService/config_secrets/<name>.txt(Compose) or mount via CSI. - Declare/mount under
secrets:in Compose (or CSI volume in K8s). - Reference with
file:primary:<name>.
- Place secret material in
-
Platform/service → OpenBao KVv2
- Seed values via the Secrets API (below) or OpenBao UI/CLI.
- Reference with
openbao+kv2://secret/<path>#<KEY>[?version=N].
-
App‑managed OAuth/API → DB credential
- Create via Visual Designer or API
POST /api/crud/credentials. - Reference with
db:credentials:<uuid>.
- Create via Visual Designer or API
-
Replace literals in your
.env/compose env with the new pointers.
Concrete mappings (examples)
- PostgreSQL (bootstrap)
POSTGRES_USER=file:primary:db-user
POSTGRES_PASSWORD=file:primary:db-password
DATABASE_URL=file:primary:db-conn-string
- Encryption key (platform)
CREDENTIAL_ENCRYPTION_KEY_POINTER=openbao+kv2://secret/crud#CREDENTIAL_ENCRYPTION_KEY
- OpenAI (platform)
OPENAI_API_KEY=openbao+kv2://secret/ai/openai#API_KEY
- Service Bus (platform)
SERVICE_BUS_CONNECTION_STRING=openbao+kv2://secret/azure/servicebus#CONNECTION_STRING
- SMTP (choose one)
# Bootstrap via file (good for bring‑up)
SMTP_PASSWORD=file:primary:smtp-password
# Or centralized in OpenBao
# SMTP_PASSWORD=openbao+kv2://secret/email/smtp#PASSWORD
- App‑managed OAuth credential (created in UI/API)
MY_SYSTEM_CREDENTIAL_POINTER=db:credentials:<uuid>
Seeding and verification (dev)
- Create/Update a KVv2 secret via the CRUDService Secrets API:
curl -s -X POST "http://localhost:8000/api/secrets" \
-H "Content-Type: application/json" \
-d '{"uri":"openbao+kv2://secret/crud#CREDENTIAL_ENCRYPTION_KEY","value":"<base64-or-random>"}'
- Read a value to verify pointer resolution (dev‑mode guards relaxed):
curl -s "http://localhost:8000/api/secrets/value?uri=openbao+kv2://secret/crud#CREDENTIAL_ENCRYPTION_KEY"
- Watch live secret events during testing (SSE):
curl -N "http://localhost:8000/api/secrets/events"
Creating app‑managed credentials
- Create a credential via API:
curl -s -X POST "http://localhost:8000/api/crud/credentials" \
-H "Content-Type: application/json" \
-d '{
"name":"jira-api",
"type":"oauth2",
"data":{
"client_id":"...",
"client_secret":"...",
"auth_url":"...",
"token_url":"...",
"redirect_url":"https://automate.ocg.labs.empowernow.ai/auth/callback",
"scope":"read write"
}
}'
-
The response contains
id. Reference it as:db:credentials:<id>. -
Frontend helper (Visual Designer):
credentialPointer(id) // → db:credentials:<id>
Kubernetes CSI notes
- Mount secrets with Secrets Store CSI to a path such as
/mnt/secrets-store. - Set
FILE_MOUNT_PATH=/mnt/secrets-storein the workload env. - Use
file:primary:<name>to reference mounted keys/files. - Prefer
openbao+kv2://...for platform secrets when you want versioning/rotation and central management.
Security hardening (prod)
- Enable API guards in
crud-service:SECRETS_API_REQUIRE_AUTH=trueSECRETS_ENFORCE_SCOPES=trueENABLE_AUTHORIZATION=true
- Keep
TENANT_ALLOWED_MOUNTS=secret; set a strongSECRET_TENANT_SALT. - Prefer OpenBao over YAML/file for long‑term storage (file is best for bootstrap only).
Troubleshooting
TENANT_MOUNT_MISMATCH/400 → your URImountisn’t allowed; checkTENANT_ALLOWED_MOUNTSand tenant id.- KVv2
?version=N:- Soft‑deleted → typed error (HTTP 409) in provider tests.
- Hard‑destroyed → typed error (HTTP 409) in provider tests.
file:pointers:- Ensure the file exists under
FILE_MOUNT_PATH(/run/secretsin Compose; your CSI mount in K8s). - For Windows dev, ensure path mappings do not interfere with container mount.
- Ensure the file exists under
Optional enhancement
- SSE payloads from
/api/secrets/eventscan be enriched withkv_versionandresource_reffor richer live views in UIs. Not required for analytics queries, but helpful for observability.
Team checklist (copy/paste)
- Inventory .env secrets and classify (bootstrap / platform / app‑managed).
- Bootstrap → Docker Secret / K8s CSI; reference with
file:primary:<name>; setFILE_MOUNT_PATH. - Platform → OpenBao KVv2; seed via Secrets API; reference with
openbao+kv2://.... - App‑managed → create via
/api/crud/credentials; referencedb:credentials:<uuid>. - Replace literals in
.env/compose env with pointers. - Verify with
/api/secrets/value?uri=...and application flows; monitor/api/secrets/eventsduring testing. - In prod, enable secrets API auth/scope enforcement and PDP PEP gate.
References
CRUDService/docker-compose-authzen4.ymlCRUDService/src/services/vault_service.py(PEP)CRUDService/src/vault_strategies/openbao_vault_strategy.py,.../db_vault_strategy.pyCRUDService/src/api/secrets_routes.py(Secrets API)CRUDService/docs/secrets_v4_design.md(design rationale, canonicalization, audits, grants)