Files
bootstrap/guides/VAULT-SECRETS.md
2026-02-21 09:55:34 -06:00

193 lines
5.2 KiB
Markdown

# Vault Secrets Management Guide
This guide applies when the project uses HashiCorp Vault for secrets management.
## Before Starting
1. Verify Vault access: `vault status`
2. Authenticate: `vault login` (method depends on environment)
3. Check your permissions for the required paths
## Canonical Structure
**ALL Vault secrets MUST follow this structure:**
```
{mount}/{service}/{component}/{secret-name}
```
### Components
- **mount**: Environment-specific mount point
- **service**: The service or application name
- **component**: Logical grouping (database, api, oauth, etc.)
- **secret-name**: Specific secret identifier
## Environment Mounts
| Mount | Environment | Usage |
|-------|-------------|-------|
| `secret-dev/` | Development | Local dev, CI |
| `secret-staging/` | Staging | Pre-production testing |
| `secret-prod/` | Production | Live systems |
## Examples
```bash
# Database credentials
secret-prod/postgres/database/app
secret-prod/mysql/database/readonly
secret-staging/redis/auth/default
# API tokens
secret-prod/authentik/admin/token
secret-prod/stripe/api/live-key
secret-dev/sendgrid/api/test-key
# JWT/Authentication
secret-prod/backend-api/jwt/signing-key
secret-prod/auth-service/session/secret
# OAuth providers
secret-prod/backend-api/oauth/google
secret-prod/backend-api/oauth/github
# Internal services
secret-prod/loki/read-auth/admin
secret-prod/grafana/admin/password
```
## Standard Field Names
Use consistent field names within secrets:
| Purpose | Fields |
|---------|--------|
| Credentials | `username`, `password` |
| Tokens | `token` |
| OAuth | `client_id`, `client_secret` |
| Connection | `url`, `host`, `port` |
| Keys | `public_key`, `private_key` |
### Example Secret Structure
```json
// secret-prod/postgres/database/app
{
"username": "app_user",
"password": "secure-password-here",
"host": "db.example.com",
"port": "5432",
"database": "myapp"
}
```
## Rules
1. **DO NOT GUESS** secret paths - Always verify the path exists
2. **Use helper scripts** in `scripts/vault/` when available
3. **All lowercase, hyphenated** (kebab-case) for all path segments
4. **Standard field names** - Use the conventions above
5. **No sensitive data in path names** - Path itself should not reveal secrets
6. **Environment separation** - Never reference prod secrets from dev
## Deprecated Paths (DO NOT USE)
These legacy patterns are deprecated and should be migrated:
| Deprecated | Migrate To |
|------------|------------|
| `secret/infrastructure/*` | `secret-{env}/{service}/...` |
| `secret/oauth/*` | `secret-{env}/{service}/oauth/{provider}` |
| `secret/database/*` | `secret-{env}/{service}/database/{user}` |
| `secret/credentials/*` | `secret-{env}/{service}/{component}/{name}` |
## Reading Secrets
### CLI
```bash
# Read a secret
vault kv get secret-prod/postgres/database/app
# Get specific field
vault kv get -field=password secret-prod/postgres/database/app
# JSON output
vault kv get -format=json secret-prod/postgres/database/app
```
### Application Code
**Python (hvac):**
```python
import hvac
client = hvac.Client(url='https://vault.example.com')
secret = client.secrets.kv.v2.read_secret_version(
path='postgres/database/app',
mount_point='secret-prod'
)
password = secret['data']['data']['password']
```
**Node.js (node-vault):**
```javascript
const vault = require('node-vault')({ endpoint: 'https://vault.example.com' });
const secret = await vault.read('secret-prod/data/postgres/database/app');
const password = secret.data.data.password;
```
**Go:**
```go
secret, err := client.Logical().Read("secret-prod/data/postgres/database/app")
password := secret.Data["data"].(map[string]interface{})["password"].(string)
```
## Writing Secrets
Only authorized personnel should write secrets. If you need a new secret:
1. Request through proper channels (ticket, PR to IaC repo)
2. Follow the canonical structure
3. Document the secret's purpose
4. Set appropriate access policies
```bash
# Example (requires write permissions)
vault kv put secret-dev/myapp/database/app \
username="dev_user" \
password="dev-password" \
host="localhost" \
port="5432"
```
## Troubleshooting
### Permission Denied
```
Error: permission denied
```
- Verify your token has read access to the path
- Check if you're using the correct mount point
- Confirm the secret path exists
### Secret Not Found
```
Error: no value found at secret-prod/data/service/component/name
```
- Verify the exact path (use `vault kv list` to explore)
- Check for typos in service/component names
- Confirm you're using the correct environment mount
### Token Expired
```
Error: token expired
```
- Re-authenticate: `vault login`
- Check token TTL: `vault token lookup`
## Security Best Practices
1. **Least privilege** - Request only the permissions you need
2. **Short-lived tokens** - Use tokens with appropriate TTLs
3. **Audit logging** - All access is logged; act accordingly
4. **No local copies** - Don't store secrets in files or env vars long-term
5. **Rotate on compromise** - Immediately rotate any exposed secrets