docs(#354): Add comprehensive OpenBao integration guide
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Complete documentation for OpenBao Transit encryption covering setup, architecture, production hardening, and operations. Sections: - Overview: Why OpenBao, Transit encryption explained - Architecture: Data flow diagrams, fallback behavior - Default Setup: Turnkey auto-init/unseal, file locations - Environment Variables: Configuration options - Transit Keys: Named keys, rotation procedures - Production Hardening: 10-point security checklist - Operations: Health checks, manual procedures, monitoring - Troubleshooting: Common issues and solutions - Disaster Recovery: Backup/restore procedures Key Topics: - Shamir key splitting upgrade (1-of-1 → 3-of-5) - TLS configuration for production - Audit logging enablement - HA storage backends (Raft/Consul) - External auto-unseal with KMS - Rate limiting via reverse proxy - Network isolation best practices - Key rotation procedures - Backup automation Closes #354 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
795
docs/OPENBAO.md
Normal file
795
docs/OPENBAO.md
Normal file
@@ -0,0 +1,795 @@
|
||||
# OpenBao Integration Guide
|
||||
|
||||
**Version:** 0.0.9
|
||||
**Status:** Production Ready
|
||||
**Related Issues:** [#346](https://git.mosaicstack.dev/mosaic/stack/issues/346), [#357](https://git.mosaicstack.dev/mosaic/stack/issues/357), [#353](https://git.mosaicstack.dev/mosaic/stack/issues/353)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Architecture](#architecture)
|
||||
3. [Default Turnkey Setup](#default-turnkey-setup)
|
||||
4. [Environment Variables](#environment-variables)
|
||||
5. [Transit Encryption Keys](#transit-encryption-keys)
|
||||
6. [Production Hardening](#production-hardening)
|
||||
7. [Operations](#operations)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
9. [Disaster Recovery](#disaster-recovery)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
### Why OpenBao?
|
||||
|
||||
OpenBao is an open-source secrets management platform forked from HashiCorp Vault after HashiCorp changed to the Business Source License. Key benefits:
|
||||
|
||||
- **Truly open-source** - Linux Foundation project with OSI-approved license
|
||||
- **Drop-in Vault replacement** - API-compatible with HashiCorp Vault
|
||||
- **Production-ready** - v2.0+ with active development and community support
|
||||
- **Transit encryption** - Encrypt data at rest without storing plaintext in OpenBao
|
||||
|
||||
### What is Transit Encryption?
|
||||
|
||||
The Transit secrets engine provides "encryption as a service":
|
||||
|
||||
- **Encryption/decryption operations** via API calls
|
||||
- **Key versioning** - Rotate keys without re-encrypting existing data
|
||||
- **No plaintext storage** - OpenBao never stores your plaintext data
|
||||
- **Ciphertext format** - Versioned format (`vault:v1:...`) enables seamless key rotation
|
||||
|
||||
**Use Case**: Encrypt sensitive data (OAuth tokens, API keys, credentials) before storing in PostgreSQL. If the database is compromised, attackers only get encrypted ciphertext.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Mosaic Stack API │
|
||||
│ │
|
||||
│ ┌────────────────┐ ┌──────────────────┐ │
|
||||
│ │ VaultService │───────>│ CryptoService │ │
|
||||
│ │ (Primary) │ │ (Fallback) │ │
|
||||
│ └────────┬───────┘ └──────────────────┘ │
|
||||
│ │ │
|
||||
│ │ Transit API │
|
||||
│ │ (encrypt/decrypt) │
|
||||
└───────────┼─────────────────────────────────────────────────┘
|
||||
│
|
||||
│ HTTP (localhost:8200)
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────┐
|
||||
│ OpenBao │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ Transit Secrets Engine │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │ mosaic- │ │ mosaic- │ + 2 more │ │
|
||||
│ │ │ credentials │ │ account- │ keys │ │
|
||||
│ │ │ │ │ tokens │ │ │
|
||||
│ │ └──────────────┘ └──────────────┘ │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ AppRole Authentication │ │
|
||||
│ │ - Role: mosaic-transit │ │
|
||||
│ │ - Policy: Transit encrypt/decrypt only │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ File Storage Backend │ │
|
||||
│ │ /openbao/data (Docker volume) │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Auto-init / Auto-unseal
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────┐
|
||||
│ OpenBao Init Sidecar │
|
||||
│ - Initializes OpenBao on first run │
|
||||
│ - Auto-unseals on container restart │
|
||||
│ - Creates Transit keys and AppRole │
|
||||
│ - Runs continuously with 30s check loop │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Data Flow: Encrypt
|
||||
|
||||
1. API receives plaintext credential (e.g., OAuth access token)
|
||||
2. VaultService encrypts via Transit: `POST /v1/transit/encrypt/mosaic-account-tokens`
|
||||
3. OpenBao returns ciphertext: `vault:v1:8SDd3WHDOjf8Fz5MSLXjL...`
|
||||
4. Ciphertext stored in PostgreSQL
|
||||
5. Plaintext never touches disk
|
||||
|
||||
### Data Flow: Decrypt
|
||||
|
||||
1. API reads ciphertext from PostgreSQL
|
||||
2. VaultService decrypts via Transit: `POST /v1/transit/decrypt/mosaic-account-tokens`
|
||||
3. OpenBao returns plaintext
|
||||
4. API uses plaintext for OAuth/API requests
|
||||
5. Plaintext cleared from memory after use
|
||||
|
||||
### Fallback Behavior
|
||||
|
||||
When OpenBao is unavailable:
|
||||
|
||||
- **Encryption**: VaultService falls back to AES-256-GCM (`CryptoService`)
|
||||
- **Decryption**: Auto-detects format (`vault:v1:` vs AES) and uses appropriate service
|
||||
- **Logging**: ERROR-level logs for visibility into infrastructure issues
|
||||
- **Backward Compatibility**: Existing AES-encrypted data remains decryptable
|
||||
|
||||
**Production Recommendation**: Set `OPENBAO_REQUIRED=true` to fail startup if OpenBao is unavailable (prevents accidental fallback).
|
||||
|
||||
---
|
||||
|
||||
## Default Turnkey Setup
|
||||
|
||||
### What Happens on First `docker compose up`
|
||||
|
||||
The `openbao-init` sidecar automatically:
|
||||
|
||||
1. **Waits** for OpenBao server to be healthy
|
||||
2. **Initializes** OpenBao with 1-of-1 Shamir key split (development mode)
|
||||
3. **Unseals** OpenBao using the stored unseal key
|
||||
4. **Enables** Transit secrets engine at `/transit`
|
||||
5. **Creates** 4 named encryption keys:
|
||||
- `mosaic-credentials` - User-provided credentials (API keys, tokens)
|
||||
- `mosaic-account-tokens` - OAuth tokens from BetterAuth accounts
|
||||
- `mosaic-federation` - Federation private keys
|
||||
- `mosaic-llm-config` - LLM provider API keys
|
||||
6. **Creates** AppRole `mosaic-transit` with Transit-only policy
|
||||
7. **Generates** AppRole credentials (role_id, secret_id)
|
||||
8. **Saves** credentials to `/openbao/init/approle-credentials`
|
||||
9. **Watches** continuously - auto-unseals on container restart every 30s
|
||||
|
||||
### File Locations
|
||||
|
||||
```
|
||||
Docker Volumes:
|
||||
├── mosaic-openbao-data # OpenBao database (encrypted storage)
|
||||
├── mosaic-openbao-init # Unseal key, root token, AppRole credentials
|
||||
└── docker/openbao/config.hcl # Server configuration (bind mount)
|
||||
```
|
||||
|
||||
### Credentials Storage
|
||||
|
||||
**Unseal Key**: `/openbao/init/unseal-key` (plaintext, volume-only access)
|
||||
**Root Token**: `/openbao/init/root-token` (plaintext, volume-only access)
|
||||
**AppRole Credentials**: `/openbao/init/approle-credentials` (JSON, read by API)
|
||||
|
||||
Example `/openbao/init/approle-credentials`:
|
||||
|
||||
```json
|
||||
{
|
||||
"role_id": "42474304-cb07-edff-f5c8-cae494ba6a51",
|
||||
"secret_id": "39ccdb6b-4570-a279-022c-36220739ebcf"
|
||||
}
|
||||
```
|
||||
|
||||
**Security Note**: These files are only accessible within the Docker internal network. The OpenBao API is bound to `127.0.0.1:8200` (localhost only).
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Required
|
||||
|
||||
None - the turnkey setup works with defaults.
|
||||
|
||||
### Optional
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
| ------------------- | --------------------- | -------------------------------------------- |
|
||||
| `OPENBAO_ADDR` | `http://openbao:8200` | OpenBao API address (Docker internal) |
|
||||
| `OPENBAO_PORT` | `8200` | Port for localhost binding in docker-compose |
|
||||
| `OPENBAO_ROLE_ID` | (read from file) | Override AppRole role_id |
|
||||
| `OPENBAO_SECRET_ID` | (read from file) | Override AppRole secret_id |
|
||||
| `OPENBAO_REQUIRED` | `false` | Fail startup if OpenBao unavailable |
|
||||
|
||||
### Production Configuration
|
||||
|
||||
**Development**:
|
||||
|
||||
```bash
|
||||
OPENBAO_ADDR=http://openbao:8200
|
||||
OPENBAO_REQUIRED=false
|
||||
```
|
||||
|
||||
**Production**:
|
||||
|
||||
```bash
|
||||
OPENBAO_ADDR=https://vault.internal.corp:8200
|
||||
OPENBAO_REQUIRED=true
|
||||
# AppRole credentials provided by external Vault deployment
|
||||
OPENBAO_ROLE_ID=<from-external-vault>
|
||||
OPENBAO_SECRET_ID=<from-external-vault>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transit Encryption Keys
|
||||
|
||||
### Named Keys
|
||||
|
||||
| Key Name | Purpose | Used By | Example Data |
|
||||
| ----------------------- | ------------------------- | ------------------------- | ------------------------------------- |
|
||||
| `mosaic-credentials` | User-provided credentials | UserCredential model | GitHub PAT, AWS access keys |
|
||||
| `mosaic-account-tokens` | OAuth tokens | BetterAuth accounts table | access_token, refresh_token, id_token |
|
||||
| `mosaic-federation` | Federation private keys | FederatedInstance model | Ed25519 private keys |
|
||||
| `mosaic-llm-config` | LLM provider credentials | LLMProviderInstance model | OpenAI API key, Anthropic API key |
|
||||
|
||||
### Key Properties
|
||||
|
||||
- **Algorithm**: AES-256-GCM96
|
||||
- **Versioning**: Enabled (transparent key rotation)
|
||||
- **Deletion**: Not allowed (exportable = false)
|
||||
- **Min Decryption Version**: 1 (all versions can decrypt)
|
||||
- **Latest Version**: Auto-increments on rotation
|
||||
|
||||
### Key Rotation
|
||||
|
||||
OpenBao Transit keys support **transparent rotation**:
|
||||
|
||||
1. Rotate key: `bao write -f transit/keys/mosaic-credentials/rotate`
|
||||
2. New version created (e.g., v2)
|
||||
3. **New encryptions** use v2
|
||||
4. **Old ciphertexts** still decrypt with v1
|
||||
5. **No data re-encryption needed**
|
||||
|
||||
**Recommendation**: Rotate keys annually or after suspected compromise.
|
||||
|
||||
### Ciphertext Format
|
||||
|
||||
```
|
||||
vault:v1:8SDd3WHDOjf8Fz5MSLXjLx0AzTdYhLMAAp4W
|
||||
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
│ └─ Base64-encoded ciphertext
|
||||
└───── Key version
|
||||
```
|
||||
|
||||
**Version prefix** enables seamless rotation - OpenBao knows which key version to use for decryption.
|
||||
|
||||
---
|
||||
|
||||
## Production Hardening
|
||||
|
||||
### Security Checklist
|
||||
|
||||
**CRITICAL - Do not deploy to production without these:**
|
||||
|
||||
| Task | Command | Priority |
|
||||
| ------------------------------------ | ----------------------------------------------------- | -------- |
|
||||
| **1. Upgrade Shamir key splitting** | `bao operator rekey -key-shares=5 -key-threshold=3` | P0 |
|
||||
| **2. Enable TLS on listener** | Update `config.hcl`: `tls_disable = 0` | P0 |
|
||||
| **3. Revoke root token** | `bao token revoke -self` | P0 |
|
||||
| **4. Enable audit logging** | `bao audit enable file file_path=/bao/logs/audit.log` | P0 |
|
||||
| **5. Use external auto-unseal** | AWS KMS, GCP CKMS, Azure Key Vault | P1 |
|
||||
| **6. HA storage backend** | Raft or Consul | P1 |
|
||||
| **7. Bind to internal network only** | Update docker-compose.yml: remove port exposure | P1 |
|
||||
| **8. Rate limiting** | Use reverse proxy (nginx/Traefik) with rate limits | P2 |
|
||||
| **9. Rotate AppRole credentials** | Regenerate secret_id monthly | P2 |
|
||||
| **10. Backup automation** | Snapshot Raft or file storage daily | P2 |
|
||||
|
||||
### 1. Shamir Key Splitting
|
||||
|
||||
**Current (Development)**: 1-of-1 key shares (single unseal key)
|
||||
**Production**: 3-of-5 key shares (requires 3 of 5 keys to unseal)
|
||||
|
||||
**Procedure**:
|
||||
|
||||
```bash
|
||||
# SSH into OpenBao container
|
||||
docker compose exec openbao sh
|
||||
|
||||
# Set environment
|
||||
export VAULT_ADDR=http://localhost:8200
|
||||
export VAULT_TOKEN=$(cat /openbao/init/root-token)
|
||||
|
||||
# Rekey to 3-of-5
|
||||
bao operator rekey -init -key-shares=5 -key-threshold=3
|
||||
|
||||
# Follow prompts to generate new keys
|
||||
# Distribute 5 keys to different key holders
|
||||
# Require 3 keys to unseal
|
||||
```
|
||||
|
||||
**Key Management Best Practices**:
|
||||
|
||||
- Store each key with a different person/system
|
||||
- Use hardware security modules (HSMs) for key storage
|
||||
- Document key recovery procedures
|
||||
- Test unseal process regularly
|
||||
|
||||
### 2. TLS Configuration
|
||||
|
||||
**Update `docker/openbao/config.hcl`**:
|
||||
|
||||
```hcl
|
||||
listener "tcp" {
|
||||
address = "0.0.0.0:8200"
|
||||
tls_disable = 0 # Changed from 1
|
||||
tls_cert_file = "/openbao/config/server.crt"
|
||||
tls_key_file = "/openbao/config/server.key"
|
||||
tls_min_version = "tls12"
|
||||
}
|
||||
```
|
||||
|
||||
**Generate TLS certificates**:
|
||||
|
||||
```bash
|
||||
# Production: Use certificates from your CA
|
||||
# Development: Generate self-signed
|
||||
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \
|
||||
-days 365 -nodes -subj "/CN=openbao.internal"
|
||||
```
|
||||
|
||||
**Update API configuration**:
|
||||
|
||||
```bash
|
||||
OPENBAO_ADDR=https://openbao.internal:8200
|
||||
```
|
||||
|
||||
### 3. Revoke Root Token
|
||||
|
||||
After initial setup, the root token should be revoked:
|
||||
|
||||
```bash
|
||||
export VAULT_TOKEN=$(cat /openbao/init/root-token)
|
||||
bao token revoke -self
|
||||
|
||||
# Remove root token file
|
||||
rm /openbao/init/root-token
|
||||
```
|
||||
|
||||
**Recovery**: Use the Shamir keys to generate a new root token if needed:
|
||||
|
||||
```bash
|
||||
bao operator generate-root -init
|
||||
```
|
||||
|
||||
### 4. Audit Logging
|
||||
|
||||
Enable audit logging to track all API activity:
|
||||
|
||||
```bash
|
||||
bao audit enable file file_path=/openbao/logs/audit.log
|
||||
|
||||
# Verify
|
||||
bao audit list
|
||||
```
|
||||
|
||||
**Mount logs volume in docker-compose.yml**:
|
||||
|
||||
```yaml
|
||||
openbao:
|
||||
volumes:
|
||||
- openbao_logs:/openbao/logs
|
||||
```
|
||||
|
||||
**Log Format**: JSON, one entry per API request
|
||||
**Retention**: Rotate daily, retain 90 days minimum
|
||||
|
||||
### 5. External Auto-Unseal
|
||||
|
||||
Replace file-based unseal with KMS-based auto-unseal:
|
||||
|
||||
**AWS KMS Example**:
|
||||
|
||||
```hcl
|
||||
# config.hcl
|
||||
seal "awskms" {
|
||||
region = "us-east-1"
|
||||
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/abc123"
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
|
||||
- No manual unseal required
|
||||
- Unseal key never touches disk
|
||||
- Audit trail in KMS service
|
||||
- Automatic rotation support
|
||||
|
||||
### 6. High Availability
|
||||
|
||||
**File Storage (Current)** - Single node, no HA
|
||||
**Raft Storage (Recommended)** - Multi-node with leader election
|
||||
|
||||
**Raft configuration**:
|
||||
|
||||
```hcl
|
||||
storage "raft" {
|
||||
path = "/openbao/data"
|
||||
node_id = "node1"
|
||||
}
|
||||
```
|
||||
|
||||
**Run 3-5 OpenBao nodes** with Raft for production HA.
|
||||
|
||||
### 7. Network Isolation
|
||||
|
||||
**Current**: Port bound to `127.0.0.1:8200` (localhost only)
|
||||
**Production**: Use internal network only, no public exposure
|
||||
|
||||
**docker-compose.yml**:
|
||||
|
||||
```yaml
|
||||
openbao:
|
||||
# Remove port mapping
|
||||
# ports:
|
||||
# - "127.0.0.1:8200:8200"
|
||||
networks:
|
||||
- internal
|
||||
```
|
||||
|
||||
**API access**: Use reverse proxy with authentication/authorization.
|
||||
|
||||
### 8. Rate Limiting
|
||||
|
||||
Prevent brute-force attacks on AppRole authentication:
|
||||
|
||||
**nginx reverse proxy**:
|
||||
|
||||
```nginx
|
||||
http {
|
||||
limit_req_zone $binary_remote_addr zone=vault_auth:10m rate=10r/s;
|
||||
|
||||
server {
|
||||
listen 8200 ssl;
|
||||
|
||||
location /v1/auth/approle/login {
|
||||
limit_req zone=vault_auth burst=20 nodelay;
|
||||
proxy_pass http://openbao:8200;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://openbao:8200;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operations
|
||||
|
||||
### Health Checks
|
||||
|
||||
**OpenBao server**:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8200/v1/sys/health
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"initialized": true,
|
||||
"sealed": false,
|
||||
"standby": false,
|
||||
"server_time_utc": 1738938524
|
||||
}
|
||||
```
|
||||
|
||||
**VaultService health** (API endpoint):
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/health
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"info": {
|
||||
"openbao": {
|
||||
"status": "up"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Manual Unseal
|
||||
|
||||
If auto-unseal fails:
|
||||
|
||||
```bash
|
||||
# Get unseal key
|
||||
docker run --rm -v mosaic-openbao-init:/data alpine cat /data/unseal-key
|
||||
|
||||
# Unseal OpenBao
|
||||
docker compose exec openbao bao operator unseal <key>
|
||||
```
|
||||
|
||||
### Check Transit Keys
|
||||
|
||||
```bash
|
||||
# Get root token
|
||||
export VAULT_TOKEN=$(docker run --rm -v mosaic-openbao-init:/data alpine cat /data/root-token)
|
||||
|
||||
# List Transit keys
|
||||
docker compose exec -e VAULT_TOKEN openbao bao list transit/keys
|
||||
|
||||
# Read key details
|
||||
docker compose exec -e VAULT_TOKEN openbao bao read transit/keys/mosaic-credentials
|
||||
```
|
||||
|
||||
### Test Encryption
|
||||
|
||||
```bash
|
||||
# Encrypt test data
|
||||
PLAINTEXT=$(echo -n "test-secret" | base64)
|
||||
docker compose exec -e VAULT_TOKEN openbao bao write -format=json \
|
||||
transit/encrypt/mosaic-credentials plaintext=$PLAINTEXT
|
||||
|
||||
# Decrypt
|
||||
docker compose exec -e VAULT_TOKEN openbao bao write -format=json \
|
||||
transit/decrypt/mosaic-credentials ciphertext=<ciphertext>
|
||||
```
|
||||
|
||||
### Monitor Logs
|
||||
|
||||
```bash
|
||||
# OpenBao server logs
|
||||
docker compose logs -f openbao
|
||||
|
||||
# Init sidecar logs
|
||||
docker compose logs -f openbao-init
|
||||
|
||||
# VaultService logs (API)
|
||||
docker compose logs -f api | grep VaultService
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### OpenBao Won't Start
|
||||
|
||||
**Symptoms**: Container restarts repeatedly, "connection refused" errors
|
||||
|
||||
**Diagnosis**:
|
||||
|
||||
```bash
|
||||
docker compose logs openbao
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
|
||||
1. **Port conflict**: Another service on 8200
|
||||
2. **Volume permission issues**: Can't write to `/openbao/data`
|
||||
3. **Invalid config**: Syntax error in `config.hcl`
|
||||
|
||||
**Solutions**:
|
||||
|
||||
```bash
|
||||
# Check port usage
|
||||
netstat -tuln | grep 8200
|
||||
|
||||
# Fix volume permissions
|
||||
docker compose down -v
|
||||
docker volume rm mosaic-openbao-data
|
||||
docker compose up -d
|
||||
|
||||
# Validate config
|
||||
docker compose exec openbao bao server -config=/openbao/config/config.hcl -test
|
||||
```
|
||||
|
||||
### OpenBao Sealed
|
||||
|
||||
**Symptoms**: API returns 503, `"sealed": true` in health check
|
||||
|
||||
**Diagnosis**:
|
||||
|
||||
```bash
|
||||
docker compose exec openbao bao status
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
# Auto-unseal should handle this
|
||||
# Force restart init container
|
||||
docker compose restart openbao-init
|
||||
|
||||
# Manual unseal if needed
|
||||
docker compose exec openbao bao operator unseal <key>
|
||||
```
|
||||
|
||||
### VaultService Falls Back to AES
|
||||
|
||||
**Symptoms**: Logs show "OpenBao unavailable, falling back to AES-256-GCM"
|
||||
|
||||
**Diagnosis**:
|
||||
|
||||
```bash
|
||||
# Check OpenBao health
|
||||
curl http://localhost:8200/v1/sys/health
|
||||
|
||||
# Check AppRole credentials
|
||||
docker run --rm -v mosaic-openbao-init:/data alpine cat /data/approle-credentials
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
|
||||
1. **OpenBao not running**: `docker compose ps openbao`
|
||||
2. **Wrong OPENBAO_ADDR**: Check environment variable
|
||||
3. **AppRole credentials missing**: Reinitialize with `docker compose restart openbao-init`
|
||||
4. **Network issue**: Check Docker network connectivity
|
||||
|
||||
**Solutions**:
|
||||
|
||||
```bash
|
||||
# Restart OpenBao stack
|
||||
docker compose restart openbao openbao-init
|
||||
|
||||
# Verify connectivity from API container
|
||||
docker compose exec api curl http://openbao:8200/v1/sys/health
|
||||
```
|
||||
|
||||
### Authentication Failures
|
||||
|
||||
**Symptoms**: "Token renewal failed", "Authentication failed"
|
||||
|
||||
**Diagnosis**:
|
||||
|
||||
```bash
|
||||
# Check AppRole
|
||||
export VAULT_TOKEN=$(docker run --rm -v mosaic-openbao-init:/data alpine cat /data/root-token)
|
||||
docker compose exec -e VAULT_TOKEN openbao bao read auth/approle/role/mosaic-transit
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
```bash
|
||||
# Regenerate AppRole credentials
|
||||
docker compose exec -e VAULT_TOKEN openbao bao write -format=json \
|
||||
-f auth/approle/role/mosaic-transit/secret-id > new-credentials.json
|
||||
|
||||
# Update credentials file
|
||||
docker run --rm -v mosaic-openbao-init:/data -v $(pwd):/host alpine \
|
||||
cp /host/new-credentials.json /data/approle-credentials
|
||||
|
||||
# Restart API
|
||||
docker compose restart api
|
||||
```
|
||||
|
||||
### Decrypt Failures
|
||||
|
||||
**Symptoms**: "Failed to decrypt data", encrypted tokens visible in database
|
||||
|
||||
**Diagnosis**:
|
||||
|
||||
```bash
|
||||
# Check ciphertext format
|
||||
psql -h localhost -U mosaic -d mosaic \
|
||||
-c "SELECT access_token FROM accounts LIMIT 1;"
|
||||
```
|
||||
|
||||
**Ciphertext formats**:
|
||||
|
||||
- `vault:v1:...` - Transit encryption (requires OpenBao)
|
||||
- `iv:tag:encrypted` - AES fallback (works without OpenBao)
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Transit ciphertext + OpenBao unavailable**: Start OpenBao
|
||||
2. **Corrupted ciphertext**: Check database integrity
|
||||
3. **Wrong encryption key**: Verify `ENCRYPTION_KEY` hasn't changed (for AES)
|
||||
|
||||
---
|
||||
|
||||
## Disaster Recovery
|
||||
|
||||
### Backup
|
||||
|
||||
**Critical Data**:
|
||||
|
||||
1. **Unseal key**: `/openbao/init/unseal-key` (Shamir keys in production)
|
||||
2. **Root token**: `/openbao/init/root-token` (revoke in production, document recovery)
|
||||
3. **AppRole credentials**: `/openbao/init/approle-credentials`
|
||||
4. **OpenBao data**: `/openbao/data` (encrypted storage)
|
||||
|
||||
**Backup Procedure**:
|
||||
|
||||
```bash
|
||||
# Backup volumes
|
||||
docker run --rm -v mosaic-openbao-init:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/openbao-init-$(date +%Y%m%d).tar.gz /data
|
||||
|
||||
docker run --rm -v mosaic-openbao-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/openbao-data-$(date +%Y%m%d).tar.gz /data
|
||||
|
||||
# Backup to remote storage
|
||||
aws s3 cp openbao-*.tar.gz s3://backups/openbao/
|
||||
```
|
||||
|
||||
**Schedule**: Daily automated backups with 30-day retention.
|
||||
|
||||
### Restore
|
||||
|
||||
**Full Recovery**:
|
||||
|
||||
```bash
|
||||
# Download backups
|
||||
aws s3 cp s3://backups/openbao/openbao-init-20260207.tar.gz .
|
||||
aws s3 cp s3://backups/openbao/openbao-data-20260207.tar.gz .
|
||||
|
||||
# Stop containers
|
||||
docker compose down
|
||||
|
||||
# Restore volumes
|
||||
docker volume create mosaic-openbao-init
|
||||
docker volume create mosaic-openbao-data
|
||||
|
||||
docker run --rm -v mosaic-openbao-init:/data -v $(pwd):/backup \
|
||||
alpine tar xzf /backup/openbao-init-20260207.tar.gz -C /data --strip-components=1
|
||||
|
||||
docker run --rm -v mosaic-openbao-data:/data -v $(pwd):/backup \
|
||||
alpine tar xzf /backup/openbao-data-20260207.tar.gz -C /data --strip-components=1
|
||||
|
||||
# Restart
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Key Recovery
|
||||
|
||||
**Lost Unseal Key**:
|
||||
|
||||
- **Development (1-of-1)**: Restore from backup
|
||||
- **Production (3-of-5)**: Collect 3 of 5 Shamir keys from key holders
|
||||
|
||||
**Lost Root Token**:
|
||||
|
||||
```bash
|
||||
# Generate new root token using Shamir keys
|
||||
bao operator generate-root -init
|
||||
bao operator generate-root -nonce=<nonce> <key1>
|
||||
bao operator generate-root -nonce=<nonce> <key2>
|
||||
bao operator generate-root -nonce=<nonce> <key3>
|
||||
```
|
||||
|
||||
**Lost AppRole Credentials**:
|
||||
|
||||
```bash
|
||||
# Use root token to regenerate
|
||||
export VAULT_TOKEN=<root-token>
|
||||
bao write -format=json -f auth/approle/role/mosaic-transit/secret-id \
|
||||
> new-approle-credentials.json
|
||||
```
|
||||
|
||||
### Testing Recovery
|
||||
|
||||
**Quarterly DR test**:
|
||||
|
||||
1. Backup production OpenBao
|
||||
2. Restore to test environment
|
||||
3. Verify all Transit keys accessible
|
||||
4. Test encrypt/decrypt operations
|
||||
5. Document any issues
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **OpenBao Documentation**: https://openbao.org/docs/
|
||||
- **Transit Secrets Engine**: https://openbao.org/docs/secrets/transit/
|
||||
- **AppRole Auth**: https://openbao.org/docs/auth/approle/
|
||||
- **Production Hardening**: https://openbao.org/docs/guides/production/
|
||||
- **Design Document**: `docs/design/credential-security.md`
|
||||
|
||||
## Support
|
||||
|
||||
For issues with Mosaic Stack's OpenBao integration:
|
||||
|
||||
- **Repository**: https://git.mosaicstack.dev/mosaic/stack
|
||||
- **Issues**: https://git.mosaicstack.dev/mosaic/stack/issues
|
||||
- **Milestone**: M9-CredentialSecurity
|
||||
|
||||
For OpenBao itself:
|
||||
|
||||
- **GitHub**: https://github.com/openbao/openbao
|
||||
- **Discussions**: https://github.com/openbao/openbao/discussions
|
||||
Reference in New Issue
Block a user