Skip to content

Hashicorp Vault

Dokumentation für Hashicorp Vault - zentrale Secrets Management Lösung.

Overview

Hashicorp Vault ist ein Tool für das sichere Speichern und Verwalten von Secrets, Passwörtern, API-Keys, Zertifikaten und anderen sensiblen Daten.

Key Features:

  • Sichere Speicherung von Secrets mit Verschlüsselung
  • Dynamische Secrets (z.B. temporäre Datenbank-Credentials)
  • Data Encryption as a Service
  • Granulare Zugriffskontrolle via Policies
  • Audit Logging aller Zugriffe
  • Secret Rotation und Leasing
  • Multi-Cloud und On-Premise Support

Architecture

┌─────────────────────────────┐
│      Hashicorp Vault        │
│                             │
│  ┌──────────────────────┐   │
│  │   Secrets Engines    │   │
│  │  - KV (Key/Value)    │   │
│  │  - Database          │   │
│  │  - PKI               │   │
│  │  - SSH               │   │
│  └──────────────────────┘   │
│                             │
│  ┌──────────────────────┐   │
│  │   Auth Methods       │   │
│  │  - Token             │   │
│  │  - LDAP/AD           │   │
│  │  - AppRole           │   │
│  │  - Kubernetes        │   │
│  └──────────────────────┘   │
│                             │
│  ┌──────────────────────┐   │
│  │   Audit Devices      │   │
│  │  - File              │   │
│  │  - Syslog            │   │
│  └──────────────────────┘   │
└─────────────────────────────┘

         │ HTTPS API

    ┌────┴────────────┐
    │                 │
┌───▼───┐      ┌──────▼──────┐
│  CLI  │      │  Web UI     │
└───────┘      └─────────────┘
    │                 │
    └────────┬────────┘

      ┌──────▼──────┐
      │ Applications│
      │  - Puppet   │
      │  - CI/CD    │
      │  - Scripts  │
      └─────────────┘

Installation

Prerequisites

  • Docker & Docker Compose installed
  • Minimum 1GB RAM, 1 CPU core
  • Storage for encrypted data
  • TLS certificates for HTTPS (or Proxy like Traefik)

Docker Installation

Docker Compose Setup

Create docker-compose.yml:

yaml
version: '3.7'
networks:
  vault:
  traefik-net:
    external: true

services:
  vault-unseal:
    image: ghcr.io/lrstanley/vault-unseal:0.7.2
    networks:
      - vault
    volumes:
      - /data/docker/vault/vault-unseal.yaml:/etc/vault-unseal.yaml
    #command: sh -c "tail -f /dev/null" 
    command: "/usr/local/bin/vault-unseal -c /etc/vault-unseal.yaml"
  vault:
    image: hashicorp/vault:1.21 # Use a specific version in production, e.g., :1.17.0

    # 1. Security: IPC_LOCK prevents memory swapping to disk
    # This is critical for security in a persistent setup.
    cap_add:
      - IPC_LOCK

    # 2. Ports: Vault API (8200) and Cluster Port (8201) exposed
    #ports:
    #  - "8200:8200" # Vault API and UI
    #  - "8201:8201" # Raft Cluster Port (needed even for a single node)

    # 3. Volumes: Ensure Persistence and Configuration Loading
    volumes:
      # Mount the HCL config file to the Vault config directory
      - /data/docker/vault/config/vault.hcl:/vault/config/vault.hcl:ro
      # Mount the named Docker volume for persistent storage
      - /data/docker/vault/data:/vault/data
      - /data/docker/vault/logs:/vault/logs

    networks:
      - traefik-net
      - vault

    deploy:
      labels:
        - "traefik.swarm.network=traefik-net"
        - "traefik.enable=true"
        - "traefik.http.routers.vault-iteas.rule=Host(`vault.iteas.cloud`)"
        - "traefik.http.routers.vault-iteas.tls.certresolver=default"
        - "traefik.http.services.vault-iteas.loadbalancer.server.port=8200"

    # 4. Environment Variables
    environment:
      # Keep VAULT_ADDR for the Vault CLI inside the container
      # This should be the address the container *knows* (localhost)
      - VAULT_ADDR=http://127.0.0.1:8200

      # IMPORTANT: Set VAULT_API_ADDR to the Traefik-exposed domain (HTTPS)
      # This tells Vault what URL to use for external requests/UI links.
      - VAULT_API_ADDR=https://vault.iteas.cloud


    # 5. Command: Run Vault in Server Mode
    # The container will use the vault.hcl config file.
    #command: "server -config=/vault/config/vault.hcl"
    #command: sh -c "tail -f /dev/null" 
    entrypoint: vault server -config /vault/config/vault.hcl

    # 6. Health Check
    # Ensures the service is actually running and responsive.
    #healthcheck:
    #  test: ["CMD", "vault", "status"]
    #  interval: 10s
    #  timeout: 5s
    #  retries: 10

