Completes Day 2 of the v1.0.3 → v1.0.4 cleanup sprint. The documentation now describes the actual repo layout instead of a fictional one. CLAUDE.md — complete rewrite Old version referenced paths that don't exist and a protocol aimed at implementing v0.11.0 (current tag: v1.0.3). The agent was following a map for a city that had been rebuilt. - backend/ → veza-backend-api/ - frontend/ → apps/web/ - ORIGIN/ (root) → veza-docs/ORIGIN/ - veza-chat-server → merged into backend-api (v0.502, commit279a10d31) - apps/desktop/ → never existed Also refreshed: stack versions (Go 1.25, Vite 5, React 18.2, Axum 0.8), commands, conventions, hook bypasses (SKIP_TYPES/SKIP_TESTS/SKIP_E2E), scope rules kept as immutable (no AI/ML, no Web3, no gamification, no dark patterns, no public popularity metrics). README.md — targeted fixes - "Version cible: v0.101" → "Version courante: v1.0.4" - "Development Setup (v0.9.3)" → "Development Setup" - Removed Desktop (Electron) section — never implemented - Removed veza-chat-server from structure — merged into backend - Removed deprecated compose files section (nothing is DEPRECATED now) k8s runbooks — remove stale chat-server references The disaster-recovery runbooks still scaled/restarted a deployment that no longer exists. In a real failover these commands would have failed silently and blocked the procedure. Files patched: - k8s/disaster-recovery/runbooks/cluster-failover.md - k8s/disaster-recovery/runbooks/data-restore.md - k8s/disaster-recovery/runbooks/database-failover.md - k8s/disaster-recovery/runbooks/rollback-procedure.md - k8s/network-policies/README.md - k8s/secrets/README.md - k8s/secrets.yaml.example Each reference is replaced by a short inline note pointing to v0.502 (commit279a10d31) so future readers understand the history. .env.example — remove CHAT_JWT_SECRET Legacy env var for the deleted chat server. Replaced by an explanatory comment. Not in this commit (user handles on Forgejo): - Closing the 5 open dependabot PRs on veza-chat-server/* branches - Deleting those 5 remote branches after the PRs are closed Refs: AUDIT_REPORT.md §5.1, §7.1, §10 P1, §10 P4
11 KiB
Secrets Management for Veza Platform
This directory contains configurations and documentation for managing secrets in the Veza platform using Kubernetes Secrets, External Secrets Operator, and optionally HashiCorp Vault.
Overview
Veza uses a multi-layered approach to secrets management:
- Kubernetes Secrets (Basic): For simple, static secrets
- External Secrets Operator (Recommended): For dynamic secrets from external providers
- HashiCorp Vault (Advanced): For enterprise-grade secrets management with rotation
Architecture
┌─────────────────────────────────────────────────────────┐
│ External Providers │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ Vault │ │ AWS │ │ GCP │ │ Azure │ │
│ │ │ │ Secrets │ │ Secrets │ │ KeyVault│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬────┘ │
└───────┼─────────────┼─────────────┼─────────────┼──────┘
│ │ │ │
└─────────────┴─────────────┴─────────────┘
│
┌─────────────▼─────────────┐
│ External Secrets Operator │
└─────────────┬─────────────┘
│
┌─────────────▼─────────────┐
│ Kubernetes Secrets │
└─────────────┬─────────────┘
│
┌─────────────▼─────────────┐
│ Veza Pods │
│ (Backend, Frontend, etc.) │
└───────────────────────────┘
Quick Start
Option 1: Basic Kubernetes Secrets (Development)
# Create secrets manually
kubectl create secret generic veza-secrets \
--from-literal=database-url='postgresql://user:pass@host:5432/db' \
--from-literal=jwt-secret='your-secret-key-min-32-chars' \
--from-literal=redis-url='redis://host:6379' \
-n veza-development
# Or from file
kubectl create secret generic veza-secrets \
--from-env-file=secrets.env \
-n veza-development
Option 2: External Secrets Operator (Recommended for Production)
# 1. Install External Secrets Operator
kubectl apply -f k8s/secrets/external-secrets-operator.yaml
# 2. Configure secret store (e.g., Vault)
kubectl apply -f k8s/secrets/secret-stores/vault-store.yaml
# 3. Create ExternalSecret resources
kubectl apply -f k8s/secrets/external-secrets/veza-secrets.yaml
Secret Stores
HashiCorp Vault
Vault provides enterprise-grade secrets management with:
- Automatic secret rotation
- Audit logging
- Fine-grained access control
- Dynamic secrets
Setup:
# 1. Install Vault (if not already installed)
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault -n vault-system --create-namespace
# 2. Configure Vault store for External Secrets
kubectl apply -f k8s/secrets/secret-stores/vault-store.yaml
# 3. Create secrets in Vault
vault kv put secret/veza/production \
database-url="postgresql://..." \
jwt-secret="..." \
redis-url="redis://..."
# 4. Create ExternalSecret
kubectl apply -f k8s/secrets/external-secrets/veza-secrets.yaml
AWS Secrets Manager
# 1. Configure AWS store
kubectl apply -f k8s/secrets/secret-stores/aws-store.yaml
# 2. Create secrets in AWS Secrets Manager
aws secretsmanager create-secret \
--name veza/production/database-url \
--secret-string "postgresql://..."
# 3. Create ExternalSecret
kubectl apply -f k8s/secrets/external-secrets/veza-secrets-aws.yaml
Google Cloud Secret Manager
# 1. Configure GCP store
kubectl apply -f k8s/secrets/secret-stores/gcp-store.yaml
# 2. Create secrets in GCP
gcloud secrets create veza-database-url --data-file=- <<< "postgresql://..."
# 3. Create ExternalSecret
kubectl apply -f k8s/secrets/external-secrets/veza-secrets-gcp.yaml
Secret Structure
Required Secrets
All Veza services require the following secrets:
| Secret Key | Description | Example |
|---|---|---|
database-url |
PostgreSQL connection string | postgresql://user:pass@host:5432/veza?sslmode=require |
redis-url |
Redis connection string | redis://host:6379/0 |
jwt-secret |
JWT signing secret (min 32 chars) | your-super-secret-jwt-key-min-32-chars-long |
Backend API Additional Secrets
| Secret Key | Description | Example |
|---|---|---|
stripe-api-key |
Stripe API key for payments | sk_live_... |
stripe-webhook-secret |
Stripe webhook signing secret | whsec_... |
smtp-password |
SMTP password for emails | password |
s3-access-key |
AWS S3 access key | AKIA... |
s3-secret-key |
AWS S3 secret key | ... |
Stream Server Additional Secrets
| Secret Key | Description | Example |
|---|---|---|
stream-server-secret |
Secret for stream server authentication | stream-secret-key |
Environment-Specific Secrets
Secrets are organized by environment:
secret/
├── veza/
│ ├── development/
│ │ ├── database-url
│ │ ├── jwt-secret
│ │ └── redis-url
│ ├── staging/
│ │ ├── database-url
│ │ ├── jwt-secret
│ │ └── redis-url
│ └── production/
│ ├── database-url
│ ├── jwt-secret
│ └── redis-url
Secret Rotation
Automatic Rotation with Vault
Vault can automatically rotate secrets:
# Enable automatic rotation for database credentials
vault write database/config/veza \
plugin_name=postgresql-database-plugin \
allowed_roles="veza-role" \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/veza" \
username="vault" \
password="vault-password" \
rotation_period="24h"
Manual Rotation
# 1. Update secret in source (Vault, AWS, etc.)
vault kv put secret/veza/production/jwt-secret value="new-secret-key"
# 2. External Secrets Operator will automatically sync
# Or force sync:
kubectl annotate externalsecret veza-secrets \
force-sync=$(date +%s) \
-n veza-production
# 3. Restart pods to pick up new secrets
kubectl rollout restart deployment/veza-backend-api -n veza-production
Rotation CronJob
A CronJob is provided to rotate secrets periodically:
kubectl apply -f k8s/secrets/secrets-rotation.yaml
Security Best Practices
-
Never commit secrets to Git
- Use
.gitignorefor secret files - Use
secrets.yaml.exampleas template
- Use
-
Use separate secrets per environment
- Different secrets for dev, staging, production
- Use namespaces to isolate environments
-
Rotate secrets regularly
- JWT secrets: Every 90 days
- Database passwords: Every 180 days
- API keys: As per provider recommendations
-
Limit access with RBAC
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: secrets-reader namespace: veza-production rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list"] -
Enable audit logging
- Log all secret access
- Monitor for unauthorized access
-
Use encryption at rest
- Enable encryption for etcd (Kubernetes secrets)
- Use encrypted volumes for Vault
-
Principle of least privilege
- Only grant access to secrets that are needed
- Use service accounts with minimal permissions
Troubleshooting
Secret not syncing
# Check ExternalSecret status
kubectl describe externalsecret veza-secrets -n veza-production
# Check External Secrets Operator logs
kubectl logs -n external-secrets-system deployment/external-secrets
# Check SecretStore connection
kubectl describe secretstore vault-store -n veza-production
Secret not found in pod
# Verify secret exists
kubectl get secret veza-secrets -n veza-production
# Check pod environment variables
kubectl exec -it deployment/veza-backend-api -n veza-production -- env | grep -i secret
# Verify secret is mounted
kubectl describe pod -l app=veza-backend-api -n veza-production | grep -A 5 "Mounts:"
Permission denied
# Check RBAC permissions
kubectl auth can-i get secrets --namespace=veza-production
# Check service account
kubectl get serviceaccount -n veza-production
kubectl describe serviceaccount veza-backend-api -n veza-production
Migration Guide
From Kubernetes Secrets to External Secrets
-
Install External Secrets Operator
kubectl apply -f k8s/secrets/external-secrets-operator.yaml -
Configure secret store
kubectl apply -f k8s/secrets/secret-stores/vault-store.yaml -
Create ExternalSecret resources
kubectl apply -f k8s/secrets/external-secrets/veza-secrets.yaml -
Verify secrets are synced
kubectl get externalsecret -n veza-production kubectl get secret veza-secrets -n veza-production -
Update deployments (if needed, External Secrets creates the same secret name)
-
Remove old manual secrets (optional, after verification)