A tiny container that snapshots your step-ca Badger database, reads certificates via step-badger, and emails an HTML report only when there are expired or soon-to-expire certificates.
  • Python 82.2%
  • Shell 10%
  • Dockerfile 7.8%
Find a file
2025-09-05 14:53:38 +02:00
docker-compose.yml Version 1.0.0 2025-09-05 14:53:38 +02:00
Dockerfile Version 1.0.0 2025-09-05 14:53:38 +02:00
entrypoint.sh Version 1.0.0 2025-09-05 14:53:38 +02:00
mailer.py Version 1.0.0 2025-09-05 14:53:38 +02:00
README.md Version 1.0.0 2025-09-05 14:53:38 +02:00
step-badger_6.5.0 Version 1.0.0 2025-09-05 14:53:38 +02:00

Step-CA Expiry Mailer

A tiny container that snapshots your step-ca Badger database, reads certificates via step-badger, and emails an HTML report only when there are expired or soon-to-expire certificates.

  • Uses the same snapshot logic and field parsing as our BookStack sync.
  • Runs on a cron schedule (weekly by default).
  • Works with no-auth SMTP relays (port 25) and authenticated SMTP (STARTTLS 587 or SMTPS 465).
  • Safe to run: mounts your Step-CA DB read-only and snapshots it to /tmp.

Quick Start

1) Build

Place your step-badger_6.5.0 binary in the build context next to the Dockerfile.

docker build -t stepca-expiry-mailer:latest .

You can rename the binary; update COPY in the Dockerfile if you do.

2) Run (no-auth relay on port 25)

docker run -d --name stepca-expiry-mailer \
  -e TZ="Europe/Zurich" \
  -e CRON_SCHEDULE="0 6 * * 1" \
  -e STEP_DB_DIR="/step/db" \
  -e DB_SNAPSHOT="true" \
  -e TMP_BASE="/tmp" \
  -e THRESHOLD_DAYS="30" \
  -e INCLUDE_EXPIRED="true" \
  -e SMTP_HOST="mail-relay.local" \
  -e SMTP_PORT="25" \
  -e SMTP_FROM="stepca@yourdomain.tld" \
  -e SMTP_TO="ops@yourdomain.tld,admins@yourdomain.tld" \
  -v /path/to/step-ca/db:/step:ro \
  --restart unless-stopped \
  stepca-expiry-mailer:latest

3) Run (authenticated, STARTTLS)

docker run -d --name stepca-expiry-mailer \
  -e SMTP_HOST="smtp.provider.tld" \
  -e SMTP_PORT="587" \
  -e SMTP_STARTTLS="true" \
  -e SMTP_USERNAME="user@provider.tld" \
  -e SMTP_PASSWORD="supersecret" \
  -e SMTP_FROM="stepca@provider.tld" \
  -e SMTP_TO="ops@provider.tld" \
  -v /path/to/step-ca/db:/step:ro \
  --restart unless-stopped \
  stepca-expiry-mailer:latest

4) Docker Compose

services:
  stepca-expiry-mailer:
    image: stepca-expiry-mailer:latest
    environment:
      TZ: Europe/Zurich
      CRON_SCHEDULE: "0 6 * * 1"      # every Monday 06:00
      STEP_DB_DIR: /step/db
      DB_SNAPSHOT: "true"
      TMP_BASE: "/tmp"
      THRESHOLD_DAYS: "30"
      INCLUDE_EXPIRED: "true"
      SMTP_HOST: mail-relay.local
      SMTP_PORT: "25"
      SMTP_FROM: stepca@yourdomain.tld
      SMTP_TO: ops@yourdomain.tld
      SMTP_STARTTLS: "false"
      EMAIL_SUBJECT_PREFIX: "[Step-CA]"
      TABLE_TITLE: "Certificates expiring soon"
    volumes:
      - /path/to/step-ca/db:/step:ro
    restart: unless-stopped