Configuration

Create ./vault/config/vault.hcl:

hcl
# 1. Listener Configuration
# Vault must listen on the Docker bridge network interface (0.0.0.0)
# and expose its API on port 8200.
listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_disable = "true" # IMPORTANT: Disable TLS for a simple local test setup.
                       # In production, this MUST be set to "false" and proper
                       # certificates must be configured.
}

api_addr = "http://127.0.0.1:8200"
cluster_addr = "http://127.0.0.1:8201"

# 2. Storage Backend (Raft)
# Raft is the built-in storage engine and modern best practice.
# The path "/vault/data" is the persistent location inside the container.
storage "raft" {
  path    = "/vault/data"
  node_id = "node1"
}

# 3. UI
ui = true

disable_mlock = "true"

Create ./vault/vault-unseal.yaml:

yaml
---

# named environment that vault-unseal is running in, gets passed in when sending
# email alerts.
environment: dev

# delay between seal-checks of each vault node.
check_interval: 15s

# maximum delay between checks of each vault node. when an error occurs, we will
# add a backoff delay, up to this maximum.
max_check_interval: 30m

# list of vault nodes to check, must include http/https, and a port (unless 80/443).
vault_nodes:
  - http://vault:8200

# unseal tokens necessary to unseal any of the given vaults in the above node
# list.
#
# WARNING: do not put enough tokens in this list that can be used to unseal a
# vault instance. I.e. if vault requires 3 of 5 tokens, DO NOT PUT 3 TOKENS HERE.
# the goal is to put less than the required amount, but have more instances of
# vault-unseal setup with the other missing tokens from the list. this ensures
# that if the server was compromised, they don't have all of the needed tokens.
#
# i.e. 1 instance of vault-unseal on each of the three nodes, each with two
# tokens. given A, B, and C tokens required, each instance should have the
# following tokens:
#   * 1: AB
#   * 2: BC
#   * 3: AC
unseal_tokens:
  - xyz

# skip tls checks for the given vault instance. useful if your instance doesn't
# have a certificate which has all of the server hostnames on it.
tls_skip_verify: false

# email notifications. setting this to false will disable all notifications.
email:
  enabled: false
  hostname: smtp.hostname.com
  port: 25
  username: your-username
  password: your-password
  # address to send from.
  from_addr: your-alerts@hostname.com
  # addresses to send to. the first will be the TO, the second and on will be CC'd
  # onto the message.
  send_addrs:
    - your-alert-group@hostname.com
    - example-user@hostname.com
  # Skip TLS certificate validation.
  tls_skip_verify: false
  # Require TLS for SMTP connections.
  # The default is opportunistic.
  mandatory_tls: false

# notifications in vault-unseal queue up to prevent email spam (e.g. 20 alerts
# in one email). this is the max allotted time an event can be queued before
# the queue is sent as a notification.
notify_max_elapsed: 10m

# queue delay is the amount of time vault-unseal waits after the last received
# notification, before it sends all of them in bulk.
notify_queue_delay: 60s

allow_single_node: true

Directory Structure

bash
# Create directory structure
mkdir -p vault/{config,data,logs}

Start Vault

bash
# Start Vault container
docker-compose up -d

# Check logs
docker-compose logs -f vault

# Verify container is running
docker-compose ps

Initialize Vault

bash
# Access Vault container
docker exec -it vault sh

# Set Vault address
export VAULT_ADDR='https://127.0.0.1:8200'
export VAULT_SKIP_VERIFY=true  # Only for self-signed certs

# Initialize (ONLY ONCE!)
vault operator init -key-shares=1 -key-threshold=1

# Output example:
# Unseal Key 1: xxx
# Initial Root Token: s.xxxxxxxxxxxxxxxxxxxxxxxx

Save Unseal Keys and Root Token
Die Unseal Keys und der Root Token müssen sicher gespeichert werden!
Ohne diese Keys kann Vault nicht entsperrt werden und alle Daten sind verloren.

