936 lines
16 KiB
Markdown
936 lines
16 KiB
Markdown
|
|
# SecretumVault How-To Guide
|
||
|
|
|
||
|
|
Step-by-step instructions for common tasks with SecretumVault.
|
||
|
|
|
||
|
|
## Table of Contents
|
||
|
|
|
||
|
|
1. [Getting Started](#getting-started)
|
||
|
|
2. [Initialize Vault](#initialize-vault)
|
||
|
|
3. [Unseal Vault](#unseal-vault)
|
||
|
|
4. [Manage Secrets](#manage-secrets)
|
||
|
|
5. [Configure Engines](#configure-engines)
|
||
|
|
6. [Setup Authorization](#setup-authorization)
|
||
|
|
7. [Configure TLS](#configure-tls)
|
||
|
|
8. [Integrate with Kubernetes](#integrate-with-kubernetes)
|
||
|
|
9. [Backup & Restore](#backup--restore)
|
||
|
|
10. [Monitor & Troubleshoot](#monitor--troubleshoot)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Getting Started
|
||
|
|
|
||
|
|
### 1. Start Vault Locally
|
||
|
|
|
||
|
|
**Using Docker Compose** (recommended for development):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8200/v1/sys/health
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:8200/v1/sys/unseal \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"key": "first_unseal_key_from_storage"
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"sealed": true,
|
||
|
|
"t": 3,
|
||
|
|
"n": 5,
|
||
|
|
"progress": 1
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Progress shows 1/3 keys provided.
|
||
|
|
|
||
|
|
**Unseal with second key:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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):**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:8200/v1/sys/unseal \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"key": "third_unseal_key_from_storage"
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"sealed": false,
|
||
|
|
"t": 3,
|
||
|
|
"n": 5,
|
||
|
|
"progress": 0
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
`sealed: false` means vault is now unsealed!
|
||
|
|
|
||
|
|
### 2. Verify Unsealed State
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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):
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[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:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# From initialization response
|
||
|
|
export VAULT_TOKEN="root_token_abc123"
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X GET http://localhost:8200/v1/secret/data/myapp \
|
||
|
|
-H "X-Vault-Token: $VAULT_TOKEN"
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X LIST http://localhost:8200/v1/secret/metadata \
|
||
|
|
-H "X-Vault-Token: $VAULT_TOKEN"
|
||
|
|
```
|
||
|
|
|
||
|
|
Response:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"keys": [
|
||
|
|
"myapp",
|
||
|
|
"database-prod",
|
||
|
|
"aws-credentials"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. Restore from Version
|
||
|
|
|
||
|
|
View available versions:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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`:
|
||
|
|
|
||
|
|
```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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"ciphertext": "vault:v1:abc123def456..."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Decrypt data:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"plaintext": "c2Vuc2l0aXZlIGRhdGE="
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Decode plaintext:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
echo "c2Vuc2l0aXZlIGRhdGE=" | base64 -d
|
||
|
|
# Output: sensitive data
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Mount at Custom Path
|
||
|
|
|
||
|
|
Change mount path in config:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[engines.kv]
|
||
|
|
path = "app-secrets/" # Instead of "secret/"
|
||
|
|
versioned = true
|
||
|
|
```
|
||
|
|
|
||
|
|
Then access at:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8200/v1/app-secrets/data/myapp \
|
||
|
|
-H "X-Vault-Token: $VAULT_TOKEN"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Setup Authorization
|
||
|
|
|
||
|
|
### 1. Create Cedar Policies
|
||
|
|
|
||
|
|
Create policy directory:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
mkdir -p /etc/secretumvault/policies
|
||
|
|
```
|
||
|
|
|
||
|
|
Create policy file:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[auth]
|
||
|
|
cedar_policies_dir = "/etc/secretumvault/policies"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Create Auth Token
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"auth": {
|
||
|
|
"client_token": "s.abc123def456",
|
||
|
|
"policies": ["default", "app-reader"],
|
||
|
|
"metadata": {
|
||
|
|
"created_time": "2025-12-21T10:30:00Z",
|
||
|
|
"ttl": "24h"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Use token:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export APP_TOKEN="s.abc123def456"
|
||
|
|
|
||
|
|
curl http://localhost:8200/v1/secret/data/myapp \
|
||
|
|
-H "X-Vault-Token: $APP_TOKEN"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Renew Token
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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`:
|
||
|
|
|
||
|
|
```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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
kubectl -n secretumvault port-forward svc/vault 8200:8200 &
|
||
|
|
```
|
||
|
|
|
||
|
|
Initialize (from earlier steps):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docker-compose logs -f vault
|
||
|
|
```
|
||
|
|
|
||
|
|
Kubernetes:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
kubectl -n secretumvault exec deployment/vault -- \
|
||
|
|
curl http://vault-etcd-client:2379/health
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Test Token Access
|
||
|
|
|
||
|
|
Validate token is working:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
- [Architecture Guide](ARCHITECTURE.md)
|
||
|
|
- [Configuration Reference](CONFIGURATION.md)
|
||
|
|
- [Deployment Guide](../DEPLOYMENT.md)
|