secretumvault/docs/HOWOTO.md
2025-12-22 21:34:01 +00:00

16 KiB

SecretumVault How-To Guide

Step-by-step instructions for common tasks with SecretumVault.

Table of Contents

  1. Getting Started
  2. Initialize Vault
  3. Unseal Vault
  4. Manage Secrets
  5. Configure Engines
  6. Setup Authorization
  7. Configure TLS
  8. Integrate with Kubernetes
  9. Backup & Restore
  10. Monitor & Troubleshoot

Getting Started

1. Start Vault Locally

Using Docker Compose (recommended for development):

# Navigate to project
cd secretumvault

# Build image
docker build -t secretumvault:latest .

# Start all services
docker-compose up -d

# Verify vault is running
curl http://localhost:8200/v1/sys/health

Using Cargo:

# Create configuration
cat > svault.toml <<'EOF'
[vault]
crypto_backend = "openssl"

[server]
address = "0.0.0.0"
port = 8200

[storage]
backend = "etcd"

[storage.etcd]
endpoints = ["http://localhost:2379"]

[seal]
seal_type = "shamir"
threshold = 2
shares = 3

[engines.kv]
path = "secret/"
versioned = true

[logging]
level = "info"
format = "json"
EOF

# Start vault (requires etcd running)
cargo run --release -- server --config svault.toml

2. Verify Health

curl http://localhost:8200/v1/sys/health

Response:

{
  "initialized": false,
  "sealed": true,
  "standby": false,
  "performance_standby": false,
  "replication_performance_mode": "disabled",
  "replication_dr_mode": "disabled",
  "server_time_utc": 1703142600,
  "version": "0.1.0"
}

Key fields:

  • initialized: false - Vault not initialized yet
  • sealed: true - Master key is sealed (expected before initialization)

Initialize Vault

1. Generate Unseal Keys

Create a request to initialize vault with Shamir Secret Sharing:

curl -X POST http://localhost:8200/v1/sys/init \
  -H "Content-Type: application/json" \
  -d '{
    "shares": 5,
    "threshold": 3
  }'

Parameters:

  • shares: 5 - Total unseal keys generated (5 people get 1 key each)
  • threshold: 3 - Need 3 keys to unseal (quorum)

Response:

{
  "keys": [
    "key_1_base64_encoded",
    "key_2_base64_encoded",
    "key_3_base64_encoded",
    "key_4_base64_encoded",
    "key_5_base64_encoded"
  ],
  "root_token": "root_token_abc123def456"
}

2. Store Keys Securely

CRITICAL: Store unseal keys immediately in a secure location!

Save in password manager (Bitwarden, 1Password, LastPass):

  • Each unseal key separately (don't store all together)
  • Distribute keys to different people/locations
  • Test that stored keys are retrievable

Save root token separately:

  • Store in same password manager
  • Label clearly: "Root Token - SecretumVault"
  • Keep temporary access only

3. Verify Initialization

curl http://localhost:8200/v1/sys/health

Response should now show initialized: true and sealed: true


Unseal Vault

Vault must be unsealed before it can serve requests.

1. Unseal with Keys

You need threshold keys (e.g., 3 of 5) to unseal.

Unseal with first key:

curl -X POST http://localhost:8200/v1/sys/unseal \
  -H "Content-Type: application/json" \
  -d '{
    "key": "first_unseal_key_from_storage"
  }'

Response:

{
  "sealed": true,
  "t": 3,
  "n": 5,
  "progress": 1
}

Progress shows 1/3 keys provided.

Unseal with second key:

curl -X POST http://localhost:8200/v1/sys/unseal \
  -H "Content-Type: application/json" \
  -d '{
    "key": "second_unseal_key_from_storage"
  }'

Response shows progress: 2/3

Unseal with third key (final):

curl -X POST http://localhost:8200/v1/sys/unseal \
  -H "Content-Type: application/json" \
  -d '{
    "key": "third_unseal_key_from_storage"
  }'

