- Python 82.2%
- Shell 10%
- Dockerfile 7.8%
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| mailer.py | ||
| README.md | ||
| step-badger_6.5.0 | ||
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
COPYin 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
-
Snapshot: The container creates a temporary copy of your Badger DB (
DB_SNAPSHOT=true). This avoids the Badger directory lock. -
Read: It runs
step-badger x509Certs <SNAPSHOT> --emit jsonand parsesnotBefore/notAfterlike the BookStack sync. -
Filter: Certificates are included if:
days_left <= THRESHOLD_DAYS, or- already expired and
INCLUDE_EXPIRED=true.
-
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_SNAPSHOT—true|false(default:true) — snapshot DB intoTMP_BASEbefore 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_PORT—25(plain/no-auth allowed),587(STARTTLS), or465(SMTPS).SMTP_STARTTLS—trueto use STARTTLS onSMTP_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 x509CertsSTEP_BADGER_ARGS— default--emit json
The image assumes your
step-badgerhas thex509Certscommand. If your binary differs, adjustSTEP_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
USERin the Dockerfile and keepcrond -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-badgerJSON didn’t includenotAfterfields. Make sure you’re using a compatiblestep-badgerbuild withx509Certs --emit json. -
No emails arrive Check
SMTP_HOST/PORTand 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(noSMTP_STARTTLS), often requires auth.
- no-auth internal relay:
-
Cron didn’t run Verify
CRON_SCHEDULEsyntax and container logs. Cron is started ascrond -f -l 8 -c /app/crontabs.
Author
- Michael Kleger
OneSystems GmbH
mkleger@onesystems.ch
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 .