Skip to main content

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.

PropertyValue
Imageopenbao/openbao:latest
Containeropenbao
URLvault.ravenhelm.dev
Port8200 (internal)
Config~/ravenhelm/data/openbao/config/
Data~/ravenhelm/data/openbao/data/
StorageRaft (single-node)
Version2.4.4
Seal TypeGCP KMS (auto-unseal)
StatusProduction

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:

  1. Navigate to vault.ravenhelm.dev
  2. Select "OIDC" from the dropdown
  3. Click "Sign in with OIDC provider"
  4. Authenticate with Google via Zitadel

AppRole (Service Access)

For machine-to-machine authentication. See OpenBao AppRole for details.

Configured Services:

ServiceRole1Password Item
NornsnornsOpenBao AppRole - Norns
n8nn8nOpenBao AppRole - n8n
BifrostbifrostOpenBao AppRole - Bifrost
Voice Gatewayvoice-gatewayOpenBao 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

PolicyServiceAccess
nornsNorns Agentsecret/data/api-keys/*, secret/data/integrations/*, secret/data/database/*
n8nn8n Workflowssecret/data/integrations/*, secret/data/api-keys/*, secret/data/database/*
bifrostBifrostsecret/data/database/*, secret/data/services/langfuse
voice-gatewayVoice Gatewaysecret/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:

PropertyValue
1Password Item"OpenBao Root Keys"
Vaultravenmask
Total Keys5
Threshold3 (any 3 keys required)
PurposeEmergency 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"


1Password Items

ItemPurpose
OpenBao Root KeysRoot token, recovery keys
OpenBao OIDC - ZitadelOIDC client credentials
OpenBao AppRole - NornsNorns service credentials
OpenBao AppRole - n8nn8n service credentials
OpenBao AppRole - BifrostBifrost service credentials
OpenBao AppRole - Voice GatewayVoice Gateway service credentials
GCP Service Account - ravenmaskGCP credentials for auto-unseal

References