Initial public release v1.0.7

- Streamlined README focused on quick start
- Complete examples for all major use cases
- Decision tree for choosing right pattern
- Comprehensive troubleshooting guide
This commit is contained in:
2025-10-26 14:53:01 +01:00
commit 1bc8aadb85
9 changed files with 3485 additions and 0 deletions

View File

@@ -0,0 +1,572 @@
# Example 6: Comprehensive Multi-Service Deployment
#
# This example demonstrates a complete application stack with:
# - PostgreSQL (rotating with previous version)
# - Redis (rotating without previous version)
# - External API key (no rotation)
# - TLS certificates (no rotation)
# - Application credentials (rotating with Reloader)
#
# Shows different rotation strategies working together
---
apiVersion: v1
kind: Namespace
metadata:
name: myapp
---
# ============================================================================
# 1. PostgreSQL Credentials - Rotates with previous version support
# ============================================================================
apiVersion: secrets.c5ai.ch/v1alpha1
kind: ManagedSecret
metadata:
name: postgres-credentials
namespace: myapp
spec:
vault:
address: "http://openbao.openbao.svc.cluster.local:8200"
authMethod: kubernetes
role: managedsecret-operator
kvVersion: v2
mount: secret
path: myapp/postgres
fields:
- name: username
type: static
value: "myapp_user"
- name: password
type: generated
generator:
type: password
length: 32
minDigits: 6
minSymbols: 6
- name: host
type: static
value: "postgres.database.svc.cluster.local"
- name: port
type: static
value: "5432"
- name: database
type: static
value: "myapp_db"
destination:
name: postgres-creds
type: Opaque
keepPreviousVersion: true # Needs old password for rotation
previousVersionTTL: 1h
rotation:
enabled: true
schedule: 2160h # 90 days
---
# ============================================================================
# 2. Redis Password - Rotates without previous version (CONFIG SET)
# ============================================================================
apiVersion: secrets.c5ai.ch/v1alpha1
kind: ManagedSecret
metadata:
name: redis-credentials
namespace: myapp
spec:
vault:
address: "http://openbao.openbao.svc.cluster.local:8200"
authMethod: kubernetes
role: managedsecret-operator
kvVersion: v2
mount: secret
path: myapp/redis
fields:
- name: password
type: generated
generator:
type: password
length: 40
minDigits: 8
minSymbols: 0 # Redis doesn't like special chars
symbolCharacters: ""
- name: host
type: static
value: "redis.myapp.svc.cluster.local"
- name: port
type: static
value: "6379"
destination:
name: redis-creds
type: Opaque
# No previous version needed - Redis CONFIG SET doesn't require auth
rotation:
enabled: true
schedule: 720h # 30 days
---
# ============================================================================
# 3. External API Keys - No rotation
# ============================================================================
apiVersion: secrets.c5ai.ch/v1alpha1
kind: ManagedSecret
metadata:
name: external-api-keys
namespace: myapp
spec:
vault:
address: "http://openbao.openbao.svc.cluster.local:8200"
authMethod: kubernetes
role: managedsecret-operator
kvVersion: v2
mount: secret
path: myapp/api-keys
fields:
- name: stripe-api-key
type: generated
generator:
type: password
length: 64
minDigits: 0
minSymbols: 0
- name: sendgrid-api-key
type: generated
generator:
type: password
length: 64
minDigits: 0
minSymbols: 0
destination:
name: api-keys
type: Opaque
rotation:
enabled: false # Managed externally
---
# ============================================================================
# 4. TLS Certificate - No rotation (managed by cert-manager)
# ============================================================================
apiVersion: secrets.c5ai.ch/v1alpha1
kind: ManagedSecret
metadata:
name: app-tls-cert
namespace: myapp
spec:
vault:
address: "http://openbao.openbao.svc.cluster.local:8200"
authMethod: kubernetes
role: managedsecret-operator
kvVersion: v2
mount: secret
path: myapp/tls
fields:
- name: tls.crt
type: static
value: |
-----BEGIN CERTIFICATE-----
# Your certificate here
-----END CERTIFICATE-----
- name: tls.key
type: static
value: |
-----BEGIN PRIVATE KEY-----
# Your private key here
-----END PRIVATE KEY-----
destination:
name: app-tls
type: kubernetes.io/tls
rotation:
enabled: false
---
# ============================================================================
# 5. Application JWT Secret - Rotates with Reloader
# ============================================================================
apiVersion: secrets.c5ai.ch/v1alpha1
kind: ManagedSecret
metadata:
name: app-jwt-secret
namespace: myapp
spec:
vault:
address: "http://openbao.openbao.svc.cluster.local:8200"
authMethod: kubernetes
role: managedsecret-operator
kvVersion: v2
mount: secret
path: myapp/jwt
fields:
- name: jwt-secret
type: generated
generator:
type: password
length: 64
minDigits: 16
minSymbols: 0
symbolCharacters: ""
destination:
name: jwt-secret
type: Opaque
rotation:
enabled: true
schedule: 4320h # 180 days
---
# ============================================================================
# PostgreSQL Password Rotation Job
# ============================================================================
apiVersion: batch/v1
kind: CronJob
metadata:
name: postgres-password-rotator
namespace: myapp
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: rotate
image: postgres:15-alpine
env:
- name: PGUSER
valueFrom:
secretKeyRef:
name: postgres-creds
key: username
- name: NEW_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-creds
key: password
- name: OLD_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-creds-previous
key: password
optional: true
- name: PGHOST
valueFrom:
secretKeyRef:
name: postgres-creds
key: host
command:
- sh
- -c
- |
if [ -n "$OLD_PASSWORD" ] && [ "$OLD_PASSWORD" != "$NEW_PASSWORD" ]; then
echo "Rotating PostgreSQL password..."
PGPASSWORD=$OLD_PASSWORD psql -c "ALTER USER $PGUSER PASSWORD '$NEW_PASSWORD';"
echo "Rotation complete!"
fi
---
# ============================================================================
# Redis Password Rotation Job
# ============================================================================
apiVersion: batch/v1
kind: CronJob
metadata:
name: redis-password-rotator
namespace: myapp
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: rotate
image: redis:7-alpine
env:
- name: REDIS_HOST
valueFrom:
secretKeyRef:
name: redis-creds
key: host
- name: REDIS_PORT
valueFrom:
secretKeyRef:
name: redis-creds
key: port
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-creds
key: password
command:
- sh
- -c
- |
# Redis CONFIG SET doesn't require current password
redis-cli -h $REDIS_HOST -p $REDIS_PORT CONFIG SET requirepass "$REDIS_PASSWORD"
echo "Redis password updated"
---
# ============================================================================
# Main Application Deployment
# ============================================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
annotations:
# Reloader restarts pods when ANY of these secrets change
reloader.stakater.com/auto: "true"
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
name: http
env:
# PostgreSQL connection
- name: POSTGRES_HOST
valueFrom:
secretKeyRef:
name: postgres-creds
key: host
- name: POSTGRES_PORT
valueFrom:
secretKeyRef:
name: postgres-creds
key: port
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: postgres-creds
key: database
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: postgres-creds
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-creds
key: password
# Redis connection
- name: REDIS_HOST
valueFrom:
secretKeyRef:
name: redis-creds
key: host
- name: REDIS_PORT
valueFrom:
secretKeyRef:
name: redis-creds
key: port
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-creds
key: password
# API Keys
- name: STRIPE_API_KEY
valueFrom:
secretKeyRef:
name: api-keys
key: stripe-api-key
- name: SENDGRID_API_KEY
valueFrom:
secretKeyRef:
name: api-keys
key: sendgrid-api-key
# JWT Secret
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: jwt-secret
key: jwt-secret
volumeMounts:
- name: tls
mountPath: /etc/tls
readOnly: true
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: tls
secret:
secretName: app-tls
---
# ============================================================================
# Service
# ============================================================================
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
name: http
---
# ============================================================================
# Ingress with TLS
# ============================================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: myapp
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.c5ai.ch
secretName: myapp-tls-ingress
rules:
- host: myapp.c5ai.ch
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
---
# ============================================================================
# Summary of Rotation Strategies
# ============================================================================
#
# 1. PostgreSQL (postgres-creds):
# - Rotates every 90 days
# - keepPreviousVersion: true
# - CronJob uses old password to authenticate and change
# - Reloader restarts app pods after rotation
#
# 2. Redis (redis-creds):
# - Rotates every 30 days
# - No previous version needed (CONFIG SET doesn't require auth)
# - CronJob updates password directly
# - Reloader restarts app pods after rotation
#
# 3. API Keys (api-keys):
# - No automatic rotation
# - Generated once, managed externally
# - Update via kubectl or Vault UI when provider rotates keys
#
# 4. TLS Certificates (app-tls):
# - No rotation
# - Managed by cert-manager or external process
# - Static content stored in Vault
#
# 5. JWT Secret (jwt-secret):
# - Rotates every 180 days
# - Simple rotation - just restart with new secret
# - Reloader handles pod restart
#
# ============================================================================
# Timeline Example: What Happens on Day 90
# ============================================================================
#
# T+0: ManagedSecret operator triggers rotation
# - Generates new passwords for postgres-creds and redis-creds
# - Updates Vault
# - Creates postgres-creds-previous
# - Updates postgres-creds
# - Updates redis-creds (no previous needed)
#
# T+1min: CronJobs detect changes
# - Postgres rotator: Uses old password to set new password in DB
# - Redis rotator: Sets new password via CONFIG SET
#
# T+2min: Reloader detects secret changes
# - Triggers rolling restart of myapp Deployment
# - Pods start with new credentials
#
# T+1hour: Cleanup
# - postgres-creds-previous automatically deleted
# - Old credentials no longer accessible
#
# ============================================================================
# Monitoring Commands
# ============================================================================
#
# # Watch all secrets
# kubectl get secrets -n myapp -w
#
# # Check rotation status
# kubectl get managedsecrets -n myapp
#
# # View rotation logs
# kubectl logs -n myapp -l job-name=postgres-password-rotator-XXXXX
# kubectl logs -n myapp -l job-name=redis-password-rotator-XXXXX
#
# # Check Reloader activity
# kubectl logs -n reloader -l app=reloader | grep myapp
#
# # Verify app pods restarted
# kubectl get pods -n myapp -o wide
#
# # Test connections with new credentials
# kubectl exec -it -n myapp deployment/myapp -- sh
# # psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB
# # redis-cli -h $REDIS_HOST -a $REDIS_PASSWORD ping
#
# ============================================================================
# Force Rotation for Testing
# ============================================================================
#
# # Force PostgreSQL rotation
# kubectl annotate managedsecret postgres-credentials -n myapp \
# reconcile="$(date +%s)" --overwrite
#
# # Force Redis rotation
# kubectl annotate managedsecret redis-credentials -n myapp \
# reconcile="$(date +%s)" --overwrite
#
# # Force all rotations
# kubectl annotate managedsecrets -n myapp --all \
# reconcile="$(date +%s)" --overwrite
#
# # Watch the cascade of events
# kubectl get events -n myapp --sort-by='.lastTimestamp' -w