Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
This commit is contained in:
Executable
+529
@@ -0,0 +1,529 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#####################################################################################
|
||||
#### mail-smoke-tests.sh — Verify mail infrastructure is healthy ####
|
||||
#### Checks Postfix, Dovecot, SMTP/IMAP, STARTTLS, SPF/DKIM/DMARC, queues. ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### License: MIT ####
|
||||
#### Version: 1.0 ####
|
||||
#### ####
|
||||
#### Usage: MAIL_DOMAIN=example.com ./mail-smoke-tests.sh ####
|
||||
#### SMTP_HOST=mail.example.com IMAP_PORT=993 ./mail-smoke-tests.sh ####
|
||||
#### ####
|
||||
#### See --help for all options. ####
|
||||
#####################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────
|
||||
MAIL_DOMAIN="${MAIL_DOMAIN:-}"
|
||||
SMTP_HOST="${SMTP_HOST:-localhost}"
|
||||
SMTP_PORT="${SMTP_PORT:-25}"
|
||||
SUBMISSION_PORT="${SUBMISSION_PORT:-587}"
|
||||
IMAP_HOST="${IMAP_HOST:-localhost}"
|
||||
IMAP_PORT="${IMAP_PORT:-993}"
|
||||
IMAP_USER="${IMAP_USER:-}"
|
||||
IMAP_PASS="${IMAP_PASS:-}"
|
||||
DKIM_SELECTOR="${DKIM_SELECTOR:-default}"
|
||||
MAX_QUEUE_SIZE="${MAX_QUEUE_SIZE:-50}"
|
||||
RELAY_TEST_ADDR="${RELAY_TEST_ADDR:-test@example.com}"
|
||||
SSL_WARN_DAYS="${SSL_WARN_DAYS:-30}"
|
||||
SKIP_DNS="${SKIP_DNS:-false}"
|
||||
SKIP_IMAP="${SKIP_IMAP:-false}"
|
||||
SKIP_RELAY="${SKIP_RELAY:-false}"
|
||||
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
|
||||
COLOR="${COLOR:-auto}"
|
||||
VERBOSE="${VERBOSE:-false}"
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────
|
||||
PASS=0; FAIL=0; SKIP=0; TOTAL=0
|
||||
RESULTS=()
|
||||
START_TIME=""
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────
|
||||
setup_colors() {
|
||||
if [[ "$COLOR" == "never" ]]; then
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
return
|
||||
fi
|
||||
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
RESET='\033[0m'
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────
|
||||
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
||||
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
||||
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[DEBUG]${RESET} $*"; fi; }
|
||||
|
||||
# ── Test Result Recording ─────────────────────────────────────────────
|
||||
record_pass() {
|
||||
local name="$1" detail="${2:-}"
|
||||
((PASS++)) || true; ((TOTAL++)) || true
|
||||
RESULTS+=("PASS|${name}|${detail}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then echo "ok ${TOTAL} - ${name}${detail:+ (${detail})}"
|
||||
else echo -e " ${GREEN}✓${RESET} ${name}${detail:+ — ${detail}}"; fi
|
||||
}
|
||||
|
||||
record_fail() {
|
||||
local name="$1" detail="${2:-}"
|
||||
((FAIL++)) || true; ((TOTAL++)) || true
|
||||
RESULTS+=("FAIL|${name}|${detail}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
echo "not ok ${TOTAL} - ${name}"
|
||||
[[ -n "$detail" ]] && echo " # ${detail}"
|
||||
else echo -e " ${RED}✗${RESET} ${name}${detail:+ — ${detail}}"; fi
|
||||
}
|
||||
|
||||
record_skip() {
|
||||
local name="$1" reason="${2:-}"
|
||||
((SKIP++)) || true; ((TOTAL++)) || true
|
||||
RESULTS+=("SKIP|${name}|${reason}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then echo "ok ${TOTAL} - ${name} # SKIP ${reason}"
|
||||
else echo -e " ${YELLOW}⊘${RESET} ${name}${reason:+ — ${reason}}"; fi
|
||||
}
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────
|
||||
has_cmd() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
section() {
|
||||
if [[ "$OUTPUT_FORMAT" != "tap" ]]; then echo ""; echo -e "${BOLD}$1${RESET}"; fi
|
||||
}
|
||||
|
||||
# ── Cleanup ───────────────────────────────────────────────────────────
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
verbose "Cleaning up temporary files..."
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# TEST FUNCTIONS
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── 1. Postfix running ───────────────────────────────────────────────
|
||||
test_postfix_running() {
|
||||
if has_cmd systemctl; then
|
||||
if systemctl is-active --quiet postfix 2>/dev/null; then
|
||||
record_pass "Postfix running" "systemctl active"
|
||||
else
|
||||
record_fail "Postfix running" "systemctl inactive"
|
||||
fi
|
||||
elif pgrep -x master >/dev/null 2>&1; then
|
||||
record_pass "Postfix running" "master process found"
|
||||
else
|
||||
record_fail "Postfix running" "master process not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 2. Dovecot running ──────────────────────────────────────────────
|
||||
test_dovecot_running() {
|
||||
if [[ "$SKIP_IMAP" == "true" ]]; then record_skip "Dovecot running" "SKIP_IMAP=true"; return; fi
|
||||
if has_cmd systemctl; then
|
||||
if systemctl is-active --quiet dovecot 2>/dev/null; then
|
||||
record_pass "Dovecot running" "systemctl active"
|
||||
else
|
||||
record_fail "Dovecot running" "systemctl inactive"
|
||||
fi
|
||||
elif pgrep -x dovecot >/dev/null 2>&1; then
|
||||
record_pass "Dovecot running" "dovecot process found"
|
||||
else
|
||||
record_fail "Dovecot running" "dovecot process not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 3. SMTP connect ─────────────────────────────────────────────────
|
||||
test_smtp_connect() {
|
||||
local output exit_code=0
|
||||
verbose "Testing SMTP connection to ${SMTP_HOST}:${SMTP_PORT}"
|
||||
output=$(timeout 10 bash -c "echo QUIT | nc -w5 ${SMTP_HOST} ${SMTP_PORT}" 2>&1) || exit_code=$?
|
||||
if [[ $exit_code -eq 0 ]] && echo "$output" | grep -q "220"; then
|
||||
local banner
|
||||
banner=$(echo "$output" | grep "220" | head -1 | tr -d '\r')
|
||||
record_pass "SMTP connect (${SMTP_HOST}:${SMTP_PORT})" "${banner:0:60}"
|
||||
else
|
||||
record_fail "SMTP connect (${SMTP_HOST}:${SMTP_PORT})" "no 220 banner received"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 4. SMTP EHLO ────────────────────────────────────────────────────
|
||||
test_smtp_ehlo() {
|
||||
local ehlo_name; ehlo_name=$(hostname -f 2>/dev/null || echo "localhost")
|
||||
local output exit_code=0
|
||||
verbose "Sending EHLO to ${SMTP_HOST}:${SMTP_PORT}"
|
||||
output=$(timeout 10 bash -c "{ echo 'EHLO ${ehlo_name}'; sleep 1; echo 'QUIT'; } | nc -w5 ${SMTP_HOST} ${SMTP_PORT}" 2>&1) || exit_code=$?
|
||||
if echo "$output" | grep -q "^250"; then
|
||||
local caps
|
||||
caps=$(echo "$output" | grep -c "^250" 2>/dev/null) || caps=0
|
||||
record_pass "SMTP EHLO" "${caps} capabilities advertised"
|
||||
else
|
||||
record_fail "SMTP EHLO" "no 250 response to EHLO"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 5. IMAP connect ─────────────────────────────────────────────────
|
||||
test_imap_connect() {
|
||||
if [[ "$SKIP_IMAP" == "true" ]]; then record_skip "IMAP connect" "SKIP_IMAP=true"; return; fi
|
||||
local output exit_code=0
|
||||
verbose "Testing IMAP connection to ${IMAP_HOST}:${IMAP_PORT}"
|
||||
if [[ "$IMAP_PORT" == "993" ]]; then
|
||||
output=$(timeout 10 bash -c "echo 'a1 LOGOUT' | openssl s_client -quiet -connect ${IMAP_HOST}:${IMAP_PORT} 2>/dev/null" 2>&1) || exit_code=$?
|
||||
else
|
||||
output=$(timeout 10 bash -c "echo 'a1 LOGOUT' | nc -w5 ${IMAP_HOST} ${IMAP_PORT}" 2>&1) || exit_code=$?
|
||||
fi
|
||||
if echo "$output" | grep -qi "OK\|IMAP\|ready"; then
|
||||
record_pass "IMAP connect (${IMAP_HOST}:${IMAP_PORT})" "server responded"
|
||||
else
|
||||
record_fail "IMAP connect (${IMAP_HOST}:${IMAP_PORT})" "no IMAP banner received"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 6. IMAP login ───────────────────────────────────────────────────
|
||||
test_imap_login() {
|
||||
if [[ "$SKIP_IMAP" == "true" ]]; then record_skip "IMAP login" "SKIP_IMAP=true"; return; fi
|
||||
if [[ -z "$IMAP_USER" || -z "$IMAP_PASS" ]]; then
|
||||
record_skip "IMAP login" "IMAP_USER/IMAP_PASS not set"
|
||||
return
|
||||
fi
|
||||
local output exit_code=0
|
||||
verbose "Attempting IMAP login as ${IMAP_USER}"
|
||||
if [[ "$IMAP_PORT" == "993" ]]; then
|
||||
output=$(timeout 10 bash -c "{ sleep 1; echo 'a1 LOGIN ${IMAP_USER} ${IMAP_PASS}'; sleep 1; echo 'a2 LOGOUT'; } | openssl s_client -quiet -connect ${IMAP_HOST}:${IMAP_PORT} 2>/dev/null" 2>&1) || exit_code=$?
|
||||
else
|
||||
output=$(timeout 10 bash -c "{ sleep 1; echo 'a1 LOGIN ${IMAP_USER} ${IMAP_PASS}'; sleep 1; echo 'a2 LOGOUT'; } | nc -w5 ${IMAP_HOST} ${IMAP_PORT}" 2>&1) || exit_code=$?
|
||||
fi
|
||||
if echo "$output" | grep -q "a1 OK"; then
|
||||
record_pass "IMAP login" "${IMAP_USER}"
|
||||
else
|
||||
record_fail "IMAP login" "login failed for ${IMAP_USER}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 7. SPF record ───────────────────────────────────────────────────
|
||||
test_spf_record() {
|
||||
if [[ "$SKIP_DNS" == "true" ]]; then record_skip "SPF record" "SKIP_DNS=true"; return; fi
|
||||
if [[ -z "$MAIL_DOMAIN" ]]; then record_skip "SPF record" "MAIL_DOMAIN not set"; return; fi
|
||||
if ! has_cmd dig; then record_skip "SPF record" "dig not installed"; return; fi
|
||||
local output
|
||||
output=$(dig +short TXT "${MAIL_DOMAIN}" 2>/dev/null) || true
|
||||
if echo "$output" | grep -qi "v=spf1"; then
|
||||
local spf
|
||||
spf=$(echo "$output" | grep -i "v=spf1" | head -1 | tr -d '"')
|
||||
record_pass "SPF record (${MAIL_DOMAIN})" "${spf:0:60}"
|
||||
else
|
||||
record_fail "SPF record (${MAIL_DOMAIN})" "no v=spf1 TXT record found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 8. DKIM record ──────────────────────────────────────────────────
|
||||
test_dkim_record() {
|
||||
if [[ "$SKIP_DNS" == "true" ]]; then record_skip "DKIM record" "SKIP_DNS=true"; return; fi
|
||||
if [[ -z "$MAIL_DOMAIN" ]]; then record_skip "DKIM record" "MAIL_DOMAIN not set"; return; fi
|
||||
if ! has_cmd dig; then record_skip "DKIM record" "dig not installed"; return; fi
|
||||
local selector_domain="${DKIM_SELECTOR}._domainkey.${MAIL_DOMAIN}"
|
||||
local output
|
||||
output=$(dig +short TXT "${selector_domain}" 2>/dev/null) || true
|
||||
if [[ -n "$output" ]] && echo "$output" | grep -qi "v=DKIM1\|p="; then
|
||||
record_pass "DKIM record (${selector_domain})" "key present"
|
||||
else
|
||||
record_fail "DKIM record (${selector_domain})" "no DKIM TXT record found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 9. DMARC record ─────────────────────────────────────────────────
|
||||
test_dmarc_record() {
|
||||
if [[ "$SKIP_DNS" == "true" ]]; then record_skip "DMARC record" "SKIP_DNS=true"; return; fi
|
||||
if [[ -z "$MAIL_DOMAIN" ]]; then record_skip "DMARC record" "MAIL_DOMAIN not set"; return; fi
|
||||
if ! has_cmd dig; then record_skip "DMARC record" "dig not installed"; return; fi
|
||||
local dmarc_domain="_dmarc.${MAIL_DOMAIN}"
|
||||
local output
|
||||
output=$(dig +short TXT "${dmarc_domain}" 2>/dev/null) || true
|
||||
if echo "$output" | grep -qi "v=DMARC1"; then
|
||||
local dmarc
|
||||
dmarc=$(echo "$output" | grep -i "v=DMARC1" | head -1 | tr -d '"')
|
||||
record_pass "DMARC record (${dmarc_domain})" "${dmarc:0:60}"
|
||||
else
|
||||
record_fail "DMARC record (${dmarc_domain})" "no v=DMARC1 TXT record found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 10. Mail queue size ─────────────────────────────────────────────
|
||||
test_mail_queue() {
|
||||
if ! has_cmd postqueue; then record_skip "Mail queue size" "postqueue not available"; return; fi
|
||||
local queue_output queue_count
|
||||
queue_output=$(postqueue -p 2>/dev/null) || true
|
||||
if echo "$queue_output" | grep -q "Mail queue is empty"; then
|
||||
record_pass "Mail queue size" "queue empty"
|
||||
return
|
||||
fi
|
||||
queue_count=$(echo "$queue_output" | grep -c "^[A-F0-9]" 2>/dev/null) || queue_count=0
|
||||
if [[ "$queue_count" -le "$MAX_QUEUE_SIZE" ]]; then
|
||||
record_pass "Mail queue size" "${queue_count} messages (<= ${MAX_QUEUE_SIZE})"
|
||||
else
|
||||
record_fail "Mail queue size" "${queue_count} messages (> ${MAX_QUEUE_SIZE})"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 11. Open relay test ─────────────────────────────────────────────
|
||||
test_open_relay() {
|
||||
if [[ "$SKIP_RELAY" == "true" ]]; then record_skip "Open relay test" "SKIP_RELAY=true"; return; fi
|
||||
local ehlo_name; ehlo_name=$(hostname -f 2>/dev/null || echo "localhost")
|
||||
local output exit_code=0
|
||||
verbose "Testing open relay via ${SMTP_HOST}:${SMTP_PORT}"
|
||||
output=$(timeout 10 bash -c "{
|
||||
sleep 1
|
||||
echo 'EHLO ${ehlo_name}'
|
||||
sleep 1
|
||||
echo 'MAIL FROM:<relay-test@example.org>'
|
||||
sleep 1
|
||||
echo 'RCPT TO:<${RELAY_TEST_ADDR}>'
|
||||
sleep 1
|
||||
echo 'QUIT'
|
||||
} | nc -w5 ${SMTP_HOST} ${SMTP_PORT}" 2>&1) || exit_code=$?
|
||||
if echo "$output" | grep -qE "^(454|550|553|554|521|503)"; then
|
||||
record_pass "Open relay test" "relay correctly refused"
|
||||
elif echo "$output" | grep -q "^250.*Ok\|^250.*Accepted"; then
|
||||
record_fail "Open relay test" "server accepted relay — possible open relay"
|
||||
else
|
||||
record_pass "Open relay test" "relay not accepted"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 12. SSL/TLS on SMTP ─────────────────────────────────────────────
|
||||
test_smtp_tls() {
|
||||
if ! has_cmd openssl; then record_skip "SMTP STARTTLS" "openssl not installed"; return; fi
|
||||
local output exit_code=0
|
||||
verbose "Testing STARTTLS on ${SMTP_HOST}:${SUBMISSION_PORT}"
|
||||
output=$(timeout 10 openssl s_client -starttls smtp -connect "${SMTP_HOST}:${SUBMISSION_PORT}" -servername "${SMTP_HOST}" </dev/null 2>&1) || exit_code=$?
|
||||
if echo "$output" | grep -qi "connected\|verify return\|SSL handshake"; then
|
||||
local expiry_line expiry_date days_left
|
||||
expiry_line=$(echo "$output" | grep -i "notAfter" | head -1) || true
|
||||
if [[ -n "$expiry_line" ]]; then
|
||||
expiry_date=$(echo "$expiry_line" | sed 's/.*notAfter=//') || true
|
||||
if [[ -n "$expiry_date" ]]; then
|
||||
local expiry_epoch now_epoch
|
||||
expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null) || expiry_epoch=0
|
||||
now_epoch=$(date +%s)
|
||||
if [[ "$expiry_epoch" -gt 0 ]]; then
|
||||
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
|
||||
if [[ "$days_left" -lt "$SSL_WARN_DAYS" ]]; then
|
||||
record_fail "SMTP STARTTLS (${SMTP_HOST}:${SUBMISSION_PORT})" "cert expires in ${days_left}d (< ${SSL_WARN_DAYS}d)"
|
||||
return
|
||||
fi
|
||||
record_pass "SMTP STARTTLS (${SMTP_HOST}:${SUBMISSION_PORT})" "cert valid, ${days_left}d remaining"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
record_pass "SMTP STARTTLS (${SMTP_HOST}:${SUBMISSION_PORT})" "TLS handshake OK"
|
||||
else
|
||||
record_fail "SMTP STARTTLS (${SMTP_HOST}:${SUBMISSION_PORT})" "STARTTLS handshake failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 13. SSL/TLS on IMAP ─────────────────────────────────────────────
|
||||
test_imap_tls() {
|
||||
if [[ "$SKIP_IMAP" == "true" ]]; then record_skip "IMAP TLS" "SKIP_IMAP=true"; return; fi
|
||||
if ! has_cmd openssl; then record_skip "IMAP TLS" "openssl not installed"; return; fi
|
||||
local output exit_code=0
|
||||
verbose "Testing TLS on ${IMAP_HOST}:${IMAP_PORT}"
|
||||
output=$(timeout 10 openssl s_client -connect "${IMAP_HOST}:${IMAP_PORT}" -servername "${IMAP_HOST}" </dev/null 2>&1) || exit_code=$?
|
||||
if echo "$output" | grep -qi "connected\|verify return\|SSL handshake"; then
|
||||
local expiry_line expiry_date days_left
|
||||
expiry_line=$(echo "$output" | grep -i "notAfter" | head -1) || true
|
||||
if [[ -n "$expiry_line" ]]; then
|
||||
expiry_date=$(echo "$expiry_line" | sed 's/.*notAfter=//') || true
|
||||
if [[ -n "$expiry_date" ]]; then
|
||||
local expiry_epoch now_epoch
|
||||
expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null) || expiry_epoch=0
|
||||
now_epoch=$(date +%s)
|
||||
if [[ "$expiry_epoch" -gt 0 ]]; then
|
||||
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
|
||||
if [[ "$days_left" -lt "$SSL_WARN_DAYS" ]]; then
|
||||
record_fail "IMAP TLS (${IMAP_HOST}:${IMAP_PORT})" "cert expires in ${days_left}d (< ${SSL_WARN_DAYS}d)"
|
||||
return
|
||||
fi
|
||||
record_pass "IMAP TLS (${IMAP_HOST}:${IMAP_PORT})" "cert valid, ${days_left}d remaining"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
record_pass "IMAP TLS (${IMAP_HOST}:${IMAP_PORT})" "TLS handshake OK"
|
||||
else
|
||||
record_fail "IMAP TLS (${IMAP_HOST}:${IMAP_PORT})" "TLS handshake failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 14. Deferred mail check ─────────────────────────────────────────
|
||||
test_deferred_queue() {
|
||||
if ! has_cmd postqueue; then record_skip "Deferred mail check" "postqueue not available"; return; fi
|
||||
local deferred_count=0
|
||||
if [[ -d /var/spool/postfix/deferred ]]; then
|
||||
deferred_count=$(find /var/spool/postfix/deferred -type f 2>/dev/null | wc -l) || deferred_count=0
|
||||
fi
|
||||
if [[ "$deferred_count" -eq 0 ]]; then
|
||||
record_pass "Deferred mail check" "no deferred messages"
|
||||
else
|
||||
record_fail "Deferred mail check" "${deferred_count} deferred message(s)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# OUTPUT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
print_tap_header() {
|
||||
echo "TAP version 13"
|
||||
}
|
||||
|
||||
print_tap_footer() {
|
||||
echo "1..${TOTAL}"
|
||||
echo "# pass ${PASS}"
|
||||
echo "# fail ${FAIL}"
|
||||
echo "# skip ${SKIP}"
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
local end_time; end_time=$(date +%s)
|
||||
local duration=$(( end_time - START_TIME ))
|
||||
echo ""
|
||||
echo -e "${BOLD}────────────────────────────────────────${RESET}"
|
||||
echo -e "${BOLD}Summary${RESET} Mail Smoke Tests"
|
||||
echo -e " ${GREEN}${PASS} passed${RESET} ${RED}${FAIL} failed${RESET} ${YELLOW}${SKIP} skipped${RESET} (${duration}s)"
|
||||
echo -e "${BOLD}────────────────────────────────────────${RESET}"
|
||||
if [[ $FAIL -eq 0 ]]; then echo -e "${GREEN}${BOLD}All tests passed.${RESET}"
|
||||
else echo -e "${RED}${BOLD}${FAIL} test(s) failed.${RESET}"; fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Smoke-test mail infrastructure. Checks Postfix, Dovecot, SMTP/IMAP
|
||||
connectivity, STARTTLS, SPF/DKIM/DMARC records, queue size, and open relay.
|
||||
|
||||
Environment variables:
|
||||
MAIL_DOMAIN Domain for DNS record checks (required for DNS tests)
|
||||
SMTP_HOST SMTP server to test (default: localhost)
|
||||
SMTP_PORT SMTP port (default: 25)
|
||||
SUBMISSION_PORT Submission port for STARTTLS (default: 587)
|
||||
IMAP_HOST IMAP server to test (default: localhost)
|
||||
IMAP_PORT IMAP port (default: 993)
|
||||
IMAP_USER IMAP login username (default: empty, skip)
|
||||
IMAP_PASS IMAP login password (default: empty, skip)
|
||||
DKIM_SELECTOR DKIM selector name (default: default)
|
||||
MAX_QUEUE_SIZE Max acceptable queue depth (default: 50)
|
||||
RELAY_TEST_ADDR Address for open relay test (default: test@example.com)
|
||||
SSL_WARN_DAYS Cert expiry warning threshold (default: 30)
|
||||
SKIP_DNS Skip SPF/DKIM/DMARC checks (default: false)
|
||||
SKIP_IMAP Skip all IMAP/Dovecot tests (default: false)
|
||||
SKIP_RELAY Skip open relay test (default: false)
|
||||
OUTPUT_FORMAT Output: text (default), tap (default: text)
|
||||
COLOR Color: auto, always, never (default: auto)
|
||||
VERBOSE Show debug output (default: false)
|
||||
|
||||
Options:
|
||||
--domain DOMAIN Set MAIL_DOMAIN
|
||||
--smtp-host HOST Set SMTP_HOST
|
||||
--imap-host HOST Set IMAP_HOST
|
||||
--skip-dns Skip DNS record checks
|
||||
--skip-imap Skip IMAP/Dovecot tests
|
||||
--skip-relay Skip open relay test
|
||||
--format FORMAT Output format: text (default), tap
|
||||
--verbose Show debug output
|
||||
--no-color Disable colored output
|
||||
--help Show this help
|
||||
|
||||
Examples:
|
||||
MAIL_DOMAIN=example.com ./$(basename "$0")
|
||||
SMTP_HOST=mail.example.com IMAP_PORT=993 ./$(basename "$0")
|
||||
OUTPUT_FORMAT=tap SKIP_DNS=true ./$(basename "$0")
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--domain) MAIL_DOMAIN="$2"; shift ;;
|
||||
--smtp-host) SMTP_HOST="$2"; shift ;;
|
||||
--imap-host) IMAP_HOST="$2"; shift ;;
|
||||
--skip-dns) SKIP_DNS=true ;;
|
||||
--skip-imap) SKIP_IMAP=true ;;
|
||||
--skip-relay) SKIP_RELAY=true ;;
|
||||
--format) OUTPUT_FORMAT="$2"; shift ;;
|
||||
--verbose) VERBOSE=true ;;
|
||||
--no-color) COLOR=never ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) err "Unknown option: $1"; usage; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
setup_colors
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
print_tap_header
|
||||
else
|
||||
echo ""
|
||||
echo -e "${BOLD}Mail Smoke Tests${RESET}"
|
||||
echo "Domain: ${MAIL_DOMAIN:-(not set)}"
|
||||
echo "SMTP: ${SMTP_HOST}:${SMTP_PORT} Submission: ${SUBMISSION_PORT}"
|
||||
echo "IMAP: ${IMAP_HOST}:${IMAP_PORT}"
|
||||
echo "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
fi
|
||||
|
||||
section "Processes"
|
||||
test_postfix_running
|
||||
test_dovecot_running
|
||||
|
||||
section "SMTP"
|
||||
test_smtp_connect
|
||||
test_smtp_ehlo
|
||||
|
||||
section "IMAP"
|
||||
test_imap_connect
|
||||
test_imap_login
|
||||
|
||||
section "DNS Records"
|
||||
test_spf_record
|
||||
test_dkim_record
|
||||
test_dmarc_record
|
||||
|
||||
section "Mail Queue"
|
||||
test_mail_queue
|
||||
test_deferred_queue
|
||||
|
||||
section "Security"
|
||||
test_open_relay
|
||||
|
||||
section "TLS Certificates"
|
||||
test_smtp_tls
|
||||
test_imap_tls
|
||||
|
||||
# ── Results ──
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
print_tap_footer
|
||||
else
|
||||
print_summary
|
||||
fi
|
||||
|
||||
[[ $FAIL -eq 0 ]] && exit 0 || exit 1
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user