#!/bin/sh
# check-ssl.sh: warn before TLS certificates expire, and catch chain or hostname errors.
# Usage: ./check-ssl.sh example.com www.example.com shop.example.com:8443
# Exit code: 0 all OK, 1 at least one WARN, 2 at least one CRIT.

WARN_DAYS=${WARN_DAYS:-30}
CRIT_DAYS=${CRIT_DAYS:-7}
status=0

for target in "$@"; do
  host=${target%%:*}
  port=${target#*:}; [ "$port" = "$target" ] && port=443

  # Fetch the certificate the server sends, verifying the chain and the hostname.
  out=$(echo | openssl s_client -connect "$host:$port" -servername "$host" \
        -verify_hostname "$host" -verify_return_error 2>&1)
  cert=$(printf '%s\n' "$out" | openssl x509 2>/dev/null)

  if [ -z "$cert" ]; then
    reason=$(printf '%s\n' "$out" | grep -m1 -o 'verify error:[^)]*' || echo 'no certificate (connection failed)')
    echo "CRIT  $target  $reason"; status=2; continue
  fi

  end=$(printf '%s\n' "$cert" | openssl x509 -noout -enddate | cut -d= -f2)
  # GNU date, then BSD/macOS date, then BusyBox (Alpine).
  end_s=$(date -u -d "$end" +%s 2>/dev/null \
       || date -j -u -f '%b %e %T %Y %Z' "$end" +%s 2>/dev/null \
       || date -u -D '%b %d %H:%M:%S %Y' -d "${end% GMT}" +%s 2>/dev/null)
  days=$(( (end_s - $(date +%s)) / 86400 ))

  if ! printf '%s\n' "$cert" | openssl x509 -noout -checkend $((CRIT_DAYS * 86400)) >/dev/null; then
    echo "CRIT  $target  expires in $days days ($end)"; status=2
  elif ! printf '%s\n' "$cert" | openssl x509 -noout -checkend $((WARN_DAYS * 86400)) >/dev/null; then
    echo "WARN  $target  expires in $days days ($end)"; [ $status -lt 1 ] && status=1
  else
    echo "OK    $target  expires in $days days ($end)"
  fi
done
exit $status
