🏦 HashiCorp Vault Integration¶
🎯 Por Qué Vault¶
Limitaciones de .env Files¶
┌────────────────────────────────────────────────────────────┐ │ .env files: │ │ ✓ Simple and quick for local development │ │ ✗ No centralized management │ │ ✗ No audit trail │ │ ✗ Manual rotation process │ │ ✗ Difficult to share securely across team │ │ ✗ No access control │ └────────────────────────────────────────────────────────────┘
Ventajas de Vault¶
┌────────────────────────────────────────────────────────────┐ │ HashiCorp Vault: │ │ ✓ Centralized secret storage │ │ ✓ Full audit trail (who accessed what, when) │ │ ✓ Automatic secret rotation │ │ ✓ Fine-grained access control (ACLs) │ │ ✓ Encryption at rest and in transit │ │ ✓ Dynamic secrets (time-limited credentials) │ │ ✓ Secret versioning and rollback │ └────────────────────────────────────────────────────────────┘
📋 Installation & Setup¶
1. Install Vault¶
Windows¶
# Using Chocolatey
choco install vault
# Or download binary from
# https://www.vaultproject.io/downloads
Linux/macOS¶
# Using package manager
# Ubuntu/Debian
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install vault
# macOS
brew install vault
2. Configure Vault Server¶
Create configuration file vault_config.hcl:
# Storage backend
storage "file" {
path = "/vault/data"
}
# HTTP listener
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = 1 # For local dev only! Use TLS in production
}
# API and UI
ui = true
api_addr = "http://127.0.0.1:8200"
# Log level
log_level = "Info"
# Disable mlock for dev (not recommended for production)
disable_mlock = true
Production configuration with TLS:
storage "consul" {
address = "127.0.0.1:8500"
path = "vault/"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/vault/tls/vault.crt"
tls_key_file = "/etc/vault/tls/vault.key"
}
ui = true
api_addr = "https://vault.audiolab.com:8200"
3. Initialize Vault¶
# Start Vault server
vault server -config=vault_config.hcl
# In another terminal, set Vault address
export VAULT_ADDR='http://127.0.0.1:8200'
# Initialize Vault (returns unseal keys and root token)
vault operator init
# Output will be like:
# Unseal Key 1: <key1>
# Unseal Key 2: <key2>
# ...
# Initial Root Token: <root_token>
# IMPORTANT: Save these keys securely! You'll need them.
# Unseal Vault (requires 3 out of 5 keys by default)
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>
# Login with root token
vault login <root_token>
🔑 Basic Usage¶
Enable KV Secrets Engine¶
# Enable KV version 2 (supports versioning)
vault secrets enable -version=2 -path=secret kv
# Verify
vault secrets list
Store Secrets¶
# Store a single secret
vault kv put secret/audiolab/ci \
windows_cert_password="SecurePassword123"
# Store multiple secrets at once
vault kv put secret/audiolab/ci \
windows_cert_password="SecurePassword123" \
apple_id="dev@audiolab.com" \
apple_app_password="xxxx-xxxx-xxxx-xxxx" \
api_key="ak_live_123456789"
# Store from file
cat secrets.json | vault kv put secret/audiolab/ci -
Retrieve Secrets¶
# Get all secrets at a path
vault kv get secret/audiolab/ci
# Get specific field
vault kv get -field=windows_cert_password secret/audiolab/ci
# Get as JSON
vault kv get -format=json secret/audiolab/ci
# Use in CI/CD
export PASSWORD=$(vault kv get -field=windows_cert_password secret/audiolab/ci)
Update Secrets¶
# Update creates a new version (old version preserved)
vault kv put secret/audiolab/ci \
windows_cert_password="NewPassword456"
# Patch (update only specific fields)
vault kv patch secret/audiolab/ci \
api_key="ak_live_987654321"
Secret Versioning¶
# Get specific version
vault kv get -version=2 secret/audiolab/ci
# List versions
vault kv metadata get secret/audiolab/ci
# Rollback to previous version
vault kv rollback -version=1 secret/audiolab/ci
# Delete specific version
vault kv delete -versions=2 secret/audiolab/ci
# Undelete
vault kv undelete -versions=2 secret/audiolab/ci
# Destroy permanently (cannot be recovered)
vault kv destroy -versions=2 secret/audiolab/ci
🔐 Access Control¶
Create Policies¶
Create audiolab-ci-policy.hcl:
# Allow read access to CI secrets
path "secret/data/audiolab/ci/*" {
capabilities = ["read", "list"]
}
# Allow read/write to dev secrets
path "secret/data/audiolab/dev/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Deny access to production secrets
path "secret/data/audiolab/prod/*" {
capabilities = ["deny"]
}
Apply policy:
vault policy write audiolab-ci audiolab-ci-policy.hcl
# List policies
vault policy list
# Read policy
vault policy read audiolab-ci
Create Tokens with Policies¶
# Create token with specific policy
vault token create -policy=audiolab-ci
# Create token with TTL (time to live)
vault token create -policy=audiolab-ci -ttl=1h
# Create renewable token
vault token create -policy=audiolab-ci -ttl=1h -renewable
# Create token for CI/CD (no TTL, but orphan)
vault token create -policy=audiolab-ci -orphan -display-name="github-actions"
🔄 Automatic Secret Rotation¶
Database Credentials Rotation¶
# Enable database secrets engine
vault secrets enable database
# Configure PostgreSQL connection
vault write database/config/audiolab-db \
plugin_name=postgresql-database-plugin \
allowed_roles="audiolab-role" \
connection_url="postgresql://{{username}}:{{password}}@localhost:5432/audiolab" \
username="vault" \
password="vault-password"
# Create role with rotation
vault write database/roles/audiolab-role \
db_name=audiolab-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
default_ttl="1h" \
max_ttl="24h"
# Get dynamic credentials (auto-rotated)
vault read database/creds/audiolab-role
🛠️ Integration Examples¶
CI/CD Integration (GitHub Actions)¶
name: Build with Vault Secrets
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Install Vault
- name: Install Vault
run: |
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
# Authenticate and fetch secrets
- name: Fetch secrets from Vault
env:
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
VAULT_TOKEN: ${{ secrets.VAULT_TOKEN }}
run: |
export CERT_PASSWORD=$(vault kv get -field=windows_cert_password secret/audiolab/ci)
echo "::add-mask::$CERT_PASSWORD"
echo "CERT_PASSWORD=$CERT_PASSWORD" >> $GITHUB_ENV
- name: Build project
run: ./build.sh
Application Integration (C++)¶
#include <cpr/cpr.h>
#include <nlohmann/json.hpp>
#include <iostream>
class VaultClient {
public:
VaultClient(const std::string& addr, const std::string& token)
: vault_addr_(addr), vault_token_(token) {}
std::string getSecret(const std::string& path, const std::string& field) {
auto response = cpr::Get(
cpr::Url{vault_addr_ + "/v1/" + path},
cpr::Header{{"X-Vault-Token", vault_token_}}
);
if (response.status_code != 200) {
throw std::runtime_error("Failed to fetch secret: " + response.text);
}
auto json = nlohmann::json::parse(response.text);
return json["data"]["data"][field].get<std::string>();
}
private:
std::string vault_addr_;
std::string vault_token_;
};
// Usage
int main() {
VaultClient vault("http://127.0.0.1:8200", "hvs.xxxxx");
try {
std::string apiKey = vault.getSecret("secret/data/audiolab/ci", "api_key");
std::cout << "API Key retrieved successfully" << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Shell Script Integration¶
#!/bin/bash
set -euo pipefail
# Configuration
VAULT_ADDR="http://127.0.0.1:8200"
VAULT_TOKEN="hvs.xxxxx"
# Function to get secret
get_secret() {
local path=$1
local field=$2
vault kv get -field="$field" "$path"
}
# Export environment variables from Vault
export VAULT_ADDR
export VAULT_TOKEN
export CERT_PASSWORD=$(get_secret secret/audiolab/ci windows_cert_password)
export APPLE_ID=$(get_secret secret/audiolab/ci apple_id)
export API_KEY=$(get_secret secret/audiolab/ci api_key)
# Run build
./build.sh
📊 Monitoring & Auditing¶
Enable Audit Logging¶
# Enable file audit device
vault audit enable file file_path=/var/log/vault_audit.log
# Enable syslog audit
vault audit enable syslog
# List audit devices
vault audit list
Query Audit Logs¶
# View recent access
tail -f /var/log/vault_audit.log | jq
# Find who accessed specific secret
cat /var/log/vault_audit.log | jq 'select(.request.path == "secret/data/audiolab/ci")'
🔧 Maintenance¶
Backup Vault¶
# Take snapshot (Vault Enterprise only)
vault operator raft snapshot save backup.snap
# For OSS, backup storage backend
tar -czf vault-backup.tar.gz /vault/data/
Seal/Unseal¶
# Seal Vault (requires restart to unseal)
vault operator seal
# Check seal status
vault status
# Unseal
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>
📚 Best Practices¶
- Never commit Vault tokens to version control
- Use short-lived tokens in CI/CD
- Enable TLS in production
- Regular audit log review
- Implement least-privilege policies
- Rotate root token periodically
- Use dynamic secrets when possible
- Monitor Vault health metrics
🔗 Related Documents¶
- SECRETS_ROTATION.md - Rotation policies
- .env.template - Local development template
- local_secrets_setup.ps1 - Local setup script