Response:

{
  "sealed": false,
  "t": 3,
  "n": 5,
  "progress": 0
}

sealed: false means vault is now unsealed!

2. Verify Unsealed State

curl http://localhost:8200/v1/sys/health

Should show sealed: false

3. Auto-Unseal (Future)

For production, configure auto-unseal via AWS KMS or GCP Cloud KMS (planned):

[seal]
seal_type = "aws-kms"

[seal.aws-kms]
key_id = "arn:aws:kms:us-east-1:account:key/id"
region = "us-east-1"

Manage Secrets

1. Store a Secret

HTTP Request:

curl -X POST http://localhost:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "username": "admin",
      "password": "supersecret123",
      "api_key": "sk_live_abc123"
    }
  }'

Environment variable setup:

# From initialization response
export VAULT_TOKEN="root_token_abc123"

Response:

{
  "request_id": "req_123",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": null,
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Status 201 Created indicates success.

2. Read a Secret

HTTP Request:

curl -X GET http://localhost:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN"

Response:

{
  "request_id": "req_124",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "data": {
      "username": "admin",
      "password": "supersecret123",
      "api_key": "sk_live_abc123"
    },
    "metadata": {
      "created_time": "2025-12-21T10:30:00Z",
      "deletion_time": "",
      "destroyed": false,
      "version": 1
    }
  }
}

Extract secret data:

# Get password field
curl -s http://localhost:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN" | jq '.data.data.password'

Output: "supersecret123"

3. Update a Secret

curl -X POST http://localhost:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "username": "admin",
      "password": "newsecret456",
      "api_key": "sk_live_abc123"
    }
  }'

New version created (version 2). Previous versions retained.

4. Delete a Secret

curl -X DELETE http://localhost:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN"

Soft delete: metadata retained, data destroyed.

5. List Secrets

curl -X LIST http://localhost:8200/v1/secret/metadata \
  -H "X-Vault-Token: $VAULT_TOKEN"

Response:

{
  "data": {
    "keys": [
      "myapp",
      "database-prod",
      "aws-credentials"
    ]
  }
}

6. Restore from Version

View available versions:

curl -X GET http://localhost:8200/v1/secret/metadata/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN"

Response shows all versions with timestamps.

Get specific version:

curl -X GET http://localhost:8200/v1/secret/data/myapp?version=1 \
  -H "X-Vault-Token: $VAULT_TOKEN"

Configure Engines

1. Enable Additional Engines

Edit svault.toml:

[engines.kv]
path = "secret/"
versioned = true

[engines.transit]
path = "transit/"
versioned = true

[engines.pki]
path = "pki/"
versioned = false

[engines.database]
path = "database/"
versioned = false

Restart vault:

# Docker Compose
docker-compose restart vault

# Or Cargo
# Kill running process (Ctrl+C) and restart
cargo run --release -- server --config svault.toml

2. Use Transit Engine (Encryption)

Create encryption key:

curl -X POST http://localhost:8200/v1/transit/keys/my-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "exportable": false,
    "key_size": 256,
    "type": "aes-gcm"
  }'

Encrypt data:

# Plaintext must be base64 encoded
PLAINTEXT=$(echo -n "sensitive data" | base64)

curl -X POST http://localhost:8200/v1/transit/encrypt/my-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"plaintext\": \"$PLAINTEXT\"}"

Response:

{
  "data": {
    "ciphertext": "vault:v1:abc123def456..."
  }
}

Decrypt data:

CIPHERTEXT="vault:v1:abc123def456..."

curl -X POST http://localhost:8200/v1/transit/decrypt/my-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"ciphertext\": \"$CIPHERTEXT\"}"

Response:

{
  "data": {
    "plaintext": "c2Vuc2l0aXZlIGRhdGE="
  }
}

Decode plaintext:

echo "c2Vuc2l0aXZlIGRhdGE=" | base64 -d
# Output: sensitive data

3. Mount at Custom Path

Change mount path in config:

[engines.kv]
path = "app-secrets/"  # Instead of "secret/"
versioned = true

Then access at:

curl http://localhost:8200/v1/app-secrets/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN"

Setup Authorization

1. Create Cedar Policies

Create policy directory:

mkdir -p /etc/secretumvault/policies

Create policy file:

cat > /etc/secretumvault/policies/default.cedar <<'EOF'
permit (
  principal,
  action,
  resource
) when {
  principal has policies &&
  principal.policies.contains("admin")
};

deny (
  principal,
  action == Action::"write",
  resource
) unless {
  context.time_of_day < 20:00
};
EOF

Update config:

[auth]
cedar_policies_dir = "/etc/secretumvault/policies"

2. Create Auth Token

curl -X POST http://localhost:8200/v1/auth/token/create \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "policies": ["default", "app-reader"],
    "ttl": "24h",
    "renewable": true
  }'

Response:

{
  "auth": {
    "client_token": "s.abc123def456",
    "policies": ["default", "app-reader"],
    "metadata": {
      "created_time": "2025-12-21T10:30:00Z",
      "ttl": "24h"
    }
  }
}

Use token:

export APP_TOKEN="s.abc123def456"

curl http://localhost:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $APP_TOKEN"

3. Renew Token

curl -X POST http://localhost:8200/v1/auth/token/renew \
  -H "X-Vault-Token: $APP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "increment": "24h"
  }'

4. Revoke Token

curl -X POST http://localhost:8200/v1/auth/token/revoke \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "s.abc123def456"
  }'

Configure TLS

1. Generate Self-Signed Certificate

For development:

openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt \
  -days 365 -nodes \
  -subj "/CN=localhost/O=SecretumVault/C=US"

2. Configure Vault

Update svault.toml:

[server]
address = "0.0.0.0"
port = 8200
tls_cert = "/path/to/tls.crt"
tls_key = "/path/to/tls.key"

3. Access via HTTPS

# Allow self-signed certificate
curl --insecure https://localhost:8200/v1/sys/health \
  -H "X-Vault-Token: $VAULT_TOKEN"

# Or with CA certificate
curl --cacert tls.crt https://localhost:8200/v1/sys/health \
  -H "X-Vault-Token: $VAULT_TOKEN"

