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:
572
examples/example-6-comprehensive-multi-service.yaml
Normal file
572
examples/example-6-comprehensive-multi-service.yaml
Normal 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
|
||||
Reference in New Issue
Block a user