Files
managedsecret-operator-public/examples/example-4-minio-rotation-previous.yaml
admin 1bc8aadb85 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
2025-10-26 14:53:01 +01:00

430 lines
13 KiB
YAML

# Example 4: MinIO Admin Password Rotation with Previous Version
#
# Use case: MinIO admin credentials that rotate every 60 days
#
# Characteristics:
# - Password rotates every 60 days
# - MinIO Client (mc) needs old credentials to change password
# - keepPreviousVersion creates minio-admin-secret-previous
# - CronJob uses old credentials to authenticate, sets new password
# - After 2 hour grace period, old secret is cleaned up
---
apiVersion: v1
kind: Namespace
metadata:
name: minio
---
# ManagedSecret for MinIO admin credentials
apiVersion: secrets.c5ai.ch/v1alpha1
kind: ManagedSecret
metadata:
name: minio-admin
namespace: minio
spec:
vault:
address: "http://openbao.openbao.svc.cluster.local:8200"
authMethod: kubernetes
role: managedsecret-operator
kvVersion: v2
mount: secret
path: minio/admin
fields:
- name: username
type: static
value: "admin"
- name: password
type: generated
generator:
type: password
length: 32
minDigits: 5
minSymbols: 5
minLowercase: 5
minUppercase: 5
symbolCharacters: "!@#$%^&*"
allowRepeat: false
- name: endpoint
type: static
value: "http://minio.minio.svc.cluster.local:9000"
destination:
name: minio-admin-secret
type: Opaque
# Keep previous version for authentication during rotation
keepPreviousVersion: true
previousVersionTTL: 2h # Longer grace period for MinIO
rotation:
enabled: true
schedule: 1440h # 60 days
rotateGeneratedOnly: true
---
# ServiceAccount for password rotation job
apiVersion: v1
kind: ServiceAccount
metadata:
name: minio-rotator
namespace: minio
---
# Role to allow reading secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: minio-rotator
namespace: minio
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
resourceNames: ["minio-admin-secret", "minio-admin-secret-previous"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: minio-rotator
namespace: minio
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: minio-rotator
subjects:
- kind: ServiceAccount
name: minio-rotator
namespace: minio
---
# CronJob to detect rotation and update MinIO password
apiVersion: batch/v1
kind: CronJob
metadata:
name: minio-password-rotator
namespace: minio
spec:
# Run every 10 minutes to check for rotation
schedule: "*/10 * * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 3
template:
metadata:
annotations:
reloader.stakater.com/auto: "true"
spec:
serviceAccountName: minio-rotator
restartPolicy: OnFailure
containers:
- name: rotate-password
image: minio/mc:latest
env:
# Current credentials
- name: MC_HOST_myminio
value: "" # Will be set in script
- name: USERNAME
valueFrom:
secretKeyRef:
name: minio-admin-secret
key: username
- name: NEW_PASSWORD
valueFrom:
secretKeyRef:
name: minio-admin-secret
key: password
- name: ENDPOINT
valueFrom:
secretKeyRef:
name: minio-admin-secret
key: endpoint
# Previous credentials (for authentication)
- name: OLD_USERNAME
valueFrom:
secretKeyRef:
name: minio-admin-secret-previous
key: username
optional: true
- name: OLD_PASSWORD
valueFrom:
secretKeyRef:
name: minio-admin-secret-previous
key: password
optional: true
command:
- sh
- -c
- |
set -e
echo "=========================================="
echo "MinIO Password Rotation Check"
echo "=========================================="
echo "User: $USERNAME"
echo "Endpoint: $ENDPOINT"
echo ""
# Check if previous password exists (rotation happened)
if [ -z "$OLD_PASSWORD" ]; then
echo "No previous password found - no rotation needed"
exit 0
fi
# Check if passwords are different
if [ "$OLD_PASSWORD" = "$NEW_PASSWORD" ]; then
echo "Passwords are identical - no rotation needed"
exit 0
fi
echo "Rotation detected! Old and new passwords differ."
echo ""
# Test if current password in MinIO is the old one
echo "Testing authentication with OLD credentials..."
if mc alias set oldminio $ENDPOINT $OLD_USERNAME $OLD_PASSWORD > /dev/null 2>&1; then
echo "✓ Old credentials still active in MinIO"
echo ""
# Test admin access
if mc admin info oldminio > /dev/null 2>&1; then
echo "✓ Admin access confirmed with old credentials"
echo ""
echo "Updating to NEW password..."
# Change password using old credentials
if mc admin user password oldminio $USERNAME $NEW_PASSWORD; then
echo "✓ Password updated successfully!"
echo ""
# Verify new password works
echo "Verifying NEW password..."
if mc alias set newminio $ENDPOINT $USERNAME $NEW_PASSWORD > /dev/null 2>&1; then
if mc admin info newminio > /dev/null 2>&1; then
echo "✓ New password verified and working!"
echo ""
echo "=========================================="
echo "Rotation completed successfully"
echo "=========================================="
exit 0
else
echo "✗ ERROR: New credentials don't have admin access!"
exit 1
fi
else
echo "✗ ERROR: New password doesn't work!"
exit 1
fi
else
echo "✗ ERROR: Failed to update password"
exit 1
fi
else
echo "✗ ERROR: No admin access with old credentials"
exit 1
fi
else
echo "Old credentials don't work - checking if new credentials already active..."
if mc alias set newminio $ENDPOINT $USERNAME $NEW_PASSWORD > /dev/null 2>&1; then
if mc admin info newminio > /dev/null 2>&1; then
echo "✓ New credentials are already active in MinIO"
echo "Rotation was already completed"
exit 0
else
echo "✗ ERROR: New credentials exist but no admin access"
exit 1
fi
else
echo "✗ ERROR: Neither old nor new credentials work!"
echo "Manual intervention required"
exit 1
fi
fi
---
# Example: MinIO StatefulSet (simplified)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
namespace: minio
spec:
serviceName: minio
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
annotations:
# Note: MinIO doesn't auto-reload credentials from env vars
# You may need to restart pods manually or use a sidecar
reloader.stakater.com/auto: "false"
spec:
containers:
- name: minio
image: minio/minio:latest
args:
- server
- /data
- --console-address
- ":9001"
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-admin-secret
key: username
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-admin-secret
key: password
ports:
- containerPort: 9000
name: api
- containerPort: 9001
name: console
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
---
# Service for MinIO
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: minio
spec:
selector:
app: minio
ports:
- port: 9000
targetPort: 9000
name: api
- port: 9001
targetPort: 9001
name: console
---
# How the rotation flow works:
#
# Day 0: Initial setup
# - ManagedSecret creates minio-admin-secret with generated password
# - MinIO starts with these credentials
# - No minio-admin-secret-previous exists yet
# - CronJob runs every 10 minutes, finds no previous password, does nothing
#
# Day 60: Rotation triggered
# 1. ManagedSecret operator:
# - Generates NEW password
# - Updates Vault with new password
# - Creates minio-admin-secret-previous with OLD password
# - Updates minio-admin-secret with NEW password
#
# 2. Within 10 minutes, CronJob runs:
# - Detects minio-admin-secret-previous exists
# - Compares OLD_PASSWORD vs NEW_PASSWORD (different!)
# - Authenticates to MinIO with OLD credentials
# - Runs: mc admin user password myminio admin new_password
# - Verifies new password works
# - Job succeeds
#
# 3. After 2 hours (previousVersionTTL):
# - ManagedSecret operator deletes minio-admin-secret-previous
# - CronJob future runs find no previous password, do nothing
#
# 4. MinIO pods:
# - IMPORTANT: MinIO does NOT reload credentials from env vars
# - You need to manually restart MinIO pods after rotation
# - Or use a sidecar to monitor and trigger restart
# - Consider NOT using reloader.stakater.com/auto for MinIO
---
# Alternative: Sidecar to handle MinIO restart
# Add this to the MinIO StatefulSet if you want automatic restarts
---
# sidecar:
# - name: credential-watcher
# image: bitnami/kubectl:latest
# command:
# - sh
# - -c
# - |
# LAST_VERSION=""
# while true; do
# CURRENT_VERSION=$(kubectl get secret minio-admin-secret -n minio -o jsonpath='{.metadata.resourceVersion}')
# if [ -n "$LAST_VERSION" ] && [ "$LAST_VERSION" != "$CURRENT_VERSION" ]; then
# echo "Credentials changed! Signaling MinIO to restart..."
# # Send SIGTERM to MinIO process to trigger graceful shutdown
# killall minio
# fi
# LAST_VERSION=$CURRENT_VERSION
# sleep 30
# done
---
# Manual rotation testing:
#
# # Check current state
# kubectl get secret minio-admin-secret -n minio -o yaml
# kubectl get secret minio-admin-secret-previous -n minio -o yaml
#
# # Force rotation
# kubectl annotate managedsecret minio-admin -n minio reconcile="$(date +%s)" --overwrite
#
# # Watch the rotation
# kubectl get secrets -n minio -w
#
# # Check CronJob executes
# kubectl get jobs -n minio -w
#
# # View rotation logs
# kubectl logs -n minio -l job-name=minio-password-rotator-XXXXX
#
# # Test MinIO access with new credentials
# NEW_USER=$(kubectl get secret minio-admin-secret -n minio -o jsonpath='{.data.username}' | base64 -d)
# NEW_PASS=$(kubectl get secret minio-admin-secret -n minio -o jsonpath='{.data.password}' | base64 -d)
# mc alias set testminio http://minio.minio.svc.cluster.local:9000 $NEW_USER $NEW_PASS
# mc admin info testminio
---
# Important Notes:
#
# 1. MinIO credential reload:
# - Unlike PostgreSQL, MinIO does NOT automatically reload credentials
# - After rotation, you must restart MinIO pods
# - Consider using a sidecar or manual restart workflow
#
# 2. High Availability:
# - For HA MinIO clusters, all nodes share the same root credentials
# - Rotation updates credentials cluster-wide
# - Rolling restart required for all nodes
#
# 3. Grace Period:
# - 2 hour previousVersionTTL allows time for manual intervention
# - Adjust based on your operational procedures
#
# 4. Monitoring:
# - Monitor CronJob success/failure
# - Alert on rotation failures
# - Track when MinIO pods were last restarted