writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
vaultUse this skill when the user says 'Vault', 'HashiCorp Vault', 'secrets management', 'dynamic secrets', 'transit engine', 'Vault policy', 'Vault auth method', 'Vault agent', 'Vault injector', 'Vault API', 'kv-v2', 'PKI secrets engine', 'database secrets engine', 'Vault role', 'Vault token'. Covers: Vault setup, secrets engines, policies, auth methods, dynami…
Independent GitHub snapshot · Automated classification · Original documentation governs
Manage secrets, encryption, and access control using HashiCorp Vault.
Exact user phrases: "Vault", "HashiCorp Vault", "secrets management", "dynamic secrets", "transit engine", "Vault policy", "Vault auth method", "Vault agent", "Vault injector", "secret engine", "kv-v2", "Vault role", "Vault token".
Before activating, verify:
Writes to Vault CLI commands, Terraform HCL for Vault, Vault policy HCL, and/or Kubernetes injector annotations.
Vault CLI commands, policy HCL, or Terraform configuration with no extraneous explanation.
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
This skill is complete when:
Direct file write. No response text.
Enable kv-v2 at secret/, write a policy granting read access, create a token, and test: vault secrets enable -path=secret kv-v2 → vault policy write → vault token create -policy=my-policy.
| Need | Engine | Example |
|---|---|---|
| Static secrets (API keys, passwords) | KV v2 | secret/myapp/config |
| Dynamic database credentials | Database | Short-lived DB users |
| TLS certificates | PKI | Internal mTLS, cert rotation |
| Encryption as a service | Transit | Encrypt/decrypt data at rest |
| Cloud credentials (AWS/Azure/GCP) | AWS/Azure/GCP | IAM role assumption |
| SSH access | SSH | Signed SSH certificates |
# Dev server (DO NOT USE in production)
vault server -dev
# Production with Integrated Storage
vault server -config=config.hcl
# config.hcl
storage "raft" {
path = "./vault/data"
node_id = "node1"
retry_join {
leader_api_addr = "https://vault-2.example.com:8200"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/path/to/cert.pem"
tls_key_file = "/path/to/key.pem"
}
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"
ui = true
log_level = "info"
seal "awskms" {
region = "us-east-1"
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/abc123"
}
# Initialize
vault operator init -key-shares=5 -key-threshold=3
# Print unseal keys and root token — store securely
vault operator unseal # Repeat with 3 of 5 keys
# Enable KV v2
vault secrets enable -path=secret kv-v2
# Write secrets
vault kv put secret/myapp/config \
db_url="postgres://user:pass@db:5432/app" \
api_key="abc123"
# Read secrets
vault kv get secret/myapp/config
vault kv get -version=2 secret/myapp/config
# List versions
vault kv list secret/myapp/
vault kv metadata get secret/myapp/config
# Soft delete
vault kv delete secret/myapp/config
# Permanent destroy
vault kv destroy secret/myapp/config
# Undelete
vault kv undelete -versions=1 secret/myapp/config
vault secrets enable database
vault write database/config/postgres-prod \
plugin_name=postgresql-database-plugin \
allowed_roles="app-role,readonly-role" \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/app" \
username="vault_admin" \
password="vault_pass"
vault write database/roles/app-role \
db_name=postgres-prod \
creation_statements="CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Get dynamic credentials
vault read database/creds/app-role
# Output:
# Key Value
# lease_id database/creds/app-role/abc123
# lease_duration 1h
# password a9b8c7d6e5f4
# username v-token-app-role-x9y8z7
# Renew lease
vault lease renew database/creds/app-role/abc123
# Revoke lease
vault lease revoke database/creds/app-role/abc123
vault write database/config/mysql-prod \
plugin_name=mysql-database-plugin \
allowed_roles="app-mysql" \
connection_url="{{username}}:{{password}}@tcp(mysql.example.com:3306)/" \
username="vault_admin" \
password="vault_pass"
vault write database/roles/app-mysql \
db_name=mysql-prod \
creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT SELECT ON app.* TO '{{name}}'@'%';" \
default_ttl="30m" \
max_ttl="4h"
# admin.hcl
path "secret/data/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/*" {
capabilities = ["list"]
}
path "sys/*" {
capabilities = ["read"]
}
path "sys/health" {
capabilities = ["read", "sudo"]
}
# developer.hcl
path "secret/data/dev/*" {
capabilities = ["read", "list"]
}
path "secret/metadata/dev/*" {
capabilities = ["list"]
}
path "database/creds/app-role" {
capabilities = ["read"]
}
# ci-bot.hcl
path "secret/data/ci/*" {
capabilities = ["read"]
}
path "database/creds/ci-role" {
capabilities = ["read"]
}
path "transit/encrypt/ci-key" {
capabilities = ["create", "update"]
}
path "transit/decrypt/ci-key" {
capabilities = ["create", "update"]
}
# audit-log.hcl
path "sys/audit/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
vault policy write admin admin.hcl
vault policy write developer developer.hcl
vault policy write ci-bot ci-bot.hcl
# Kubernetes auth
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host=https://kubernetes.default.svc \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
vault write auth/kubernetes/role/my-app \
bound_service_account_names=my-app \
bound_service_account_namespaces=default \
policies=developer \
ttl=1h \
max_ttl=24h \
period=0
# AppRole auth
vault auth enable approle
vault write auth/approle/role/my-role \
secret_id_ttl=10m \
secret_id_num_uses=1 \
token_ttl=1h \
token_max_ttl=4h \
token_policies=ci-bot
vault read auth/approle/role/my-role/role-id
vault write -f auth/approle/role/my-role/secret-id
# OIDC auth
vault auth enable oidc
vault write auth/oidc/config \
oidc_discovery_url="https://accounts.example.com" \
oidc_client_id="vault-client" \
oidc_client_secret="client-secret" \
default_role="developer"
vault write auth/oidc/role/developer \
bound_audiences="vault-client" \
allowed_redirect_uris=["https://vault.example.com:8200/oidc/callback"] \
user_claim="sub" \
policies="developer" \
ttl="1h"
# AWS IAM auth
vault auth enable aws
vault write auth/aws/config/client \
access_key="AKIA..." \
secret_key="..."
vault write auth/aws/role/deploy-role \
auth_type=iam \
bound_iam_principal_arn="arn:aws:iam::123456789012:role/deploy-role" \
policies=ci-bot \
ttl=1h
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# Generate root CA
vault write pki/root/generate/internal \
common_name=example.com \
ttl=87600h
# Configure CRL and issuing 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 for cert issuance
vault write pki/roles/shared \
allowed_domains=example.com \
allow_subdomains=true \
max_ttl=720h \
key_type=rsa \
key_bits=2048
# Issue cert
vault write pki/issue/shared \
common_name=myapp.example.com \
ttl=24h
vault secrets enable transit
# Create encryption key
vault write -f transit/keys/payment-keys
# Rotate key
vault write -f transit/keys/payment-keys/rotate
# Encrypt
vault write transit/encrypt/payment-keys \
plaintext=$(echo -n "sensitive-data" | base64)
# Decrypt
vault write transit/decrypt/payment-keys \
ciphertext="vault:v1:abc123..."
# Create key with exportable material
vault write transit/keys/exportable-key \
type=aes256-gcm96 \
exportable=true
# Create datakey (envelope encryption)
vault write transit/datakey/plaintext/payment-keys \
key_version=1 \
bits=256
# vault-agent.hcl
pid_file = "/tmp/vault-agent-pid"
vault {
address = "https://vault.example.com:8200"
}
auto_auth {
method "kubernetes" {
mount_path = "auth/kubernetes"
config = {
role = "my-app"
}
}
sink "file" {
config = {
path = "/vault/secrets/token"
}
}
}
template {
source = "/vault/templates/config.ctmpl"
destination = "/app/config/.env"
create_dest_dirs = true
}
# /vault/templates/config.ctmpl
{{- with secret "secret/data/myapp/config" }}
DB_URL={{ .Data.data.db_url }}
API_KEY={{ .Data.data.api_key }}
{{- end }}
{{- with secret "database/creds/app-role" }}
DB_USER={{ .Data.username }}
DB_PASS={{ .Data.password }}
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-app"
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_URL={{ .Data.data.db_url }}
export API_KEY={{ .Data.data.api_key }}
{{- end -}}
vault.hashicorp.com/agent-run-as-user: "1000"
vault.hashicorp.com/agent-run-as-group: "1000"
# Enable file audit
vault audit enable file file_path=/vault/logs/audit.log
# Enable syslog audit
vault audit enable syslog \
facility="AUTH" \
tag="vault" \
log_level="info"
# List audit devices
vault audit list
# Disable audit
vault audit disable file/
provider "vault" {
address = "https://vault.example.com:8200"
token = var.vault_token
}
resource "vault_mount" "kv" {
path = "secret"
type = "kv-v2"
}
resource "vault_policy" "developer" {
name = "developer"
policy = file("policies/developer.hcl")
}
resource "vault_kubernetes_auth_backend_config" "k8s" {
backend = vault_auth_backend.kubernetes.path
kubernetes_host = "https://kubernetes.default.svc"
}
resource "vault_kubernetes_auth_backend_role" "app" {
backend = vault_auth_backend.kubernetes.path
role_name = "my-app"
bound_service_account_names = ["my-app"]
bound_service_account_namespaces = ["default"]
token_policies = ["developer"]
token_ttl = 3600
}
resource "vault_database_secret_backend_connection" "postgres" {
backend = vault_mount.database.path
name = "postgres-prod"
allowed_roles = ["app-role"]
postgresql {
connection_url = "postgresql://{{username}}:{{password}}@postgres:5432/app"
username = "vault_admin"
password = var.db_admin_password
}
}
resource "vault_database_secret_backend_role" "app_role" {
backend = vault_mount.database.path
name = "app-role"
db_name = vault_database_secret_backend_connection.postgres.name
creation_statements = [
"CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"
]
default_ttl = 3600
max_ttl = 86400
}
default_ttl and max_ttl on dynamic credentials — never omitkv-v2 over kv-v1 for secret versioning and delete protectionmax_lease_ttl of 1h for dynamic secrets whenever possiblevault operator raft list-peers.* capabilities) — security risk.After completing this skill: