OpenBao
Secrets management platform (HashiCorp Vault fork) for secure credential storage and dynamic secrets.
Overview
OpenBao provides centralized secrets management for the Ravenhelm infrastructure, including static secrets storage, dynamic credential generation, and encryption as a service.
| Property | Value |
|---|---|
| Image | openbao/openbao:latest |
| Container | openbao |
| URL | vault.ravenhelm.dev |
| Port | 8200 (internal) |
| Config | ~/ravenhelm/data/openbao/config/ |
| Data | ~/ravenhelm/data/openbao/data/ |
| Storage | Raft (single-node) |
| Version | 2.4.4 |
| Seal Type | GCP KMS (auto-unseal) |
| Status | Production |
Architecture
OpenBao Architecture
┌─────────────────────────────────────────────────────────────┐
│ OpenBao Server │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Secrets │ │ Auth │ │
│ │ Engines │ │ Methods │ │
│ │ - KV v2 │ │ - Token │ │
│ │ - Database │ │ - OIDC │ │
│ │ - PKI │ │ - AppRole │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────┐ │
│ │ Raft Storage Backend │ │
│ │ /openbao/data/ │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ GCP KMS (Auto-Unseal) │
│ Project: ravenhelm-agent-studio-dev │
│ Keyring: openbao-keyring │
│ Key: openbao-unseal-key │
└─────────────────────────────────────────────────────────────┘
Authentication Methods
OIDC (Human Access)
For interactive UI access via Zitadel SSO. See OpenBao OIDC for details.
Quick Login:
- Navigate to vault.ravenhelm.dev
- Select "OIDC" from the dropdown
- Click "Sign in with OIDC provider"
- Authenticate with Google via Zitadel
AppRole (Service Access)
For machine-to-machine authentication. See OpenBao AppRole for details.
Configured Services:
| Service | Role | 1Password Item |
|---|---|---|
| Norns | norns | OpenBao AppRole - Norns |
| n8n | n8n | OpenBao AppRole - n8n |
| Bifrost | bifrost | OpenBao AppRole - Bifrost |
| Voice Gateway | voice-gateway | OpenBao AppRole - Voice Gateway |
Token (Root/Emergency)
Root token stored in 1Password for emergency access only.
ROOT_TOKEN=$(op item get "OpenBao Root Keys" --vault ravenmask --fields "Root Token" --reveal)
Secrets Structure
Path Organization
secret/
├── api-keys/ # Third-party API credentials
│ ├── anthropic # ANTHROPIC_API_KEY
│ ├── openai # OPENAI_API_KEY
│ ├── deepgram # DEEPGRAM_API_KEY
│ ├── elevenlabs # ELEVENLABS_API_KEY
│ ├── google-maps # GOOGLE_MAPS_API_KEY
│ └── linear # LINEAR_API_KEY
├── database/ # Database credentials
│ ├── postgres # POSTGRES_USER, POSTGRES_PASSWORD
│ └── redis # REDIS_PASSWORD
├── integrations/ # Third-party service integrations
│ ├── gitlab # GITLAB_TOKEN
│ ├── notion # NOTION_API_KEY
│ ├── slack # SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET
│ ├── todoist # TODOIST_API_KEY
│ └── twilio # TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN
├── infrastructure/ # Cloud provider credentials
│ └── aws # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
└── services/ # Internal service credentials
├── langfuse # LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY
└── livekit # LIVEKIT_API_KEY, LIVEKIT_API_SECRET
Reading Secrets
# With root token
ROOT_TOKEN=$(op item get "OpenBao Root Keys" --vault ravenmask --fields "Root Token" --reveal)
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao kv get secret/api-keys/anthropic"
# Via API
curl -s -H "X-Vault-Token: $TOKEN" \
https://vault.ravenhelm.dev/v1/secret/data/api-keys/anthropic | jq '.data.data'
Writing Secrets
# Single field
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao kv put secret/api-keys/myservice api_key=xxx"
# Multiple fields
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao kv put secret/integrations/myservice \
client_id=xxx \
client_secret=yyy \
webhook_url=https://..."
Configuration
docker-compose.yml
Location: ~/ravenhelm/services/openbao/docker-compose.yml
services:
openbao:
image: openbao/openbao:latest
container_name: openbao
restart: unless-stopped
networks:
- ravenhelm_net
environment:
- TZ=America/Chicago
- BAO_ADDR=http://127.0.0.1:8200
- BAO_API_ADDR=http://openbao:8200
- BAO_CLUSTER_ADDR=http://openbao:8201
- SKIP_SETCAP=true
- GOOGLE_APPLICATION_CREDENTIALS=/openbao/gcp-credentials.json
volumes:
- /Users/ravenhelm/ravenhelm/data/openbao/data:/openbao/data
- /Users/ravenhelm/ravenhelm/data/openbao/config:/openbao/config:ro
- /Users/ravenhelm/ravenhelm/secrets/gcp-openbao.json:/openbao/gcp-credentials.json:ro
entrypoint: ["/bin/sh", "-c"]
command: ["exec bao server -config=/openbao/config"]
labels:
- "traefik.enable=true"
- "traefik.http.routers.openbao.rule=Host(`vault.ravenhelm.dev`)"
- "traefik.http.routers.openbao.entrypoints=websecure"
- "traefik.http.routers.openbao.tls.certresolver=letsencrypt"
- "traefik.http.services.openbao.loadbalancer.server.port=8200"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:8200/v1/sys/health?standbyok=true || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
ravenhelm_net:
external: true
config.hcl
Location: ~/ravenhelm/data/openbao/config/config.hcl
ui = true
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = true # TLS handled by Traefik
}
storage "raft" {
path = "/openbao/data"
node_id = "odin-openbao-1"
}
seal "gcpckms" {
project = "ravenhelm-agent-studio-dev"
region = "global"
key_ring = "openbao-keyring"
crypto_key = "openbao-unseal-key"
}
cluster_addr = "http://openbao:8201"
api_addr = "http://openbao:8200"
telemetry {
prometheus_retention_time = "60s"
disable_hostname = true
}
log_level = "info"
Policies
Admin Policy
Full access for OIDC-authenticated users:
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
Service Policies
| Policy | Service | Access |
|---|---|---|
norns | Norns Agent | secret/data/api-keys/*, secret/data/integrations/*, secret/data/database/* |
n8n | n8n Workflows | secret/data/integrations/*, secret/data/api-keys/*, secret/data/database/* |
bifrost | Bifrost | secret/data/database/*, secret/data/services/langfuse |
voice-gateway | Voice Gateway | secret/data/api-keys/deepgram, secret/data/api-keys/elevenlabs, secret/data/integrations/twilio, secret/data/services/livekit |
Quick Commands
# Check status
ssh ravenhelm@100.115.101.81 "docker exec openbao bao status"
# View logs
ssh ravenhelm@100.115.101.81 "docker logs -f openbao"
# List secrets engines
ROOT_TOKEN=$(op item get "OpenBao Root Keys" --vault ravenmask --fields "Root Token" --reveal)
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao secrets list"
# List auth methods
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao auth list"
# List policies
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao policy list"
# List all secrets in a path
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao kv list secret/"
Recovery Keys
After auto-unseal migration, the original Shamir unseal keys are now recovery keys:
| Property | Value |
|---|---|
| 1Password Item | "OpenBao Root Keys" |
| Vault | ravenmask |
| Total Keys | 5 |
| Threshold | 3 (any 3 keys required) |
| Purpose | Emergency recovery, root token generation |
When to Use:
- Generate new root token if lost
- Recover if GCP KMS is unavailable
- Disaster recovery scenarios
Backup & Restore
Create Backup
ROOT_TOKEN=$(op item get "OpenBao Root Keys" --vault ravenmask --fields "Root Token" --reveal)
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao operator raft snapshot save /openbao/data/backup.snap"
ssh ravenhelm@100.115.101.81 "docker cp openbao:/openbao/data/backup.snap ~/ravenhelm/backups/openbao-$(date +%Y%m%d).snap"
Restore from Backup
ssh ravenhelm@100.115.101.81 "docker cp ~/ravenhelm/backups/openbao-YYYYMMDD.snap openbao:/openbao/data/restore.snap"
ssh ravenhelm@100.115.101.81 "docker exec -e BAO_TOKEN=$ROOT_TOKEN openbao bao operator raft snapshot restore /openbao/data/restore.snap"
Related Documentation
- OpenBao Auto-Unseal - GCP KMS auto-unseal configuration
- OpenBao OIDC - Zitadel SSO authentication
- OpenBao AppRole - Service authentication
- OpenBao Norns Integration - Norns vault integration
- OpenBao Operations Runbook - Day-to-day operations
1Password Items
| Item | Purpose |
|---|---|
OpenBao Root Keys | Root token, recovery keys |
OpenBao OIDC - Zitadel | OIDC client credentials |
OpenBao AppRole - Norns | Norns service credentials |
OpenBao AppRole - n8n | n8n service credentials |
OpenBao AppRole - Bifrost | Bifrost service credentials |
OpenBao AppRole - Voice Gateway | Voice Gateway service credentials |
GCP Service Account - ravenmask | GCP credentials for auto-unseal |
References
- OpenBao Documentation
- HashiCorp Vault Docs (API compatible)