How It Works

  1. Snapshot: The container creates a temporary copy of your Badger DB (DB_SNAPSHOT=true). This avoids the Badger directory lock.

  2. Read: It runs step-badger x509Certs <SNAPSHOT> --emit json and parses notBefore/notAfter like the BookStack sync.

  3. Filter: Certificates are included if:

    • days_left <= THRESHOLD_DAYS, or
    • already expired and INCLUDE_EXPIRED=true.
  4. Email: If and only if there are matching entries, it sends a nicely formatted HTML email (plus a plain-text alternative).


Configuration

Required (for email)

  • SMTP_HOST — SMTP server hostname or IP.
  • SMTP_FROM — sender address (e.g., stepca@yourdomain.tld).
  • SMTP_TO — comma-separated recipients list.

Common

  • CRON_SCHEDULE — cron syntax for when to run (default: 0 6 * * 1 → Monday 06:00).
  • STEP_DB_DIR — path inside the container to your Step-CA DB root (default: /step/db).
  • DB_SNAPSHOTtrue|false (default: true) — snapshot DB into TMP_BASE before reading.
  • TMP_BASE — base directory for snapshots (default: /tmp).
  • THRESHOLD_DAYS — days left threshold for “soon to expire” (default: 30).
  • INCLUDE_EXPIRED — include already expired certs (default: true).
  • EMAIL_SUBJECT_PREFIX — subject prefix (default: [Step-CA]).
  • TABLE_TITLE — headline shown in the email (default: Certificates expiring soon).

SMTP

  • SMTP_PORT25 (plain/no-auth allowed), 587 (STARTTLS), or 465 (SMTPS).
  • SMTP_STARTTLStrue to use STARTTLS on SMTP_PORT (typically 587).
  • SMTP_USERNAME, SMTP_PASSWORD — set when your SMTP requires auth.

step-badger (advanced)

  • STEP_BADGER_CMD — default /usr/local/bin/step-badger x509Certs
  • STEP_BADGER_ARGS — default --emit json

The image assumes your step-badger has the x509Certs command. If your binary differs, adjust STEP_BADGER_CMD/ARGS.


Permissions & Security

  • The container runs as root by default so it can:

    • read the mounted Badger DB (which is often root-owned),
    • write a private cron file under /app/crontabs.
  • Mount your Step-CA directory read-only: -v /path/to/db:/step:ro.

  • If you must run non-root, grant read+execute on the host to the container UID/GID (ACLs or group membership), then switch USER in the Dockerfile and keep crond -c /app/crontabs.


Troubleshooting

  • Permission denied / Badger lock Ensure the host path is mounted read-only and the container runs as root, or adjust host ACLs if you run non-root. Snapshotting avoids the directory lock.

  • “No parsable certificates with not_after” Means step-badger JSON didnt include notAfter fields. Make sure youre using a compatible step-badger build with x509Certs --emit json.

  • No emails arrive Check SMTP_HOST/PORT and whether your relay requires auth or STARTTLS/SMTPS. Use:

    • no-auth internal relay: SMTP_PORT=25, SMTP_STARTTLS=false, no username/password.
    • STARTTLS: SMTP_PORT=587, SMTP_STARTTLS=true.
    • SMTPS: SMTP_PORT=465 (no SMTP_STARTTLS), often requires auth.
  • Cron didnt run Verify CRON_SCHEDULE syntax and container logs. Cron is started as crond -f -l 8 -c /app/crontabs.



Author


License

MIT License free for commercial and private use.


Credits

  • step-badger by Smallstep community.
  • This mailer uses the same snapshot & parsing approach as our BookStack sync for step-ca.

Build

Multi Platform Builder

docker buildx create --use --platform=linux/arm64,linux/amd64 --name multi-platform-builder
docker buildx inspect --bootstrap

Build

docker buildx build --platform linux/amd64,linux/arm64 --push --provenance=mode=max --sbom=true --tag onesystems/step-ca-to-e-mail:v1.0.0 --tag onesystems/step-ca-to-e-mail:latest --file Dockerfile .