193 lines
5.2 KiB
Markdown
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
|