Unseal Vault

Der Vault kann direkt via Web-UI oder CLI entsperrt werden.

bash
# From host machine (requires vault CLI)
export VAULT_ADDR='https://vault.iteas.cloud'
vault operator unseal <key1>

# Or from inside container
docker exec -it vault sh
export VAULT_ADDR='https://127.0.0.1:8200'
export VAULT_SKIP_VERIFY=true
vault operator unseal <key1>

# Check status
vault status

Vault CLI Access from Host

For convenience, install Vault CLI on the host machine: https://developer.hashicorp.com/vault/install

bash
# Configure environment for linux
export VAULT_ADDR='https://vault.iteas.cloud'
# or for windows powershell
$env:VAULT_ADDR = 'https://vault.iteas.cloud'

# Verify
vault status

Basic Usage

Authentication

bash
# Login with root token
vault login <root_token>

# Login with Microsoft OIDC
vault login -method=oidc -path=oidc

Secrets Engines

Key/Value (KV) Secrets

bash
# Enable KV v2 secrets engine
vault secrets enable -path=secret kv-v2

# Write a secret
vault kv put secret/myapp/config \
  username="admin" \
  password="SuperSecret123"

# Read a secret
vault kv get secret/myapp/config

# Read specific field
vault kv get -field=password secret/myapp/config

# List secrets
vault kv list secret/myapp

# Delete secret
vault kv delete secret/myapp/config

Database Dynamic Secrets

bash
# Enable database secrets engine
vault secrets enable database

# Configure database connection
vault write database/config/mysql \
  plugin_name=mysql-database-plugin \
  connection_url="{{username}}:{{password}}@tcp(mysql.iteas.tools:3306)/" \
  allowed_roles="readonly,readwrite" \
  username="vault" \
  password="vault-password"

# Create role for dynamic credentials
vault write database/roles/readonly \
  db_name=mysql \
  creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT SELECT ON *.* TO '{{name}}'@'%';" \
  default_ttl="1h" \
  max_ttl="24h"

# Generate dynamic credentials
vault read database/creds/readonly

Policies

Policies definieren, wer auf welche Secrets zugreifen darf.

Create Policy

Create app-policy.hcl:

hcl
# Read-only access to app secrets
path "secret/data/myapp/*" {
  capabilities = ["read", "list"]
}

# Write access to specific path
path "secret/data/myapp/temp/*" {
  capabilities = ["create", "update", "delete"]
}

Apply policy:

bash
vault policy write app-policy app-policy.hcl

# List policies
vault policy list

# Read policy
vault policy read app-policy

Tokens

bash
# Create token with specific policy
vault token create -policy=app-policy

# Create renewable token
vault token create -policy=app-policy -renewable -ttl=720h

# Lookup token info
vault token lookup <token>

# Renew token
vault token renew <token>

# Revoke token
vault token revoke <token>

Authentication Methods

Enable LDAP Auth

bash
# Enable LDAP
vault auth enable ldap

# Configure LDAP
vault write auth/ldap/config \
  url="ldaps://ldap.iteas.tools" \
  userdn="ou=users,dc=iteas,dc=tools" \
  groupdn="ou=groups,dc=iteas,dc=tools" \
  binddn="cn=vault,ou=serviceaccounts,dc=iteas,dc=tools" \
  bindpass="password"

# Map LDAP group to policy
vault write auth/ldap/groups/admins policies=admin-policy
vault write auth/ldap/groups/developers policies=dev-policy

# Login with LDAP
vault login -method=ldap username=john

Enable AppRole Auth

bash
# Enable AppRole
vault auth enable approle

# Create role
vault write auth/approle/role/my-app \
  token_policies="app-policy" \
  token_ttl=1h \
  token_max_ttl=4h

# Get Role ID
vault read auth/approle/role/my-app/role-id

# Generate Secret ID
vault write -f auth/approle/role/my-app/secret-id

# Login with AppRole
vault write auth/approle/login \
  role_id="<role_id>" \
  secret_id="<secret_id>"

