Skip to content

🏦 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

  1. Never commit Vault tokens to version control
  2. Use short-lived tokens in CI/CD
  3. Enable TLS in production
  4. Regular audit log review
  5. Implement least-privilege policies
  6. Rotate root token periodically
  7. Use dynamic secrets when possible
  8. Monitor Vault health metrics