Schedule icon
Commands icon
Docker icon
Script icon
If icon
SlackIncomingWebhook icon
Set icon

CertWatch - Kubernetes TLS certificate expiry watcher

A Kestra blueprint that watches Kubernetes TLS secrets, detects certificates expiring soon, and alerts on Slack without spamming.

Categories
Infrastructure

How it works

  1. list_secrets (io.kestra.plugin.scripts.shell.Commands) runs kubectl get secrets in the target namespace and saves the full secret list as JSON. It authenticates with a client certificate and key passed as Kestra secrets, the same pattern the Kubernetes gallery blueprints use.
  2. find_trouble (io.kestra.plugin.scripts.python.Script) reads the remembered state from the KV store, then checks every secret of type kubernetes.io/tls. Each certificate is decoded and passed to openssl, which reports its expiry date. Anything expiring within the expiry_days input counts as troubled. Anything already alerted on is dropped.
  3. maybe_alert (io.kestra.plugin.core.flow.If) runs only when there is something new to report, and send_alert posts the expiring certificates to Slack with the days left and a kubectl command to inspect each one.
  4. save_state (io.kestra.plugin.core.kv.Set) writes back the new list of troubled secret names as one JSON value. Renewed certificates drop out on their own, so a future expiry alerts again.

What you get

  • A daily certificate expiry check for any namespace, no agent to install inside the cluster.
  • Slack alerts only for new trouble: which certificate, how many days are left, and the kubectl command to look at it.
  • No alert spam: the KV-backed memory remembers who was already reported, and forgets certificates that got renewed.
  • Quiet runs when nothing is expiring soon.

Who it's for

Anyone running TLS-terminated workloads on Kubernetes who has been bitten by an expired certificate at 2am. Works on any cluster you can reach with a client certificate: minikube, kind, managed clusters, and cert-manager managed secrets.

Why orchestrate this with Kestra

A shell script on a cron server can check expiry dates, but then you need somewhere to keep the "already alerted" state, somewhere to hold the secrets, and a place to see the run history when an alert looks wrong. Kestra gives you all three: the KV store is the memory, secrets stay out of the YAML, and every run keeps its logs and outputs where you can inspect them.

Prerequisites

  • A Kubernetes cluster reachable from where Kestra runs, with a client certificate and key for authentication.
  • Kestra with the Docker task runner available (used for the kubectl and Python steps).
  • A Slack Incoming Webhook URL.

Secrets

Secret What it is
CLIENT_CRT base64-encoded client certificate for the cluster
CLIENT_KEY base64-encoded client key for the cluster
MASTERURL base64-encoded Kubernetes API server URL, reachable from inside the Kestra container
SLACK_WEBHOOK base64-encoded Slack Incoming Webhook URL

In Kestra OSS, secrets come from environment variables prefixed with SECRET_ and must be base64-encoded.

Quick start

  1. Start Kestra with the four secrets above as SECRET_ environment variables.
  2. Import this flow and run it with the namespace you want to watch.
  3. Create a test TLS secret with a short-lived certificate and run again: the Slack alert lists the secret and the days left.
  4. Run once more without changing anything: no second alert. Renew the certificate and run again: quiet, and the state is cleared.

How to extend

  • Watch ingress hostnames too by resolving TLS secrets referenced from Ingress objects.
  • Lower the schedule to every hour for faster detection by changing the cron trigger.
  • Deploy one copy of the flow per namespace you want to watch, changing only the namespace input.
  • Route alerts to PagerDuty or Opsgenie by swapping the Slack task for another notification task.

Inputs

Input Type Default What it does
namespace STRING default the namespace whose TLS secrets to watch
expiry_days INT 14 a certificate expiring within this many days counts as troubled
slack_webhook STRING (required) Incoming webhook URL of the Slack channel for the alerts

Links

Pitfalls

  • Only secrets of type kubernetes.io/tls are checked. Certificates stored as opaque secrets or outside Kubernetes are invisible to this flow.
  • The expiry check reads the tls.crt field only. If a secret has a full chain, the leaf certificate's date is what counts.
  • Make sure the MASTERURL is reachable from inside the Kestra container. localhost inside the container is not your host: use host.docker.internal when Kestra runs in Docker on the same machine as the cluster.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.