Security Best Practices

  1. Enable Audit Logging

    bash
    vault audit enable file file_path=/var/log/vault/audit.log
  2. Use Least Privilege Policies - Grant minimal necessary permissions

  3. Enable TLS - Always use HTTPS for Vault API

  4. Rotate Root Token

    bash
    vault token revoke <old_root_token>
    vault operator generate-root
  5. Regular Backups - Backup Vault data and unseal keys

  6. Monitor Seal Status - Alert if Vault becomes sealed

  7. Use Auto-Unseal - Consider cloud KMS for auto-unseal (AWS KMS, Azure Key Vault)

  8. Token TTL Limits - Set reasonable token lifetimes

  9. Secret Rotation - Regularly rotate credentials

  10. Access Control - Use network policies to restrict access

Monitoring & Maintenance

Health Check

bash
# Check Vault status
vault status

# Check seal status
vault operator seal-status

# Check leader (in HA setup)
vault operator raft list-peers

# Check Docker container health
docker-compose ps
docker-compose logs vault --tail=50

Metrics

Vault exposes metrics via /v1/sys/metrics endpoint.

bash
# Get metrics
curl -H "X-Vault-Token: $VAULT_TOKEN" \
  https://vault.iteas.cloud/v1/sys/metrics

Backup & Recovery

Backup

bash
# Backup using raft snapshot (Raft storage only)
vault operator raft snapshot save backup.snap

Restore

bash
# Restore raft snapshot
vault operator raft snapshot restore backup.snap

# Unseal after restore
vault operator unseal <key1>

Raft Snapshot zurückspielen

Wenn Vault mit Raft-Storage betrieben wird, kann ein Snapshot wie folgt wiederhergestellt werden:

1. Snapshot wiederherstellen (Vault muss laufen und unsealed sein):

bash
# Vault authentifizieren
vault login <root_token>

# Snapshot mit -force zurückspielen
vault operator raft snapshot restore -force backup.snap

2. Alternative: Restore während Vault sealed ist

Wenn Vault noch sealed ist, kann der Snapshot auch direkt zurückgespielt werden:

bash
# Zunächst unseal durchführen
vault operator unseal <key>

# Nach Authentifizierung Snapshot restore
vault login <root_token>
vault operator raft snapshot restore -force backup.snap

# Nach erfolgreichem Restore wird Vault automatisch neu versiegelt
# Erneut unseal durchführen
vault operator unseal <key>

Wichtige Hinweise:

  • Der -force Parameter überschreibt bestehende Daten
  • Nach dem Restore wird Vault automatisch sealed und muss erneut entsperrt werden
  • Alle Vault-Knoten im Cluster müssen gestoppt sein, bevor ein Snapshot restored wird
  • Bei HA-Setup: Restore auf dem Leader-Knoten durchführen

HA-Cluster Restore:

bash
# 1. Alle Vault-Knoten stoppen
docker-compose stop vault-1 vault-2 vault-3

# 2. Nur ersten Knoten starten
docker-compose up -d vault-1

# 3. Unseal durchführen
vault operator unseal <key1>

# 4. Snapshot restore
vault login <root_token>
vault operator raft snapshot restore -force backup.snap

# 5. Erneut unseal
vault operator unseal <key1>

# 6. Andere Knoten starten (joinen automatisch)
docker-compose up -d vault-2 vault-3

# 7. Alle Knoten unsealen
vault operator unseal <key>  # Auf jedem Knoten

High Availability (HA)

Für Production-Umgebungen sollte Vault in HA-Konfiguration betrieben werden.

Raft Storage HA with Docker

Docker Compose HA Setup

Create docker-compose-ha.yml:

yaml
version: '3.8'

services:
  vault-1:
    image: hashicorp/vault:1.15
    container_name: vault-1
    restart: unless-stopped
    ports:
      - "8200:8200"
      - "8201:8201"
    environment:
      VAULT_ADDR: 'https://0.0.0.0:8200'
      VAULT_API_ADDR: 'https://vault-1.iteas.tools:8200'
      VAULT_CLUSTER_ADDR: 'https://vault-1.iteas.tools:8201'
    volumes:
      - ./vault-1/config:/vault/config:ro
      - ./vault-1/data:/vault/data
      - ./vault-1/tls:/vault/tls:ro
    cap_add:
      - IPC_LOCK
    command: server
    networks:
      - vault-network

  vault-2:
    image: hashicorp/vault:1.15
    container_name: vault-2
    restart: unless-stopped
    ports:
      - "8210:8200"
      - "8211:8201"
    environment:
      VAULT_ADDR: 'https://0.0.0.0:8200'
      VAULT_API_ADDR: 'https://vault-2.iteas.tools:8200'
      VAULT_CLUSTER_ADDR: 'https://vault-2.iteas.tools:8201'
    volumes:
      - ./vault-2/config:/vault/config:ro
      - ./vault-2/data:/vault/data
      - ./vault-2/tls:/vault/tls:ro
    cap_add:
      - IPC_LOCK
    command: server
    networks:
      - vault-network

  vault-3:
    image: hashicorp/vault:1.15
    container_name: vault-3
    restart: unless-stopped
    ports:
      - "8220:8200"
      - "8221:8201"
    environment:
      VAULT_ADDR: 'https://0.0.0.0:8200'
      VAULT_API_ADDR: 'https://vault-3.iteas.tools:8200'
      VAULT_CLUSTER_ADDR: 'https://vault-3.iteas.tools:8201'
    volumes:
      - ./vault-3/config:/vault/config:ro
      - ./vault-3/data:/vault/data
      - ./vault-3/tls:/vault/tls:ro
    cap_add:
      - IPC_LOCK
    command: server
    networks:
      - vault-network