4. Production Certificate (Let's Encrypt)

For Kubernetes with cert-manager, use the Helm installation which handles automatic certificate renewal.


Integrate with Kubernetes

1. Deploy Vault

# Apply manifests
kubectl apply -f k8s/01-namespace.yaml
kubectl apply -f k8s/02-configmap.yaml
kubectl apply -f k8s/03-deployment.yaml
kubectl apply -f k8s/04-service.yaml
kubectl apply -f k8s/05-etcd.yaml

# Wait for pods
kubectl -n secretumvault wait --for=condition=ready pod -l app=vault --timeout=300s

2. Initialize and Unseal

Port-forward vault:

kubectl -n secretumvault port-forward svc/vault 8200:8200 &

Initialize (from earlier steps):

curl -X POST http://localhost:8200/v1/sys/init \
  -H "Content-Type: application/json" \
  -d '{"shares": 3, "threshold": 2}'

Save keys, then unseal (from earlier steps).

3. Create Kubernetes ServiceAccount

cat > /tmp/app-sa.yaml <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
  name: myapp
  namespace: default
EOF

kubectl apply -f /tmp/app-sa.yaml

4. Pod Secret Injection

Create ClusterRoleBinding to allow reading vault-config:

cat > /tmp/vault-reader.yaml <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: vault-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: vault-reader
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: vault-reader
subjects:
- kind: ServiceAccount
  name: myapp
  namespace: default
EOF

kubectl apply -f /tmp/vault-reader.yaml

5. Deploy Application Pod

cat > /tmp/myapp-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: myapp
  namespace: default
spec:
  serviceAccountName: myapp
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: VAULT_ADDR
      value: "http://vault.secretumvault.svc.cluster.local:8200"
    - name: VAULT_TOKEN
      valueFrom:
        secretKeyRef:
          name: vault-token
          key: token
    volumeMounts:
    - name: vault-config
      mountPath: /etc/vault
      readOnly: true
  volumes:
  - name: vault-config
    configMap:
      name: vault-config
      namespace: secretumvault
EOF

kubectl apply -f /tmp/myapp-pod.yaml

Backup & Restore

1. Backup Secrets

Export all secrets:

# List all secrets
SECRETS=$(curl -s http://localhost:8200/v1/secret/metadata \
  -H "X-Vault-Token: $VAULT_TOKEN" | jq -r '.data.keys[]')

# Backup each secret
for secret in $SECRETS; do
  curl -s http://localhost:8200/v1/secret/data/$secret \
    -H "X-Vault-Token: $VAULT_TOKEN" > $secret-backup.json
done

2. Export with Encryption

Encrypt backup before storing:

# Create transit key for backups
curl -X POST http://localhost:8200/v1/transit/keys/backup-key \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "aes-gcm"}'

# Backup and encrypt
for secret in $SECRETS; do
  CONTENT=$(curl -s http://localhost:8200/v1/secret/data/$secret \
    -H "X-Vault-Token: $VAULT_TOKEN" | base64)

  ENCRYPTED=$(curl -s -X POST http://localhost:8200/v1/transit/encrypt/backup-key \
    -H "X-Vault-Token: $VAULT_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"plaintext\": \"$CONTENT\"}" | jq -r '.data.ciphertext')

  echo "$ENCRYPTED" > $secret-backup.enc
done

3. Restore Secrets

# List backup files
for backup in *-backup.json; do
  secret=${backup%-backup.json}

  # Read backup
  curl -X POST http://localhost:8200/v1/secret/data/$secret \
    -H "X-Vault-Token: $VAULT_TOKEN" \
    -H "Content-Type: application/json" \
    -d @$backup
done

Monitor & Troubleshoot

1. Check Vault Health

curl http://localhost:8200/v1/sys/health | jq .

Key fields to check:

  • sealed: Should be false
  • initialized: Should be true
  • standby: Should be false (or expected leader state)

2. View Metrics

Prometheus metrics endpoint:

curl http://localhost:9090/metrics | grep vault

Common metrics:

  • vault_secrets_stored_total - Total secrets stored
  • vault_secrets_read_total - Total secrets read
  • vault_operations_encrypt - Encryption operations
  • vault_tokens_created - Tokens created

3. Check Logs

Docker Compose:

docker-compose logs -f vault

Kubernetes:

kubectl -n secretumvault logs -f deployment/vault

Look for:

  • ERROR entries with details
  • WARN for unexpected but recoverable conditions
  • INFO for normal operations

4. Verify Storage Connectivity

Check etcd from vault pod:

kubectl -n secretumvault exec deployment/vault -- \
  curl http://vault-etcd-client:2379/health

5. Test Token Access

Validate token is working:

curl -X GET http://localhost:8200/v1/auth/token/self \
  -H "X-Vault-Token: $VAULT_TOKEN" | jq '.auth'

Response shows token metadata and policies.

6. Common Issues

Issue: "sealed: true" after restart

  • Solution: Run unseal procedure with stored keys

Issue: "permission denied" on secret read

  • Solution: Check Cedar policies, verify token has correct policies

Issue: Storage connection error

  • Solution: Verify backend endpoint in config (etcd DNS/IP)

Issue: High memory usage

  • Solution: Check number of active leases, revoke old tokens

Issue: Slow operations

  • Solution: Check storage backend performance, review metrics

For more details, see: