| name | secrets-certificate-manager |
| description | Use this skill to manage secrets, API keys, connection strings, and TLS certificates across cloud secret stores and Kubernetes clusters. Triggers: any request to rotate a secret, renew a certificate, audit secret access, detect expiring certs, inject secrets into workloads, set up cert-manager, migrate secrets between environments, or detect hardcoded credentials in infrastructure or application code.
|
| tools | ["bash","computer"] |
Secrets & Certificate Manager Skill
Automate the full secrets and certificate lifecycle: centralised storage,
automatic rotation, expiry monitoring, workload injection, and audit trail —
across Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, and Kubernetes
Secrets with cert-manager.
Secret Store Backends
| Backend | Auth | CLI / SDK |
|---|
| Azure Key Vault | Managed Identity / SP | az keyvault |
| AWS Secrets Manager | IAM Role / IRSA | aws secretsmanager |
| HashiCorp Vault | AppRole / K8s Auth | vault |
| Kubernetes Secrets | RBAC / Service Account | kubectl |
| GCP Secret Manager | Workload Identity | gcloud secrets |
Secret Lifecycle
Create / Update a Secret
az keyvault secret set \
--vault-name "$VAULT_NAME" \
--name "$SECRET_NAME" \
--value "$SECRET_VALUE" \
--expires "$(date -d '+90 days' +%Y-%m-%dT%H:%M:%SZ)" \
--tags "owner=$OWNER" "service=$SERVICE" "rotation=auto"
aws secretsmanager create-secret \
--name "$SECRET_NAME" \
--secret-string "$SECRET_VALUE" \
--tags Key=owner,Value="$OWNER" Key=service,Value="$SERVICE"
echo -n "$SECRET_VALUE" | \
kubectl create secret generic "$SECRET_NAME" \
--from-literal=value=/dev/stdin \
--dry-run=client -o yaml | \
kubeseal --controller-namespace sealed-secrets > sealed-secret.yaml
Read a Secret (for diagnosis only — never log values)
az keyvault secret show --vault-name "$VAULT_NAME" --name "$SECRET_NAME" \
--query "{name:name, expires:attributes.expires, enabled:attributes.enabled}" \
--output json
Secret Rotation
Automatic Rotation Workflow
rotate_secret() {
local secret_name=$1
local generator=$2
NEW_VALUE=$(eval "$generator")
validate_secret "$secret_name" "$NEW_VALUE" || {
echo "Validation failed — aborting rotation"
return 1
}
az keyvault secret set \
--vault-name "$VAULT_NAME" \
--name "$secret_name" \
--value "$NEW_VALUE"
kubectl rollout restart deployment \
-l "secret=$secret_name" -n "$NAMESPACE"
kubectl rollout status deployment \
-l "secret=$secret_name" -n "$NAMESPACE" --timeout=5m
az keyvault secret set-attributes \
--vault-name "$VAULT_NAME" \
--name "$secret_name" \
--version "$OLD_VERSION" \
--enabled false
echo "Rotation complete for $secret_name"
}
Rotation Schedule
| Secret type | Rotation interval | Auto-rotatable |
|---|
| Database passwords | 90 days | ✅ |
| API keys (internal services) | 90 days | ✅ |
| Service principal credentials | 180 days | ✅ |
| Customer-facing API keys | On-demand | ⚠️ Notify first |
| SSH keys | 365 days | ✅ |
| JWT signing keys | 180 days | ✅ |
| Encryption keys (KEK) | 365 days | ⚠️ Needs approval |
Certificate Management (cert-manager)
Install cert-manager
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--set installCRDs=true \
--set prometheus.enabled=true
Create a ClusterIssuer (Let's Encrypt + Azure DNS)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@company.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- dns01:
azureDNS:
subscriptionID: $SUBSCRIPTION_ID
resourceGroupName: $DNS_RG
hostedZoneName: $ZONE
managedIdentity:
clientID: $IDENTITY_CLIENT_ID
Create a Certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ${TENANT_ID}-tls
namespace: $NAMESPACE
spec:
secretName: ${TENANT_ID}-tls-secret
dnsNames:
- ${TENANT_ID}.app.company.com
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
duration: 2160h
renewBefore: 360h
Expiry Monitoring
kubectl get certificates -A -o json | \
jq -r '.items[] | {
namespace: .metadata.namespace,
name: .metadata.name,
expiry: .status.notAfter,
ready: .status.conditions[]? | select(.type=="Ready") | .status
}'
kubectl get certificates -A -o json | \
jq -r '.items[] | select(
(.status.notAfter | fromdateiso8601) < (now + 30*86400)
) | "\(.metadata.namespace)/\(.metadata.name) expires \(.status.notAfter)"'
Hardcoded Secret Detection
gitleaks detect --source . \
--report-format json \
--report-path reports/secrets.json
kubectl get secrets -A -o json | \
jq -r '.items[] | .data // {} | to_entries[] |
"\(.key): \(.value | @base64d)"' | \
grep -iE "password|key|token|secret|credential" | \
grep -v "^#"
On detection:
- Block PR / commit
- Classify: API key, DB password, cert private key, etc.
- Auto-rotate if integration exists
- Notify committer + security team
- Log to audit trail
Kubernetes Secrets Best Practices
Enforce these standards via OPA/Gatekeeper:
- All Secrets stored in Key Vault / Secrets Manager, not plaintext YAML
- Secrets synced to pods via External Secrets Operator (ESO) or CSI driver
- No
env injection of secrets (use mounted volumes or ESO refs)
imagePullSecrets rotated every 180 days
- Secret access audited via K8s audit log → SIEM
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: ${SERVICE}-secrets
namespace: $NAMESPACE
spec:
refreshInterval: 1h
secretStoreRef:
name: azure-keyvault
kind: ClusterSecretStore
target:
name: ${SERVICE}-secrets
data:
- secretKey: db-password
remoteRef:
key: ${SERVICE}-db-password
Audit & Compliance
az monitor activity-log list \
--start-time "$(date -d '-7 days' +%Y-%m-%dT%H:%M:%SZ)" \
--namespace Microsoft.KeyVault \
--query "[?operationName.value=='Microsoft.KeyVault/vaults/secrets/read'].
{time:eventTimestamp, caller:caller, secret:resourceId}" \
--output table
Examples
- "Rotate all database passwords that haven't been rotated in 90+ days"
- "Show me all TLS certificates expiring in the next 30 days"
- "Set up cert-manager for tenant-42's namespace with Let's Encrypt"
- "Audit who accessed production secrets in Key Vault last week"
- "Scan the platform codebase for any hardcoded credentials"
- "Migrate secrets from the old Key Vault to the new one after the subscription move"
Output Format
{
"operation": "rotate|create|audit|scan|expiry_check",
"secrets_affected": [],
"certificates_expiring_soon": [],
"hardcoded_secrets_found": 0,
"rotation_status": "success|partial|failure",
"audit_events": [],
"next_rotation_due": "ISO8601"
}