Skip to content

Kubernetes — alert when a Secret has not rotated in N days

Compliance mandates rotating k8s Secrets (DB passwords, API tokens) every 90 days. Nobody auto-rotates, Secrets live since cluster creation. The auditor finds it first.

Recipe

bash
#!/usr/bin/env bash
# /etc/cron.d/k8s-secret-age
# 0 7 * * * root /opt/k8s-secret-age.sh

CONTEXT=${KUBE_CONTEXT:-prod}
NS=${NS:-prod}
MAX_DAYS=${MAX_DAYS:-90}
EXCLUDE_TYPE='kubernetes.io/service-account-token|helm.sh/release.v1'

NOW=$(date -u +%s)
STALE=$(kubectl --context "$CONTEXT" -n "$NS" get secrets -o json \
  | jq --argjson now "$NOW" --argjson max "$MAX_DAYS" --arg ex "$EXCLUDE_TYPE" '
      [.items[] |
       select(.type | test($ex) | not) |
       {name: .metadata.name,
        days: (($now - (.metadata.creationTimestamp | fromdateiso8601)) / 86400 | floor)} |
       select(.days > $max)]')

COUNT=$(echo "$STALE" | jq 'length')

if [ "${COUNT:-0}" -gt 0 ]; then
  EXAMPLES=$(echo "$STALE" | jq -r '.[] | "\(.name)=\(.days)d"' | head -5 | tr '\n' ',')
  curl -fsS "$HEARTBEAT_URL" --data-urlencode "stale_secrets=$COUNT,examples=$EXAMPLES"
  exit 2
fi
echo "OK (no Secrets older than ${MAX_DAYS}d)"

Same thing in Enterno.io

Wire to an Enterno heartbeat with 365-day retention — keeps a history of "what and when we rotated" for the compliance audit.

Set up HTTP monitor → ← All recipes

Related recipes