Add server ops certificate expiry check

This commit is contained in:
Walusimbi Silver
2026-04-24 10:36:33 +03:00
commit 785c13119b
3 changed files with 173 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
set -uo pipefail
CERT_DIR="${CERT_DIR:-/etc/letsencrypt/live}"
EXPIRY_DAYS="${EXPIRY_DAYS:-14}"
NTFY_URL="${NTFY_URL:-https://ntfy.silverwal.com/silver}"
NTFY_TITLE="${NTFY_TITLE:-SSL certificate warning}"
NTFY_PRIORITY="${NTFY_PRIORITY:-high}"
NTFY_TAGS="${NTFY_TAGS:-warning,lock}"
NTFY_TOKEN="${NTFY_TOKEN:-}"
ALERT_ON_NO_CERTS="${ALERT_ON_NO_CERTS:-true}"
host="$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo "unknown-host")"
now_epoch="$(date +%s)"
threshold_seconds=$((EXPIRY_DAYS * 86400))
send_alert() {
local message="$1"
local curl_args=(
-fsS
-H "Title: ${NTFY_TITLE}"
-H "Priority: ${NTFY_PRIORITY}"
-H "Tags: ${NTFY_TAGS}"
)
if [[ -n "${NTFY_TOKEN}" ]]; then
curl_args+=(-H "Authorization: Bearer ${NTFY_TOKEN}")
fi
curl "${curl_args[@]}" --data-binary "${message}" "${NTFY_URL}" >/dev/null
}
fail() {
local message="$1"
send_alert "${message}" || echo "Failed to send ntfy alert to ${NTFY_URL}" >&2
echo "${message}" >&2
exit 1
}
if ! command -v openssl >/dev/null 2>&1; then
fail "SSL certificate check failed on ${host}: openssl is not installed."
fi
if ! command -v curl >/dev/null 2>&1; then
echo "SSL certificate check failed on ${host}: curl is not installed." >&2
exit 1
fi
if [[ ! -d "${CERT_DIR}" ]]; then
fail "SSL certificate check failed on ${host}: certificate directory ${CERT_DIR} does not exist."
fi
warnings=()
cert_count=0
while IFS= read -r -d '' cert_path; do
cert_count=$((cert_count + 1))
cert_name="$(basename "$(dirname "${cert_path}")")"
enddate_line="$(openssl x509 -in "${cert_path}" -noout -enddate 2>/dev/null)"
if [[ -z "${enddate_line}" ]]; then
warnings+=("${cert_name}: could not read expiry date from ${cert_path}")
continue
fi
not_after="${enddate_line#notAfter=}"
expiry_epoch="$(date -d "${not_after}" +%s 2>/dev/null)"
if [[ -z "${expiry_epoch}" ]]; then
warnings+=("${cert_name}: could not parse expiry date '${not_after}'")
continue
fi
seconds_left=$((expiry_epoch - now_epoch))
days_left=$((seconds_left / 86400))
if (( seconds_left < 0 )); then
warnings+=("${cert_name}: EXPIRED on ${not_after}")
elif ! openssl x509 -checkend "${threshold_seconds}" -noout -in "${cert_path}" >/dev/null 2>&1; then
warnings+=("${cert_name}: expires in ${days_left} day(s), on ${not_after}")
fi
done < <(find "${CERT_DIR}" -mindepth 2 -maxdepth 2 \( -type f -o -type l \) -name cert.pem -print0)
if (( cert_count == 0 )); then
if [[ "${ALERT_ON_NO_CERTS}" == "true" ]]; then
fail "SSL certificate check found no cert.pem files under ${CERT_DIR} on ${host}."
fi
echo "No certificates found under ${CERT_DIR}; no alert sent."
exit 0
fi
if (( ${#warnings[@]} > 0 )); then
message="SSL certificate expiry warning on ${host}. Threshold: ${EXPIRY_DAYS} day(s).
$(printf '%s\n' "${warnings[@]}")"
send_alert "${message}" || echo "Failed to send ntfy alert to ${NTFY_URL}" >&2
echo "${message}" >&2
exit 1
fi
echo "All ${cert_count} certificate(s) are valid for more than ${EXPIRY_DAYS} day(s)."