networks:
  vault-network:
    driver: bridge

Configuration for HA Node

Create ./vault-1/config/vault.hcl:

hcl
ui = true

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/vault/tls/vault.crt"
  tls_key_file  = "/vault/tls/vault.key"
}

storage "raft" {
  path    = "/vault/data"
  node_id = "vault-1"

  retry_join {
    leader_api_addr = "https://vault-2:8200"
    leader_ca_cert_file = "/vault/tls/ca.crt"
  }
  retry_join {
    leader_api_addr = "https://vault-3:8200"
    leader_ca_cert_file = "/vault/tls/ca.crt"
  }
}

api_addr = "https://vault-1.iteas.tools:8200"
cluster_addr = "https://vault-1.iteas.tools:8201"

Note: Create similar configs for vault-2 and vault-3 with respective node_id values.

Troubleshooting

Vault is Sealed

Symptom: vault status shows Sealed: true

Solution:

bash
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>

Permission Denied

Symptom: Error: permission denied

Solution:

  1. Check token capabilities: vault token capabilities <token> <path>
  2. Review policy: vault policy read <policy_name>
  3. Verify token has correct policy attached

Connection Refused

Symptom: Cannot connect to Vault

Solution:

  1. Check Docker container: docker-compose ps
  2. Check container logs: docker-compose logs vault
  3. Verify listener address in config
  4. Check firewall: firewall-cmd --list-all
  5. Test connectivity: curl -k https://vault.iteas.tools:8200/v1/sys/health
  6. Check if container is running: docker exec -it vault vault status

Token Expired

Symptom: Error: invalid token

Solution:

bash
# Renew if renewable
vault token renew

# Or login again
vault login <method>

Resources

Integration

Vault kann mit verschiedenen Tools und Plattformen integriert werden:

  • Puppet/Ansible/Chef - Configuration Management
  • Kubernetes - Service Account Auth
  • CI/CD - Jenkins, GitLab CI, GitHub Actions
  • Cloud Providers - AWS, Azure, GCP
  • Databases - MySQL, PostgreSQL, MongoDB
  • Applications - Custom applications via API

Siehe spezifische Integrations-Dokumentation:

Docker Management

Common Docker Commands

bash
# Start Vault
docker-compose up -d

# Stop Vault
docker-compose stop

# Restart Vault
docker-compose restart

# View logs
docker-compose logs -f vault

# Access Vault shell
docker exec -it vault sh

# Remove container (data persists in volumes)
docker-compose down

# Remove container and volumes (WARNING: deletes data!)
docker-compose down -v

Auto-Start on Boot

Ensure Docker is set to start on boot:

bash
sudo systemctl enable docker

Docker Compose with restart: unless-stopped will automatically start containers on boot.

Updates

bash
# Pull latest Vault image
docker-compose pull

# Recreate container with new image
docker-compose up -d

# Unseal after update
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>

Notes

ITeas Setup

Vault Server: https://vault.iteas.cloud

Installation Method: Docker mit Docker Compose

Storage Backend: File storage (single node) / Raft (High Availability)

Primary Integration: Puppet Hiera Backend

Docker Volumes:

  • Configuration: ./vault/config
  • Data: ./vault/data
  • Logs: ./vault/logs

Iteas Tools Integration Platform Version v1.0.21

Version: v1.0.21 Version: v1.0.21
Commit: 7a0e1c11
Deployed at: 2026-09-24T13:56:52Z