#!/bin/sh # Extracts a PEM cert/key pair for one domain out of Traefik's acme.json — # Traefik keeps its certificates there, so a PEM extraction step is required. # Run this on the host, after Traefik has issued or renewed the certificate, and # again on a schedule (cron/systemd timer) since acme.json is not itself watched # by SelfPost/Postfix. # # Requires jq. Usage: ./extract-cert.sh # # The layout is not documented (doc.traefik.io/traefik/v3.1/https/acme/ says # only "JSON file"); read in Traefik v3.1.0: pkg/provider/acme/local_store.go # keeps map[resolver name]*StoredData, store.go StoredData{Account, # Certificates}, provider.go Certificate{domain, certificate, key} with the PEM # as []byte - base64 in JSON (Go encoding/json). "le" is the resolver # docker-compose.traefik.yml names. set -eu ACME_JSON="${1:?path to acme.json}" DOMAIN="${2:?domain name, e.g. mail.example.com}" OUT_DIR="${3:?output directory, e.g. ./extracted-certs}" mkdir -p "$OUT_DIR" jq -r --arg domain "$DOMAIN" ' .le.Certificates[] | select(.domain.main == $domain) | .certificate' "$ACME_JSON" | base64 -d > "$OUT_DIR/fullchain.pem" jq -r --arg domain "$DOMAIN" ' .le.Certificates[] | select(.domain.main == $domain) | .key' "$ACME_JSON" | base64 -d > "$OUT_DIR/privkey.pem" chmod 0640 "$OUT_DIR/fullchain.pem" "$OUT_DIR/privkey.pem" echo "extracted $DOMAIN to $OUT_DIR/{fullchain,privkey}.pem"