Skip to main content

Continuous TLS certificate monitoring and alerting, powered by certinspect.

Project description

certminder

CI PyPI Python License: MIT

Scheduled checks · Expiry & revocation alerts · Fingerprint change detection · Deduplicated notifications · Console / email / Slack / webhook · Prometheus metrics

PyPI · Quick start · Configure · Alerts · Prometheus · Deployment · Issues

Continuous TLS certificate monitoring and alerting — the watch loop on top of certinspect.

certinspect tells you what a certificate looks like right now. certminder runs it on a schedule, remembers what it saw last time, and alerts you when a certificate is about to expire, gets revoked, changes fingerprint, or becomes unreachable.

Why a separate tool

certminder never re-implements TLS or X.509 logic — that all lives in certinspect. certminder adds only what a monitor needs:

  • a schedule (run once for cron, or loop as a daemon),
  • state memory to detect changes between runs,
  • deduplicated alerts (notify once per condition, recover once),
  • pluggable notifiers (console, email, Slack, generic webhook),
  • optional Prometheus metrics for the node_exporter textfile collector.

Install

pip install certminder       # pulls in certinspect automatically
# or from source:
pip install -e '.[dev]'

Quick start

# inspect a single host ad hoc
certminder check example.com

# copy and edit the sample config, then:
certminder once -c certminder.yml     # one cycle — ideal for cron
certminder run  -c certminder.yml     # run continuously as a daemon

Configure

Everything is driven by a YAML file (see certminder.example.yml):

interval: 6h
state_file: ~/.certminder/state.json
defaults:
  verify: true
  days: 30
  critical_days: 15
notifiers:
  - type: console
  - type: slack
    webhook_url: "https://hooks.slack.com/services/XXX/YYY/ZZZ"
  - type: email
    host: smtp.example.com
    port: 587
    username: alerts@example.com
    password: CHANGE_ME
    from_addr: alerts@example.com
    to: [ops@example.com]
targets:
  - host: example.com
  - host: api.example.com
    port: 8443
  - host: mail.example.com
    starttls: smtp

What it alerts on

Event Severity Trigger
EXPIRING warning within --days of expiry
CRITICAL / EXPIRED critical within critical_days, or already expired
REVOKED critical OCSP/CRL says revoked (needs verify)
CHAIN_UNTRUSTED critical chain fails to validate
HOSTNAME_MISMATCH critical cert does not match the hostname
FINGERPRINT_CHANGED warning fingerprint differs from last cycle
UNREACHABLE critical host/handshake failed
RECOVERED info a prior problem cleared

Each condition alerts once; certminder remembers it and stays quiet until it changes, then sends a single recovery notice.

Exit codes (once)

  • 0 — no events this cycle
  • 1 — at least one event was emitted
  • 2 — configuration error

Add --json to once to print a machine-readable summary of the cycle (one entry per target plus the events) to stdout, handy for piping:

certminder once -c certminder.yml --json | jq '.targets[] | {target, status, days_to_expire}'

Prometheus metrics

Set prometheus_file in the config to a path inside the node_exporter textfile collector directory. certminder rewrites it atomically at the end of every cycle:

certminder_certificate_expiry_days{target="example.com:443",host="example.com",port="443",status="VALID"} 42
certminder_certificate_valid{...} 1
certminder_target_up{...} 1
certminder_last_run_timestamp_seconds 1700000000

Deployment

Ready-to-use units live in deploy/ plus a Dockerfile:

  • systemd timercertminder.service + certminder.timer run one cycle on a schedule (cron-style, recommended).
  • systemd daemoncertminder-daemon.service runs the run loop under supervision.
  • croncertminder.cron for hosts without systemd timers.
  • Docker — multi-stage build; mount your certminder.yml at /etc/certminder/certminder.yml and a volume at /var/lib/certminder.

Docker

Build the image:

docker build -t certminder .

Run a single cycle (cron-style — config and state mounted from the host):

docker run --rm \
  -v "$PWD/certminder.yml:/etc/certminder/certminder.yml:ro" \
  -v certminder-state:/var/lib/certminder \
  certminder once -c /etc/certminder/certminder.yml

Run continuously as a daemon (this is the default CMD):

docker run -d --name certminder \
  --restart unless-stopped \
  -v "$PWD/certminder.yml:/etc/certminder/certminder.yml:ro" \
  -v certminder-state:/var/lib/certminder \
  certminder

The named volume certminder-state persists state.json and the Prometheus file across restarts — keep it so deduplication survives container recreation. The console notifier prints to stdout; read it with docker logs -f certminder (timestamps from Docker with -t, or set timestamp: true on the console notifier). The container runs in UTC.

Docker Compose

services:
  certminder:
    build: . # or: image: certminder
    container_name: certminder
    restart: unless-stopped
    command: run -c /etc/certminder/certminder.yml
    volumes:
      - ./certminder.yml:/etc/certminder/certminder.yml:ro
      - certminder-state:/var/lib/certminder
    logging: # cap the daemon's logs so they don't grow without bound
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

volumes:
  certminder-state:
docker compose up -d            # build (if needed) and start the daemon
docker compose logs -f certminder
docker compose up -d --build    # rebuild after upgrading certminder/certinspect
docker compose down             # stop and remove

Development

ruff check . && ruff format --check .
pytest -q

Tests mock the certinspect subprocess, so the suite never touches the network.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

certminder-0.3.0.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

certminder-0.3.0-py3-none-any.whl (22.1 kB view details)

Uploaded Python 3

File details

Details for the file certminder-0.3.0.tar.gz.

File metadata

  • Download URL: certminder-0.3.0.tar.gz
  • Upload date:
  • Size: 24.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for certminder-0.3.0.tar.gz
Algorithm Hash digest
SHA256 79336bd563a15f5a591c38806cebad98c67b71b4adac8841f554d58cd1acea2a
MD5 d2e9d36fc5ef5804d9af01cef424a932
BLAKE2b-256 ce2ea0fba593202d0c67616f50d92667eae6ede53d8f1b2322060faf9b18b285

See more details on using hashes here.

Provenance

The following attestation bundles were made for certminder-0.3.0.tar.gz:

Publisher: publish.yml on mangrisano/certminder

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file certminder-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: certminder-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 22.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for certminder-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fa7237c892dc451841603e76ed0c425fec596cf18d1f85238da404e8d70c7367
MD5 5cf86d64b426fa65ee1ea7ac9bbb90bd
BLAKE2b-256 30c4aafa04e77fabb0991b041248f263649734394e79570fb05074a4ad777805

See more details on using hashes here.

Provenance

The following attestation bundles were made for certminder-0.3.0-py3-none-any.whl:

Publisher: publish.yml on mangrisano/certminder

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page