
Hashicorp Vault
- 144 installs
- 44 repo stars
- Updated May 22, 2026
- bagelhole/devops-security-agent-skills
Configure HashiCorp Vault engines, ACL policies, AppRole and Kubernetes auth, lease renewal, and secure secret injection for cloud and CI workloads via agent-guided DevOps security workflows.
About
Agent skill for HashiCorp Vault covering secret engines, ACL policies, AppRole and Kubernetes authentication, lease management, rotation workflows, and secure credential injection patterns for DevOps security automation.
- Dynamic secrets
- ACL policy authoring
- AppRole and K8s auth
- Lease renewal flows
- Secure injection patterns
Hashicorp Vault by the numbers
- 144 all-time installs (skills.sh)
- Ranked #904 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill hashicorp-vaultAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 144 |
|---|---|
| repo stars | ★ 44 |
| Last updated | May 22, 2026 |
| Repository | bagelhole/devops-security-agent-skills ↗ |
What it does
Configure HashiCorp Vault engines, ACL policies, AppRole and Kubernetes auth, lease renewal, and secure secret injection for cloud and CI workloads via agent-guided DevOps security workflows.
Files
HashiCorp Vault
Centrally manage secrets, encryption, and access with HashiCorp Vault.
When to Use This Skill
Use this skill when:
- Centralizing secrets management
- Implementing dynamic credentials
- Managing PKI and certificates
- Encrypting sensitive data
- Meeting compliance requirements
Prerequisites
- Vault server (dev or production)
- Vault CLI installed
- Network access to Vault
Quick Start
Development Server
# Start dev server
vault server -dev
# Set environment
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
# Verify connection
vault statusProduction Deployment
# config.hcl
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
}
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"
ui = true# Initialize Vault
vault operator init -key-shares=5 -key-threshold=3
# Unseal (run 3 times with different keys)
vault operator unseal <key-1>
vault operator unseal <key-2>
vault operator unseal <key-3>
# Login
vault login <root-token>Secret Engines
KV Secrets
# Enable KV v2
vault secrets enable -path=secret kv-v2
# Write secret
vault kv put secret/myapp/config \
username="admin" \
password="s3cr3t"
# Read secret
vault kv get secret/myapp/config
vault kv get -field=password secret/myapp/config
# Update secret
vault kv put secret/myapp/config \
username="admin" \
password="new-password"
# List secrets
vault kv list secret/
# Delete secret
vault kv delete secret/myapp/config
# Version history
vault kv metadata get secret/myapp/configDatabase Secrets
# Enable database engine
vault secrets enable database
# Configure PostgreSQL connection
vault write database/config/postgresql \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@localhost:5432/mydb" \
allowed_roles="readonly,readwrite" \
username="vault" \
password="vault-password"
# Create role
vault write database/roles/readonly \
db_name=postgresql \
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"
# Get credentials
vault read database/creds/readonlyAWS Secrets
# Enable AWS engine
vault secrets enable aws
# Configure root credentials
vault write aws/config/root \
access_key=AKIA... \
secret_key=secret... \
region=us-east-1
# Create role
vault write aws/roles/deploy \
credential_type=iam_user \
policy_document=-<<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}
EOF
# Get credentials
vault read aws/creds/deployPKI Secrets
# Enable PKI engine
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# Generate root CA
vault write -field=certificate pki/root/generate/internal \
common_name="example.com" \
ttl=87600h > ca_cert.crt
# Configure URLs
vault write pki/config/urls \
issuing_certificates="https://vault.example.com:8200/v1/pki/ca" \
crl_distribution_points="https://vault.example.com:8200/v1/pki/crl"
# Create role
vault write pki/roles/web-server \
allowed_domains="example.com" \
allow_subdomains=true \
max_ttl="720h"
# Issue certificate
vault write pki/issue/web-server \
common_name="web.example.com" \
ttl="24h"Authentication Methods
AppRole
# Enable AppRole
vault auth enable approle
# Create role
vault write auth/approle/role/myapp \
token_policies="myapp-policy" \
token_ttl=1h \
token_max_ttl=4h \
secret_id_ttl=10m
# Get role ID
vault read auth/approle/role/myapp/role-id
# Generate secret ID
vault write -f auth/approle/role/myapp/secret-id
# Login
vault write auth/approle/login \
role_id=<role-id> \
secret_id=<secret-id>Kubernetes
# Enable Kubernetes auth
vault auth enable kubernetes
# Configure
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# Create role
vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=default \
policies=myapp-policy \
ttl=1hOIDC
# Enable OIDC auth
vault auth enable oidc
# Configure
vault write auth/oidc/config \
oidc_discovery_url="https://accounts.google.com" \
oidc_client_id="your-client-id" \
oidc_client_secret="your-client-secret" \
default_role="default"
# Create role
vault write auth/oidc/role/default \
bound_audiences="your-client-id" \
allowed_redirect_uris="http://localhost:8250/oidc/callback" \
user_claim="sub" \
policies="default"Policies
Policy Definition
# myapp-policy.hcl
# Read secrets
path "secret/data/myapp/*" {
capabilities = ["read", "list"]
}
# Database credentials
path "database/creds/myapp-db" {
capabilities = ["read"]
}
# PKI certificates
path "pki/issue/web-server" {
capabilities = ["create", "update"]
}
# Deny access to other secrets
path "secret/data/other/*" {
capabilities = ["deny"]
}# Create policy
vault policy write myapp myapp-policy.hcl
# List policies
vault policy list
# Read policy
vault policy read myappApplication Integration
Python
import hvac
# Initialize client
client = hvac.Client(url='http://localhost:8200')
# AppRole authentication
client.auth.approle.login(
role_id='role-id',
secret_id='secret-id'
)
# Read secret
secret = client.secrets.kv.v2.read_secret_version(
path='myapp/config',
mount_point='secret'
)
password = secret['data']['data']['password']
# Get database credentials
db_creds = client.secrets.database.generate_credentials(
name='myapp-db'
)Kubernetes Sidecar
apiVersion: v1
kind: Pod
metadata:
name: myapp
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
vault.hashicorp.com/agent-inject-template-config: |
{{- with secret "secret/data/myapp/config" -}}
export DB_PASSWORD="{{ .Data.data.password }}"
{{- end }}
spec:
serviceAccountName: myapp
containers:
- name: myapp
image: myapp:latest
command: ["/bin/sh", "-c", "source /vault/secrets/config && ./start.sh"]Common Issues
Issue: Sealed Vault
Problem: Vault is sealed after restart Solution: Implement auto-unseal with cloud KMS or HSM
Issue: Token Expired
Problem: Application token has expired Solution: Implement token renewal, use shorter-lived tokens
Issue: Permission Denied
Problem: Cannot access secrets Solution: Review policies, check token capabilities
Best Practices
- Use short-lived tokens
- Implement auto-unseal
- Enable audit logging
- Use namespaces for isolation
- Rotate root tokens regularly
- Implement least-privilege policies
- Use dynamic secrets where possible
- Regular backup and DR testing
Related Skills
- aws-secrets-manager - AWS native secrets
- sops-encryption - File encryption
- kubernetes-hardening - K8s security
# Kubernetes Authentication for Vault
# Enables pods to authenticate with Vault using service accounts
---
# ServiceAccount for Vault auth
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
namespace: vault
---
# ClusterRoleBinding for token review
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vault-tokenreview-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-auth
namespace: vault
---
# Secret for SA token (K8s 1.24+)
apiVersion: v1
kind: Secret
metadata:
name: vault-auth-token
namespace: vault
annotations:
kubernetes.io/service-account.name: vault-auth
type: kubernetes.io/service-account-token
---
# Example: Application ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: myapp
---
# Example: Pod using Vault Agent Injector
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
annotations:
# Vault Agent Injector annotations
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
vault.hashicorp.com/agent-inject-template-config: |
{{- with secret "secret/data/myapp/config" -}}
DATABASE_URL={{ .Data.data.database_url }}
API_KEY={{ .Data.data.api_key }}
{{- end }}
spec:
serviceAccountName: myapp
containers:
- name: myapp
image: myapp:latest
# Secrets available at /vault/secrets/config
volumeMounts:
- name: secrets
mountPath: /vault/secrets
readOnly: true
# Vault Server Configuration
# /etc/vault.d/vault.hcl
# Cluster name
cluster_name = "production"
# Storage backend (Raft for HA)
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
retry_join {
leader_api_addr = "https://vault-2.example.com:8200"
}
retry_join {
leader_api_addr = "https://vault-3.example.com:8200"
}
}
# Listener configuration
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
# TLS settings
tls_min_version = "tls12"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
}
# API address
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault-1.example.com:8201"
# UI
ui = true
# Telemetry
telemetry {
prometheus_retention_time = "30s"
disable_hostname = true
}
# Audit logging
# Enable via API after init:
# vault audit enable file file_path=/var/log/vault/audit.log
# Seal configuration (Auto-unseal with AWS KMS)
# seal "awskms" {
# region = "us-east-1"
# kms_key_id = "alias/vault-unseal-key"
# }
# Performance settings
max_lease_ttl = "768h"
default_lease_ttl = "768h"
disable_mlock = false
disable_cache = false
# Plugin directory
plugin_directory = "/opt/vault/plugins"
Vault Secrets Engines Guide
KV Secrets Engine (v2)
Enable and Configure
# Enable KV v2
vault secrets enable -path=secret kv-v2
# Write secret
vault kv put secret/myapp/config \
db_host="postgres.example.com" \
db_user="myapp" \
db_password="secret123"
# Read secret
vault kv get secret/myapp/config
vault kv get -field=db_password secret/myapp/config
# List secrets
vault kv list secret/myapp/
# Delete secret
vault kv delete secret/myapp/config
# Versioning
vault kv get -version=1 secret/myapp/config
vault kv rollback -version=1 secret/myapp/configDatabase Secrets Engine
Setup Dynamic Credentials
# Enable database engine
vault secrets enable database
# Configure PostgreSQL
vault write database/config/myapp-db \
plugin_name=postgresql-database-plugin \
allowed_roles="myapp-role" \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/myapp?sslmode=disable" \
username="vault_admin" \
password="admin_password"
# Create role
vault write database/roles/myapp-role \
db_name=myapp-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Generate credentials
vault read database/creds/myapp-roleAWS Secrets Engine
Setup Dynamic AWS Credentials
# Enable AWS engine
vault secrets enable aws
# Configure root credentials
vault write aws/config/root \
access_key=AKIAXXXXXXXX \
secret_key=xxxxxxxx \
region=us-east-1
# Create role
vault write aws/roles/deploy-role \
credential_type=iam_user \
policy_document=-<<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}
EOF
# Generate credentials
vault read aws/creds/deploy-rolePKI Secrets Engine
Certificate Authority
# Enable PKI
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# Generate root CA
vault write pki/root/generate/internal \
common_name="Example Root CA" \
ttl=87600h
# Create role
vault write pki/roles/server-cert \
allowed_domains="example.com" \
allow_subdomains=true \
max_ttl="720h"
# Issue certificate
vault write pki/issue/server-cert \
common_name="api.example.com" \
ttl="24h"Transit Secrets Engine
Encryption as a Service
# Enable transit
vault secrets enable transit
# Create encryption key
vault write -f transit/keys/myapp-key
# Encrypt data
vault write transit/encrypt/myapp-key \
plaintext=$(echo "secret data" | base64)
# Decrypt data
vault write transit/decrypt/myapp-key \
ciphertext="vault:v1:xxxxx"
# Rotate key
vault write -f transit/keys/myapp-key/rotateVault Policy Guide
Policy Syntax
# Basic policy structure
path "secret/data/myapp/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}Capabilities
| Capability | HTTP Verb | Description |
|---|---|---|
create | POST/PUT | Create new data |
read | GET | Read data |
update | POST/PUT | Update existing data |
delete | DELETE | Delete data |
list | LIST | List keys |
sudo | - | Root-protected paths |
deny | - | Explicitly deny access |
Common Policies
Application Read-Only
# app-readonly.hcl
path "secret/data/myapp/*" {
capabilities = ["read", "list"]
}
path "secret/metadata/myapp/*" {
capabilities = ["read", "list"]
}Developer Policy
# developer.hcl
path "secret/data/dev/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/data/staging/*" {
capabilities = ["read", "list"]
}
# Deny production access
path "secret/data/prod/*" {
capabilities = ["deny"]
}CI/CD Pipeline
# cicd.hcl
# Read deployment secrets
path "secret/data/deploy/*" {
capabilities = ["read"]
}
# Generate dynamic database credentials
path "database/creds/myapp-role" {
capabilities = ["read"]
}
# Sign SSH keys
path "ssh-client-signer/sign/deploy-role" {
capabilities = ["create", "update"]
}Admin Policy
# admin.hcl
# Manage secrets engines
path "sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Manage policies
path "sys/policies/acl/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Manage auth methods
path "sys/auth/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# View audit logs
path "sys/audit" {
capabilities = ["read", "list"]
}Policy Templates
Using Templating
# Per-user secrets path
path "secret/data/users/{{identity.entity.name}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Team-based access
path "secret/data/teams/{{identity.groups.names}}/*" {
capabilities = ["read", "list"]
}Policy Management
# Write policy
vault policy write myapp-policy myapp-policy.hcl
# List policies
vault policy list
# Read policy
vault policy read myapp-policy
# Delete policy
vault policy delete myapp-policy
# Test policy (requires root)
vault token create -policy=myapp-policy#!/bin/bash
# Vault Backup Script (Raft Storage)
# Usage: ./vault-backup.sh [output-dir]
set -euo pipefail
OUTPUT_DIR="${1:-./vault-backups}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$OUTPUT_DIR/vault-snapshot-$TIMESTAMP.snap"
mkdir -p "$OUTPUT_DIR"
echo "========================================="
echo "Vault Raft Snapshot Backup"
echo "Output: $BACKUP_FILE"
echo "========================================="
echo ""
# Check Vault status
if ! vault status &>/dev/null; then
echo "Error: Cannot connect to Vault or Vault is sealed"
exit 1
fi
# Take snapshot
echo "Creating snapshot..."
vault operator raft snapshot save "$BACKUP_FILE"
if [ -f "$BACKUP_FILE" ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "Snapshot created successfully!"
echo "File: $BACKUP_FILE"
echo "Size: $SIZE"
else
echo "Error: Snapshot creation failed"
exit 1
fi
# Verify snapshot
echo ""
echo "Verifying snapshot..."
vault operator raft snapshot inspect "$BACKUP_FILE" | head -20
# Cleanup old backups (keep last 7)
echo ""
echo "Cleaning up old backups (keeping last 7)..."
ls -t "$OUTPUT_DIR"/vault-snapshot-*.snap 2>/dev/null | tail -n +8 | xargs -r rm -v
echo ""
echo "========================================="
echo "Backup complete"
echo ""
echo "To restore:"
echo " vault operator raft snapshot restore $BACKUP_FILE"
echo "========================================="
#!/bin/bash
# Vault Initialization and Unseal Script
# Usage: ./vault-init.sh [vault-addr]
set -euo pipefail
export VAULT_ADDR="${1:-http://127.0.0.1:8200}"
echo "========================================="
echo "Vault Initialization"
echo "Address: $VAULT_ADDR"
echo "========================================="
echo ""
# Check if Vault is already initialized
INIT_STATUS=$(vault status -format=json 2>/dev/null | jq -r '.initialized' || echo "error")
if [ "$INIT_STATUS" == "true" ]; then
echo "Vault is already initialized"
SEALED=$(vault status -format=json | jq -r '.sealed')
if [ "$SEALED" == "true" ]; then
echo "Vault is sealed. Use unseal keys to unseal."
else
echo "Vault is unsealed and ready."
fi
exit 0
fi
if [ "$INIT_STATUS" == "error" ]; then
echo "Error: Cannot connect to Vault at $VAULT_ADDR"
exit 1
fi
# Initialize Vault
echo "Initializing Vault..."
echo ""
# Initialize with 5 key shares, 3 required to unseal
INIT_OUTPUT=$(vault operator init \
-key-shares=5 \
-key-threshold=3 \
-format=json)
# Save keys securely
echo "$INIT_OUTPUT" > vault-init-keys.json
chmod 600 vault-init-keys.json
echo "Vault initialized successfully!"
echo ""
echo "IMPORTANT: vault-init-keys.json contains your unseal keys and root token"
echo "Store these securely and distribute unseal keys to different people"
echo ""
# Extract keys
UNSEAL_KEY_1=$(echo "$INIT_OUTPUT" | jq -r '.unseal_keys_b64[0]')
UNSEAL_KEY_2=$(echo "$INIT_OUTPUT" | jq -r '.unseal_keys_b64[1]')
UNSEAL_KEY_3=$(echo "$INIT_OUTPUT" | jq -r '.unseal_keys_b64[2]')
ROOT_TOKEN=$(echo "$INIT_OUTPUT" | jq -r '.root_token')
# Unseal Vault
echo "Unsealing Vault..."
vault operator unseal "$UNSEAL_KEY_1" >/dev/null
vault operator unseal "$UNSEAL_KEY_2" >/dev/null
vault operator unseal "$UNSEAL_KEY_3" >/dev/null
echo "Vault unsealed successfully!"
echo ""
echo "Root Token: $ROOT_TOKEN"
echo ""
echo "Login with: vault login $ROOT_TOKEN"
echo ""
echo "========================================="
echo "Next steps:"
echo "1. Store unseal keys securely (different locations)"
echo "2. Create AppRole or other auth methods"
echo "3. Enable audit logging"
echo "4. Configure secrets engines"
echo "========================================="