Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HashiCorp Vault Integration

Integrate Horizon Epoch with HashiCorp Vault for secure credential management.

Overview

Vault integration supports:

  • Static secrets (KV v2)
  • Dynamic database credentials
  • PKI certificates for mTLS
  • Multiple authentication methods

Prerequisites

  • HashiCorp Vault server (1.x or later)
  • Vault CLI (for setup)
  • Network access from Horizon Epoch to Vault

Authentication Methods

Token Authentication

Simplest method, suitable for development:

from horizon_epoch import Config

config = Config(
    vault_addr="https://vault.example.com:8200",
    vault_token="hvs.xxx..."
)
export VAULT_ADDR="https://vault.example.com:8200"
export VAULT_TOKEN="hvs.xxx..."

AppRole Authentication

Recommended for applications:

config = Config(
    vault_addr="https://vault.example.com:8200",
    vault_auth_method="approle",
    vault_role_id="xxx-xxx-xxx",
    vault_secret_id="${VAULT_SECRET_ID}"  # From env or file
)

Setup in Vault:

# Enable AppRole
vault auth enable approle

# Create policy
vault policy write horizon-epoch - <<EOF
path "secret/data/horizon-epoch/*" {
  capabilities = ["read"]
}
path "database/creds/horizon-epoch-*" {
  capabilities = ["read"]
}
EOF

# Create role
vault write auth/approle/role/horizon-epoch \
    token_policies="horizon-epoch" \
    token_ttl=1h \
    token_max_ttl=4h

Kubernetes Authentication

For Kubernetes deployments:

config = Config(
    vault_addr="https://vault.example.com:8200",
    vault_auth_method="kubernetes",
    vault_role="horizon-epoch"
)

AWS IAM Authentication

For AWS environments:

config = Config(
    vault_addr="https://vault.example.com:8200",
    vault_auth_method="aws",
    vault_role="horizon-epoch-ec2"
)

KV Secrets (Static)

Store database credentials in KV v2:

# Store secret
vault kv put secret/horizon-epoch/postgres \
    username=epoch_user \
    password=secret123
config.add_postgres(
    name="mydb",
    host="db.example.com",
    database="production",
    vault_path="secret/data/horizon-epoch/postgres"
)

Dynamic Database Credentials

Vault can generate short-lived database credentials:

# Configure database secrets engine
vault secrets enable database

vault write database/config/postgres \
    plugin_name=postgresql-database-plugin \
    allowed_roles="horizon-epoch-*" \
    connection_url="postgresql://{{username}}:{{password}}@db:5432/production" \
    username="vault" \
    password="vault-password"

vault write database/roles/horizon-epoch-readonly \
    db_name=postgres \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"
config.add_postgres(
    name="mydb",
    host="db.example.com",
    database="production",
    vault_role="database/creds/horizon-epoch-readonly"
)

PKI Certificates

For mTLS authentication:

# Enable PKI
vault secrets enable pki

# Configure PKI (simplified)
vault write pki/root/generate/internal \
    common_name="Horizon Epoch CA"

vault write pki/roles/horizon-epoch-client \
    allowed_domains="epoch.internal" \
    allow_subdomains=true \
    max_ttl="720h"
config.add_postgres(
    name="mydb",
    host="db.example.com",
    vault_pki_role="pki/issue/horizon-epoch-client",
    sslmode="verify-full"
)

Configuration File

# epoch.toml
[vault]
addr = "https://vault.example.com:8200"
auth_method = "approle"
role_id = "xxx-xxx-xxx"
secret_id_file = "/run/secrets/vault-secret-id"

[storage.postgres.production]
host = "db.example.com"
database = "production"
vault_path = "secret/data/horizon-epoch/postgres"

[storage.postgres.readonly]
host = "db.example.com"
database = "production"
vault_role = "database/creds/horizon-epoch-readonly"

Troubleshooting

Permission Denied

Error: permission denied on path "secret/data/..."
  • Check policy is attached to token/role
  • Verify path is correct (KV v2 uses secret/data/ prefix)
  • Check Vault audit log for details

Token Expired

Error: Vault token expired
  • Token TTL may be too short
  • Enable token renewal in config
  • Use AppRole for automatic re-authentication

Connection Failed

Error: failed to connect to Vault
  • Verify VAULT_ADDR is correct
  • Check network connectivity
  • Verify TLS certificates if using HTTPS

Certificate Issues

Error: x509: certificate signed by unknown authority
config = Config(
    vault_addr="https://vault.example.com:8200",
    vault_ca_cert="/path/to/ca.crt"
)

Best Practices

  1. Use AppRole or Kubernetes auth in production
  2. Enable audit logging in Vault
  3. Use dynamic credentials when possible
  4. Set appropriate TTLs - short enough for security, long enough for operations
  5. Rotate static secrets regularly

Next Steps