2026-07-11 19:17:54 -07:00
|
|
|
# Vault: Secret Management & JWT Auth
|
|
|
|
|
|
2026-07-19 09:29:17 -07:00
|
|
|
**Vault:** `https://vault.riotpiao.com`
|
2026-07-11 19:17:54 -07:00
|
|
|
**Internal:** `vault.iam.svc.cluster.local:8200`
|
|
|
|
|
**Namespace:** `iam`
|
|
|
|
|
|
|
|
|
|
## When to Use
|
|
|
|
|
|
|
|
|
|
- **Store secrets** — Database passwords, API keys, TLS certs
|
|
|
|
|
- **Rotate credentials** — Auto-rotate, track rotation history
|
|
|
|
|
- **JWT validation** — Verify tokens from Authentik, no external call needed
|
|
|
|
|
- **Audit trail** — Who accessed what secret, when
|
|
|
|
|
|
|
|
|
|
## Quick Start
|
|
|
|
|
|
|
|
|
|
**1. Login to Vault:**
|
|
|
|
|
```bash
|
2026-07-19 09:29:17 -07:00
|
|
|
# Browser: https://vault.riotpiao.com
|
2026-07-11 19:17:54 -07:00
|
|
|
# Auth method: OIDC → "Sign in with Authentik" (federated)
|
2026-07-14 13:14:46 -07:00
|
|
|
# Or: Device code → core secrets login (CLI)
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# Via CLI (device code flow)
|
2026-07-14 13:14:46 -07:00
|
|
|
core secrets login
|
2026-07-11 19:17:54 -07:00
|
|
|
# → Opens browser, approve device code
|
|
|
|
|
# → Token cached in ~/.talos/vault
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**2. Store a secret:**
|
|
|
|
|
```bash
|
|
|
|
|
# Field name = variable name (SCREAMING_SNAKE_CASE)
|
2026-07-14 13:14:46 -07:00
|
|
|
core put cluster/ANTHROPIC_API_KEY ANTHROPIC_API_KEY="sk-..."
|
|
|
|
|
core put cluster/STORY_CRATER_DB_PASS STORY_CRATER_DB_PASS="dbpass123"
|
2026-07-11 19:17:54 -07:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**3. Retrieve a secret:**
|
|
|
|
|
```bash
|
|
|
|
|
# Always use --key flag
|
2026-07-14 13:14:46 -07:00
|
|
|
core get cluster/ANTHROPIC_API_KEY --key ANTHROPIC_API_KEY
|
2026-07-11 19:17:54 -07:00
|
|
|
# → sk-...
|
|
|
|
|
|
|
|
|
|
# Full secret as JSON
|
2026-07-14 13:14:46 -07:00
|
|
|
core get cluster/ANTHROPIC_API_KEY --json
|
2026-07-11 19:17:54 -07:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**4. Load into shell (helmfile, scripts):**
|
|
|
|
|
```bash
|
|
|
|
|
vsource .env
|
|
|
|
|
# → Expands empty vars from Vault (ANTHROPIC_API_KEY=)
|
|
|
|
|
# → Passes hardcoded vars as-is (DEBUG=true)
|
|
|
|
|
|
|
|
|
|
helmfile diff
|
|
|
|
|
helmfile apply
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Vault Paths (KV v2)
|
|
|
|
|
|
|
|
|
|
**Naming convention:** `cluster/<VARIABLE_NAME>`
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
cluster/
|
|
|
|
|
├── ANTHROPIC_API_KEY # third-party API
|
|
|
|
|
├── STORY_CRATER_DB_PASS # database password
|
|
|
|
|
├── MINIO_ROOT_PASSWORD # MinIO credentials
|
|
|
|
|
├── AUTHENTIK_BOOTSTRAP_PASSWORD # initial admin pass
|
|
|
|
|
├── iam/
|
|
|
|
|
│ ├── federation # Authentik OIDC app config
|
|
|
|
|
│ ├── roles/admin # RBAC role definitions
|
|
|
|
|
│ ├── services/grafana # OAuth2 client info
|
|
|
|
|
│ ├── agents/ci-bot # machine credentials
|
|
|
|
|
│ └── bindings/alice # user→role mappings
|
|
|
|
|
└── kubernetes/
|
|
|
|
|
├── ingress-tls # TLS cert private keys
|
|
|
|
|
└── pull-secrets # Docker registry creds
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Common Patterns
|
|
|
|
|
|
|
|
|
|
**Store generated secret immediately (keeps it out of shell history):**
|
|
|
|
|
```bash
|
|
|
|
|
# Generate & store in one command
|
2026-07-14 13:14:46 -07:00
|
|
|
core put cluster/GRAFANA_OIDC_CLIENT_SECRET \
|
2026-07-11 19:17:54 -07:00
|
|
|
GRAFANA_OIDC_CLIENT_SECRET="$(openssl rand -hex 32)"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Use in Kubernetes Secret:**
|
|
|
|
|
```bash
|
|
|
|
|
# Create Secret using Vault secret
|
|
|
|
|
kubectl create secret generic grafana-oidc \
|
2026-07-14 13:14:46 -07:00
|
|
|
--from-literal=client-secret="$(core get cluster/GRAFANA_OIDC_CLIENT_SECRET --key GRAFANA_OIDC_CLIENT_SECRET)" \
|
2026-07-11 19:17:54 -07:00
|
|
|
-n logging
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Rotate credentials safely:**
|
|
|
|
|
```bash
|
|
|
|
|
# 1. Generate new secret
|
|
|
|
|
NEW_PASS=$(openssl rand -base64 24)
|
|
|
|
|
|
|
|
|
|
# 2. Store in Vault
|
2026-07-14 13:14:46 -07:00
|
|
|
core put cluster/OLD_DB_PASS OLD_DB_PASS="$NEW_PASS"
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# 3. Update database user
|
|
|
|
|
psql -h ddb-cluster-rw.ddb.svc.cluster.local -U postgres \
|
|
|
|
|
-c "ALTER USER story_crater PASSWORD '$NEW_PASS';"
|
|
|
|
|
|
|
|
|
|
# 4. Update Kubernetes Secret
|
|
|
|
|
kubectl patch secret db-credentials -n story-crater-backend \
|
|
|
|
|
--type merge -p "{\"stringData\":{\"password\":\"$NEW_PASS\"}}"
|
|
|
|
|
|
|
|
|
|
# 5. Restart pods to pick up new Secret
|
|
|
|
|
k rollout restart -n story-crater-backend deployment/app
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**JWT token from CLI:**
|
|
|
|
|
```bash
|
|
|
|
|
# After device code login
|
2026-07-14 13:14:46 -07:00
|
|
|
core secrets login
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# Token is cached and auto-renewed
|
|
|
|
|
# Use for API calls
|
|
|
|
|
VAULT_TOKEN=$(cat ~/.talos/vault)
|
|
|
|
|
curl -H "X-Vault-Token: $VAULT_TOKEN" \
|
|
|
|
|
https://vault.iam.svc.cluster.local:8200/v1/secret/data/cluster/ANTHROPIC_API_KEY
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Monitoring
|
|
|
|
|
|
|
|
|
|
**Vault status:**
|
|
|
|
|
```bash
|
|
|
|
|
# Check if sealed
|
2026-07-14 13:14:46 -07:00
|
|
|
core status
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# If sealed (disaster recovery):
|
|
|
|
|
# See /TROUBLESHOOTING.md § Vault Sealed
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Audit log (who accessed what):**
|
|
|
|
|
```bash
|
|
|
|
|
# Enable audit logging (already enabled by helmfile)
|
|
|
|
|
# Logs stored in Loki under vault namespace
|
|
|
|
|
|
|
|
|
|
# View recent access
|
2026-07-14 13:14:46 -07:00
|
|
|
core audit log --limit 50
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# Export for compliance
|
2026-07-14 13:14:46 -07:00
|
|
|
core audit export --format json > vault-audit.json
|
2026-07-11 19:17:54 -07:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Security Rules
|
|
|
|
|
|
|
|
|
|
✅ **DO:**
|
|
|
|
|
- Store ALL secrets in Vault (never .env in git)
|
|
|
|
|
- Use field name = variable name
|
|
|
|
|
- Always use `--key` flag when retrieving
|
|
|
|
|
- Rotate credentials on schedule (quarterly)
|
|
|
|
|
- Review audit log for anomalies
|
|
|
|
|
|
|
|
|
|
❌ **DON'T:**
|
|
|
|
|
- Commit `.env` with real secrets (only `.env.example`)
|
|
|
|
|
- Use positional arguments (must use `--key`)
|
|
|
|
|
- Share Vault root token
|
|
|
|
|
- Store secrets in pod env (use Secret volumes)
|
|
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
**Cannot login (Authentik OIDC fails):**
|
|
|
|
|
```bash
|
|
|
|
|
# Check Authentik is running
|
|
|
|
|
k get pods -n iam -l app=authentik
|
|
|
|
|
|
|
|
|
|
# Verify OIDC app in Authentik console
|
|
|
|
|
# Applications → Vault OIDC → Check client ID/secret
|
|
|
|
|
|
|
|
|
|
# Restart Vault to re-sync OIDC config
|
|
|
|
|
k rollout restart -n iam statefulset/vault
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Vault is sealed:**
|
|
|
|
|
```bash
|
|
|
|
|
# Check status
|
2026-07-14 13:14:46 -07:00
|
|
|
core status
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# If sealed, use unseal keys (stored in MinIO backup)
|
|
|
|
|
# See /TROUBLESHOOTING.md § Vault Sealed for recovery steps
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Secret not found:**
|
|
|
|
|
```bash
|
|
|
|
|
# Verify path exists
|
2026-07-14 13:14:46 -07:00
|
|
|
core list cluster
|
2026-07-11 19:17:54 -07:00
|
|
|
|
|
|
|
|
# Check secret name (case-sensitive)
|
2026-07-14 13:14:46 -07:00
|
|
|
core get cluster/anthropic_api_key --key anthropic_api_key # won't work
|
|
|
|
|
core get cluster/ANTHROPIC_API_KEY --key ANTHROPIC_API_KEY # correct
|
2026-07-11 19:17:54 -07:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**vsource not expanding secrets:**
|
|
|
|
|
```bash
|
|
|
|
|
# Verify .env has empty value
|
|
|
|
|
grep ANTHROPIC_API_KEY .env
|
|
|
|
|
# → Should be: ANTHROPIC_API_KEY= (empty, not a value)
|
|
|
|
|
|
|
|
|
|
# Verify Vault is accessible
|
2026-07-14 13:14:46 -07:00
|
|
|
core get cluster/ANTHROPIC_API_KEY --key ANTHROPIC_API_KEY
|
2026-07-11 19:17:54 -07:00
|
|
|
# → Should return secret
|
|
|
|
|
|
|
|
|
|
# Run vsource explicitly
|
|
|
|
|
vsource .env
|
|
|
|
|
echo $ANTHROPIC_API_KEY # should be populated
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
See `/TROUBLESHOOTING.md` for full incident guide.
|