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:
2026-05-25 03:31:08 +02:00
parent dbd6bf0324
commit a1a17e81a1
332 changed files with 174509 additions and 1106 deletions
+88 -1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
################################################################################
# Script Name: postfix-metrics.sh
# Version: 1.01
# Description: Prometheus exporter for Postfix mail server metrics
#
# Usage:
@@ -95,6 +96,7 @@ for queue in incoming active deferred hold corrupt; do
done
# Oldest message in queue (seconds)
echo ""
echo "# HELP postfix_queue_oldest_seconds Age of oldest message in queue"
echo "# TYPE postfix_queue_oldest_seconds gauge"
for queue in deferred hold; do
@@ -108,6 +110,7 @@ for queue in deferred hold; do
done
# Message counters by status
echo ""
echo "# HELP postfix_messages_total Total messages by status"
echo "# TYPE postfix_messages_total counter"
for status in sent bounced deferred expired; do
@@ -118,6 +121,7 @@ rejected=$(grep_count 'reject:' "$LOG_FILE")
echo "postfix_messages_total{status=\"rejected\",hostname=\"${HOSTNAME}\"} ${rejected}"
# SMTP connections
echo ""
echo "# HELP postfix_smtp_connections SMTP connection stats"
echo "# TYPE postfix_smtp_connections counter"
connections=$(grep_count 'connect from' "$LOG_FILE")
@@ -126,12 +130,14 @@ echo "postfix_smtp_connections{type=\"connect\",hostname=\"${HOSTNAME}\"} ${conn
echo "postfix_smtp_connections{type=\"disconnect\",hostname=\"${HOSTNAME}\"} ${disconnections}"
# Connection timeouts
echo ""
echo "# HELP postfix_timeout_total Connection timeout events"
echo "# TYPE postfix_timeout_total counter"
timeout_count=$(grep_count 'timeout after' "$LOG_FILE")
echo "postfix_timeout_total{hostname=\"${HOSTNAME}\"} ${timeout_count}"
# SASL authentication
echo ""
echo "# HELP postfix_sasl_auth_total SASL authentication attempts"
echo "# TYPE postfix_sasl_auth_total counter"
sasl_success=$(grep_count 'sasl_username=' "$LOG_FILE")
@@ -140,22 +146,26 @@ echo "postfix_sasl_auth_total{result=\"success\",hostname=\"${HOSTNAME}\"} ${sas
echo "postfix_sasl_auth_total{result=\"failed\",hostname=\"${HOSTNAME}\"} ${sasl_fail}"
# Message sizes (bytes)
echo ""
echo "# HELP postfix_message_size_bytes_total Total bytes of messages processed"
echo "# TYPE postfix_message_size_bytes_total counter"
total_bytes=$(grep -oP 'size=\K\d+' "$LOG_FILE" 2>/dev/null | awk '{sum+=$1} END {print sum+0}')
echo "postfix_message_size_bytes_total{hostname=\"${HOSTNAME}\"} ${total_bytes}"
echo ""
echo "# HELP postfix_message_size_bytes_avg Average message size"
echo "# TYPE postfix_message_size_bytes_avg gauge"
avg_size=$(grep -oP 'size=\K\d+' "$LOG_FILE" 2>/dev/null | awk '{sum+=$1; count++} END {if(count>0) print int(sum/count); else print 0}')
echo "postfix_message_size_bytes_avg{hostname=\"${HOSTNAME}\"} ${avg_size}"
echo ""
echo "# HELP postfix_message_size_bytes_max Largest message size"
echo "# TYPE postfix_message_size_bytes_max gauge"
max_size=$(grep -oP 'size=\K\d+' "$LOG_FILE" 2>/dev/null | sort -rn | head -1)
echo "postfix_message_size_bytes_max{hostname=\"${HOSTNAME}\"} ${max_size:-0}"
# Per-recipient domain stats (top domains)
echo ""
echo "# HELP postfix_recipient_domain_total Messages per recipient domain"
echo "# TYPE postfix_recipient_domain_total counter"
grep -oP 'to=<[^@]+@\K[^>]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -20 | while read -r count domain; do
@@ -163,6 +173,7 @@ grep -oP 'to=<[^@]+@\K[^>]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn
done
# Sender domain stats
echo ""
echo "# HELP postfix_sender_domain_total Messages per sender domain"
echo "# TYPE postfix_sender_domain_total counter"
grep -oP 'from=<[^@]+@\K[^>]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -20 | while read -r count domain; do
@@ -170,6 +181,7 @@ grep -oP 'from=<[^@]+@\K[^>]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -
done
# Bounce reasons
echo ""
echo "# HELP postfix_bounce_reason_total Bounces by reason"
echo "# TYPE postfix_bounce_reason_total counter"
bounce_user=$(grep_count 'User unknown' "$LOG_FILE")
@@ -184,6 +196,7 @@ echo "postfix_bounce_reason_total{reason=\"dns_error\",hostname=\"${HOSTNAME}\"}
echo "postfix_bounce_reason_total{reason=\"connection_refused\",hostname=\"${HOSTNAME}\"} ${bounce_refused}"
# Relay stats
echo ""
echo "# HELP postfix_relay_total Messages by relay"
echo "# TYPE postfix_relay_total counter"
grep -oP 'relay=\K[^,\[]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -10 | while read -r count relay; do
@@ -191,6 +204,7 @@ grep -oP 'relay=\K[^,\[]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn |
done
# Client connections (top IPs)
echo ""
echo "# HELP postfix_client_connections_total Connections per client IP"
echo "# TYPE postfix_client_connections_total counter"
grep -oP 'connect from \S+\[\K[^\]]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -10 | while read -r count ip; do
@@ -198,6 +212,7 @@ grep -oP 'connect from \S+\[\K[^\]]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c |
done
# TLS stats
echo ""
echo "# HELP postfix_tls_connections_total TLS connection statistics"
echo "# TYPE postfix_tls_connections_total counter"
tls_in=$(grep_count 'Anonymous TLS connection established from' "$LOG_FILE")
@@ -214,6 +229,7 @@ echo "postfix_tls_connections_total{direction=\"inbound\",verified=\"untrusted\"
echo "postfix_tls_connections_total{direction=\"outbound\",verified=\"untrusted\",hostname=\"${HOSTNAME}\"} ${untrusted_out}"
# TLS protocol versions
echo ""
echo "# HELP postfix_tls_protocol_total TLS protocol version usage"
echo "# TYPE postfix_tls_protocol_total counter"
for proto in TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; do
@@ -222,34 +238,40 @@ for proto in TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; do
done
# Delay stats (queue time)
echo ""
echo "# HELP postfix_delay_seconds_total Total delay time in seconds"
echo "# TYPE postfix_delay_seconds_total counter"
total_delay=$(grep -oP 'delay=\K[\d.]+' "$LOG_FILE" 2>/dev/null | awk '{sum+=$1} END {print sum+0}')
echo "postfix_delay_seconds_total{hostname=\"${HOSTNAME}\"} ${total_delay}"
echo ""
echo "# HELP postfix_delay_seconds_avg Average delivery delay"
echo "# TYPE postfix_delay_seconds_avg gauge"
avg_delay=$(grep -oP 'delay=\K[\d.]+' "$LOG_FILE" 2>/dev/null | awk '{sum+=$1; count++} END {if(count>0) printf "%.2f", sum/count; else print 0}')
echo "postfix_delay_seconds_avg{hostname=\"${HOSTNAME}\"} ${avg_delay}"
echo ""
echo "# HELP postfix_delay_seconds_max Maximum delivery delay"
echo "# TYPE postfix_delay_seconds_max gauge"
max_delay=$(grep -oP 'delay=\K[\d.]+' "$LOG_FILE" 2>/dev/null | sort -rn | head -1)
echo "postfix_delay_seconds_max{hostname=\"${HOSTNAME}\"} ${max_delay:-0}"
# Postfix process count
echo ""
echo "# HELP postfix_processes Number of running postfix processes"
echo "# TYPE postfix_processes gauge"
proc_count=$(pgrep -c -f "postfix" 2>/dev/null) || proc_count=0
echo "postfix_processes{hostname=\"${HOSTNAME}\"} ${proc_count}"
# Mail loop detection
echo ""
echo "# HELP postfix_mail_loop_total Detected mail loops"
echo "# TYPE postfix_mail_loop_total counter"
loops=$(grep_count 'mail forwarding loop' "$LOG_FILE")
echo "postfix_mail_loop_total{hostname=\"${HOSTNAME}\"} ${loops}"
# Service status
echo ""
echo "# HELP postfix_up Postfix service status (1=running, 0=stopped)"
echo "# TYPE postfix_up gauge"
if postfix status &>/dev/null || systemctl is-active postfix &>/dev/null; then
@@ -259,6 +281,7 @@ else
fi
# Queue age distribution (messages by age bucket)
echo ""
echo "# HELP postfix_queue_age_bucket Messages in deferred queue by age"
echo "# TYPE postfix_queue_age_bucket gauge"
now=$(date +%s)
@@ -268,6 +291,7 @@ for mins in 5 15 60 360 1440; do
done
# Delivery attempts (retries)
echo ""
echo "# HELP postfix_delivery_attempts_total Delivery attempts by result"
echo "# TYPE postfix_delivery_attempts_total counter"
first_attempt=$(grep_count 'delay=.*delays=0/' "$LOG_FILE")
@@ -276,6 +300,7 @@ echo "postfix_delivery_attempts_total{type=\"first\",hostname=\"${HOSTNAME}\"} $
echo "postfix_delivery_attempts_total{type=\"retry\",hostname=\"${HOSTNAME}\"} ${retry_attempt}"
# DSN status codes breakdown
echo ""
echo "# HELP postfix_dsn_total Delivery Status Notification codes"
echo "# TYPE postfix_dsn_total counter"
for dsn in "2.0.0" "4.7.1" "5.1.1" "5.1.2" "5.2.1" "5.2.2" "5.4.1" "5.7.1"; do
@@ -284,6 +309,7 @@ for dsn in "2.0.0" "4.7.1" "5.1.1" "5.1.2" "5.2.1" "5.2.2" "5.4.1" "5.7.1"; do
done
# Delay breakdown by phase
echo ""
echo "# HELP postfix_delay_phase_seconds_total Delay time by phase"
echo "# TYPE postfix_delay_phase_seconds_total counter"
grep -oP 'delays=\K[\d.]+/[\d.]+/[\d.]+/[\d.]+' "$LOG_FILE" 2>/dev/null | awk -F'/' '{
@@ -298,6 +324,7 @@ grep -oP 'delays=\K[\d.]+/[\d.]+/[\d.]+/[\d.]+' "$LOG_FILE" 2>/dev/null | awk -F
done
# RBL rejections (per blocklist)
echo ""
echo "# HELP postfix_rbl_reject_total Rejections by RBL"
echo "# TYPE postfix_rbl_reject_total counter"
for rbl in "zen.spamhaus.org" "bl.spamcop.net" "b.barracudacentral.org" "dnsbl.sorbs.net"; do
@@ -306,12 +333,14 @@ for rbl in "zen.spamhaus.org" "bl.spamcop.net" "b.barracudacentral.org" "dnsbl.s
done
# Invalid HELO/EHLO attempts
echo ""
echo "# HELP postfix_helo_invalid_total Invalid HELO/EHLO attempts"
echo "# TYPE postfix_helo_invalid_total counter"
helo_invalid=$(grep_count 'Helo command rejected' "$LOG_FILE")
echo "postfix_helo_invalid_total{hostname=\"${HOSTNAME}\"} ${helo_invalid}"
# Anvil rate limiting
echo ""
echo "# HELP postfix_rate_limited_total Anvil rate limit events"
echo "# TYPE postfix_rate_limited_total counter"
rate_conn=$(grep_count 'anvil.*connection rate' "$LOG_FILE")
@@ -322,12 +351,14 @@ echo "postfix_rate_limited_total{type=\"message\",hostname=\"${HOSTNAME}\"} ${ra
echo "postfix_rate_limited_total{type=\"recipient\",hostname=\"${HOSTNAME}\"} ${rate_rcpt}"
# Milter/content filter rejections
echo ""
echo "# HELP postfix_milter_reject_total Milter rejection events"
echo "# TYPE postfix_milter_reject_total counter"
milter_reject=$(grep_count 'milter-reject' "$LOG_FILE")
echo "postfix_milter_reject_total{hostname=\"${HOSTNAME}\"} ${milter_reject}"
# Header/body checks rejections
echo ""
echo "# HELP postfix_header_checks_reject_total Header/body check rejections"
echo "# TYPE postfix_header_checks_reject_total counter"
header_reject=$(grep_count 'header_checks:' "$LOG_FILE")
@@ -336,6 +367,7 @@ echo "postfix_header_checks_reject_total{type=\"header\",hostname=\"${HOSTNAME}\
echo "postfix_header_checks_reject_total{type=\"body\",hostname=\"${HOSTNAME}\"} ${body_reject}"
# Policy daemon deferrals
echo ""
echo "# HELP postfix_policyd_total Policy daemon events"
echo "# TYPE postfix_policyd_total counter"
policyd_defer=$(grep_count 'policy.*DEFER' "$LOG_FILE")
@@ -344,6 +376,7 @@ echo "postfix_policyd_total{action=\"defer\",hostname=\"${HOSTNAME}\"} ${policyd
echo "postfix_policyd_total{action=\"reject\",hostname=\"${HOSTNAME}\"} ${policyd_reject}"
# DKIM signing (if OpenDKIM is used)
echo ""
echo "# HELP postfix_dkim_total DKIM signing/verification results"
echo "# TYPE postfix_dkim_total counter"
dkim_signed=$(grep_count 'DKIM-Signature field added' "$LOG_FILE")
@@ -354,6 +387,7 @@ echo "postfix_dkim_total{result=\"pass\",hostname=\"${HOSTNAME}\"} ${dkim_pass}"
echo "postfix_dkim_total{result=\"fail\",hostname=\"${HOSTNAME}\"} ${dkim_fail}"
# SPF results
echo ""
echo "# HELP postfix_spf_total SPF check results"
echo "# TYPE postfix_spf_total counter"
for result in pass fail softfail neutral none permerror temperror; do
@@ -363,6 +397,7 @@ done
# DMARC results (if OpenDMARC is used)
# OpenDMARC logs: "opendmarc[PID]: QUEUEID: domain.com pass/fail/none"
echo ""
echo "# HELP postfix_dmarc_total DMARC check results"
echo "# TYPE postfix_dmarc_total counter"
for result in pass fail none; do
@@ -371,6 +406,7 @@ for result in pass fail none; do
done
# Hourly volume (traffic patterns)
echo ""
echo "# HELP postfix_hourly_volume Messages processed per hour"
echo "# TYPE postfix_hourly_volume gauge"
current_date=$(date +%b" "%d)
@@ -381,6 +417,7 @@ for hour in $(seq -w 0 23); do
done
# Recent throughput (last 5/15/60 minutes)
echo ""
echo "# HELP postfix_messages_recent Messages sent in recent time windows"
echo "# TYPE postfix_messages_recent gauge"
for mins in 5 15 60; do
@@ -394,6 +431,7 @@ for mins in 5 15 60; do
done
# Active SMTP sessions estimate
echo ""
echo "# HELP postfix_smtp_sessions_active Estimated active SMTP sessions"
echo "# TYPE postfix_smtp_sessions_active gauge"
smtp_procs=$(pgrep -c -x smtp 2>/dev/null) || smtp_procs=0
@@ -402,20 +440,24 @@ echo "postfix_smtp_sessions_active{type=\"outbound\",hostname=\"${HOSTNAME}\"} $
echo "postfix_smtp_sessions_active{type=\"inbound\",hostname=\"${HOSTNAME}\"} ${smtpd_procs}"
# Qmgr active recipients
echo ""
echo "# HELP postfix_qmgr_recipients Active recipients in queue manager"
echo "# TYPE postfix_qmgr_recipients gauge"
active_recipients=$(find "${QUEUE_DIR}/active" -type f -exec cat {} \; 2>/dev/null | wc -l) || active_recipients=0
echo "postfix_qmgr_recipients{hostname=\"${HOSTNAME}\"} ${active_recipients}"
# Estimated queue memory usage (based on file sizes)
echo ""
echo "# HELP postfix_queue_size_bytes Total size of queue files in bytes"
echo "# TYPE postfix_queue_size_bytes gauge"
for queue in incoming active deferred hold; do
size=$(du -sb "${QUEUE_DIR}/${queue}" 2>/dev/null | cut -f1) || size=0
size=$(du -sb "${QUEUE_DIR}/${queue}" 2>/dev/null | cut -f1)
size=${size:-0}
echo "postfix_queue_size_bytes{queue=\"${queue}\",hostname=\"${HOSTNAME}\"} ${size}"
done
# Warnings and fatal errors
echo ""
echo "# HELP postfix_log_events_total Log events by severity"
echo "# TYPE postfix_log_events_total counter"
warnings=$(grep_count 'warning:' "$LOG_FILE")
@@ -426,6 +468,7 @@ echo "postfix_log_events_total{level=\"fatal\",hostname=\"${HOSTNAME}\"} ${fatal
echo "postfix_log_events_total{level=\"panic\",hostname=\"${HOSTNAME}\"} ${panics}"
# SMTP response codes
echo ""
echo "# HELP postfix_smtp_response_total SMTP response codes"
echo "# TYPE postfix_smtp_response_total counter"
smtp_2xx=$(grep_count 'status=sent' "$LOG_FILE")
@@ -441,6 +484,7 @@ echo "postfix_smtp_response_total{code=\"5xx\",hostname=\"${HOSTNAME}\"} ${smtp_
# - "status=bounced (host ... said: 550 ...)"
# - "dsn=5.1.1" (DSN codes start with same digit)
# - Remote server responses with just the code
echo ""
echo "# HELP postfix_smtp_error_code_total Specific SMTP error codes"
echo "# TYPE postfix_smtp_error_code_total counter"
for code in 421 450 451 452 500 501 502 503 504 550 551 552 553 554; do
@@ -452,6 +496,7 @@ done
# TLS cipher suites (top 10)
# Requires smtpd_tls_loglevel=1 and smtp_tls_loglevel=1 in main.cf
# Postfix logs: "TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)"
echo ""
echo "# HELP postfix_tls_cipher_total TLS cipher suite usage"
echo "# TYPE postfix_tls_cipher_total counter"
cipher_output=$({
@@ -467,6 +512,7 @@ else
fi
# TLS certificate expiry (check multiple locations)
echo ""
echo "# HELP postfix_cert_expiry_seconds Seconds until TLS certificate expires"
echo "# TYPE postfix_cert_expiry_seconds gauge"
CERT_FILE=""
@@ -493,6 +539,7 @@ echo "postfix_cert_expiry_seconds{hostname=\"${HOSTNAME}\"} ${cert_seconds}"
# LMTP delivery metrics (Postfix side)
# Matches: "postfix/lmtp[PID]: ... status=sent"
echo ""
echo "# HELP postfix_lmtp_delivery_total LMTP delivery stats"
echo "# TYPE postfix_lmtp_delivery_total counter"
lmtp_sent=$(grep_count 'postfix/lmtp\[.*status=sent' "$LOG_FILE")
@@ -502,6 +549,7 @@ echo "postfix_lmtp_delivery_total{status=\"sent\",hostname=\"${HOSTNAME}\"} ${lm
echo "postfix_lmtp_delivery_total{status=\"deferred\",hostname=\"${HOSTNAME}\"} ${lmtp_deferred}"
echo "postfix_lmtp_delivery_total{status=\"bounced\",hostname=\"${HOSTNAME}\"} ${lmtp_bounced}"
echo ""
echo "# HELP postfix_lmtp_connections_total LMTP connection events"
echo "# TYPE postfix_lmtp_connections_total counter"
lmtp_connect=$(grep_count 'postfix/lmtp\[.*connect' "$LOG_FILE")
@@ -513,6 +561,7 @@ echo "postfix_lmtp_connections_total{type=\"disconnect\",hostname=\"${HOSTNAME}\
echo "postfix_lmtp_connections_total{type=\"timeout\",hostname=\"${HOSTNAME}\"} ${lmtp_timeout}"
echo "postfix_lmtp_connections_total{type=\"refused\",hostname=\"${HOSTNAME}\"} ${lmtp_refused}"
echo ""
echo "# HELP postfix_lmtp_delay_seconds LMTP delivery delay stats"
echo "# TYPE postfix_lmtp_delay_seconds gauge"
lmtp_avg_delay=$(grep 'postfix/lmtp\[' "$LOG_FILE" 2>/dev/null | grep -oP 'delay=\K[\d.]+' | awk '{sum+=$1; count++} END {if(count>0) printf "%.2f", sum/count; else print 0}')
@@ -529,6 +578,7 @@ for log in "/var/log/dovecot.log" "/var/log/mail.log" "/var/log/syslog"; do
fi
done
if [[ -n "$DOVECOT_LOG" ]]; then
echo ""
echo "# HELP postfix_dovecot_delivery_total Dovecot local delivery stats"
echo "# TYPE postfix_dovecot_delivery_total counter"
lmtp_delivered=$(grep_count 'lmtp.*saved mail' "$DOVECOT_LOG")
@@ -536,6 +586,7 @@ if [[ -n "$DOVECOT_LOG" ]]; then
echo "postfix_dovecot_delivery_total{type=\"lmtp\",hostname=\"${HOSTNAME}\"} ${lmtp_delivered}"
echo "postfix_dovecot_delivery_total{type=\"lda\",hostname=\"${HOSTNAME}\"} ${lda_delivered}"
echo ""
echo "# HELP postfix_dovecot_sieve_total Dovecot sieve filter actions"
echo "# TYPE postfix_dovecot_sieve_total counter"
sieve_fileinto=$(grep_count 'sieve.*fileinto' "$DOVECOT_LOG")
@@ -545,6 +596,7 @@ if [[ -n "$DOVECOT_LOG" ]]; then
echo "postfix_dovecot_sieve_total{action=\"discard\",hostname=\"${HOSTNAME}\"} ${sieve_discard}"
echo "postfix_dovecot_sieve_total{action=\"redirect\",hostname=\"${HOSTNAME}\"} ${sieve_redirect}"
echo ""
echo "# HELP postfix_dovecot_auth_total Dovecot authentication attempts"
echo "# TYPE postfix_dovecot_auth_total counter"
auth_success=$(grep_count 'auth.*successful' "$DOVECOT_LOG")
@@ -552,6 +604,7 @@ if [[ -n "$DOVECOT_LOG" ]]; then
echo "postfix_dovecot_auth_total{result=\"success\",hostname=\"${HOSTNAME}\"} ${auth_success}"
echo "postfix_dovecot_auth_total{result=\"failed\",hostname=\"${HOSTNAME}\"} ${auth_fail}"
echo ""
echo "# HELP postfix_dovecot_imap_connections_total Dovecot IMAP connections"
echo "# TYPE postfix_dovecot_imap_connections_total counter"
imap_login=$(grep_count 'imap-login:.*Login' "$DOVECOT_LOG")
@@ -559,6 +612,7 @@ if [[ -n "$DOVECOT_LOG" ]]; then
echo "postfix_dovecot_imap_connections_total{type=\"login\",hostname=\"${HOSTNAME}\"} ${imap_login}"
echo "postfix_dovecot_imap_connections_total{type=\"disconnect\",hostname=\"${HOSTNAME}\"} ${imap_disconnect}"
echo ""
echo "# HELP postfix_dovecot_pop3_connections_total Dovecot POP3 connections"
echo "# TYPE postfix_dovecot_pop3_connections_total counter"
pop3_login=$(grep_count 'pop3-login:.*Login' "$DOVECOT_LOG")
@@ -582,6 +636,7 @@ else
fi
if [[ -n "$SPAM_DAEMON" ]]; then
echo ""
echo "# HELP postfix_spamassassin_total SpamAssassin scan results"
echo "# TYPE postfix_spamassassin_total counter"
@@ -599,6 +654,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
echo "postfix_spamassassin_total{result=\"spam\",hostname=\"${HOSTNAME}\"} ${spam_identified}"
echo "postfix_spamassassin_total{result=\"ham\",hostname=\"${HOSTNAME}\"} ${ham_clean}"
echo ""
echo "# HELP postfix_spamassassin_score_total SpamAssassin score distribution"
echo "# TYPE postfix_spamassassin_score_total counter"
@@ -624,6 +680,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
echo "postfix_spamassassin_score_total{bucket=\"5-10\",hostname=\"${HOSTNAME}\"} ${score_5_10}"
echo "postfix_spamassassin_score_total{bucket=\"10+\",hostname=\"${HOSTNAME}\"} ${score_10_plus}"
echo ""
echo "# HELP postfix_spamassassin_score_avg Average SpamAssassin score"
echo "# TYPE postfix_spamassassin_score_avg gauge"
if [[ "$SPAM_DAEMON" == "spampd" ]]; then
@@ -635,6 +692,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
fi
echo "postfix_spamassassin_score_avg{hostname=\"${HOSTNAME}\"} ${avg_score}"
echo ""
echo "# HELP postfix_spamassassin_score_max Maximum SpamAssassin score seen"
echo "# TYPE postfix_spamassassin_score_max gauge"
if [[ "$SPAM_DAEMON" == "spampd" ]]; then
@@ -647,11 +705,13 @@ if [[ -n "$SPAM_DAEMON" ]]; then
echo "postfix_spamassassin_score_max{hostname=\"${HOSTNAME}\"} ${max_score:-0}"
# Messages scanned total
echo ""
echo "# HELP postfix_spamassassin_scanned_total Total messages scanned"
echo "# TYPE postfix_spamassassin_scanned_total counter"
scanned_total=$((spam_identified + ham_clean))
echo "postfix_spamassassin_scanned_total{hostname=\"${HOSTNAME}\"} ${scanned_total}"
echo ""
echo "# HELP postfix_spamassassin_scan_time_seconds SpamAssassin scan time stats"
echo "# TYPE postfix_spamassassin_scan_time_seconds gauge"
if [[ "$SPAM_DAEMON" == "spampd" ]]; then
@@ -667,6 +727,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
# spampd-specific: message size stats
if [[ "$SPAM_DAEMON" == "spampd" ]]; then
echo ""
echo "# HELP postfix_spamassassin_message_size_bytes SpamAssassin processed message sizes"
echo "# TYPE postfix_spamassassin_message_size_bytes gauge"
avg_size=$(grep -oP 'spampd.*, \K\d+(?= bytes)' "$SPAM_LOG" 2>/dev/null | awk '{sum+=$1; count++} END {if(count>0) printf "%.0f", sum/count; else print 0}')
@@ -674,6 +735,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
echo "postfix_spamassassin_message_size_bytes{stat=\"avg\",hostname=\"${HOSTNAME}\"} ${avg_size:-0}"
echo "postfix_spamassassin_message_size_bytes{stat=\"max\",hostname=\"${HOSTNAME}\"} ${max_size:-0}"
echo ""
echo "# HELP postfix_spamassassin_threshold SpamAssassin spam threshold"
echo "# TYPE postfix_spamassassin_threshold gauge"
threshold=$(grep -oP 'spampd.*/-?\K[\d.]+(?=\))' "$SPAM_LOG" 2>/dev/null | head -1)
@@ -691,6 +753,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
fi
done
if [[ -n "$SA_RULES_LOG" ]]; then
echo ""
echo "# HELP postfix_spamassassin_rules_total Top SpamAssassin rules triggered"
echo "# TYPE postfix_spamassassin_rules_total counter"
grep -oP 'tests=\K[^,\]\s]+' "$SA_RULES_LOG" 2>/dev/null | tr ',' '\n' | tr -d ' ' | sort | uniq -c | sort -rn | head -15 | while read -r count rule; do
@@ -699,6 +762,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
fi
# Daemon status
echo ""
echo "# HELP postfix_spamassassin_up SpamAssassin daemon status"
echo "# TYPE postfix_spamassassin_up gauge"
if pgrep -f "${SPAM_DAEMON}" &>/dev/null; then
@@ -707,6 +771,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
echo "postfix_spamassassin_up{daemon=\"${SPAM_DAEMON}\",hostname=\"${HOSTNAME}\"} 0"
fi
echo ""
echo "# HELP postfix_spamassassin_processes Number of spam daemon processes"
echo "# TYPE postfix_spamassassin_processes gauge"
spam_procs=$(pgrep -c -f "${SPAM_DAEMON}" 2>/dev/null) || spam_procs=0
@@ -714,6 +779,7 @@ if [[ -n "$SPAM_DAEMON" ]]; then
fi
# Greylisting stats (postgrey)
echo ""
echo "# HELP postfix_greylist_total Greylisting events"
echo "# TYPE postfix_greylist_total counter"
greylist_defer=$(grep_count 'action=greylist' "$LOG_FILE")
@@ -723,6 +789,7 @@ echo "postfix_greylist_total{action=\"defer\",hostname=\"${HOSTNAME}\"} ${greyli
echo "postfix_greylist_total{action=\"pass\",hostname=\"${HOSTNAME}\"} ${greylist_pass}"
echo "postfix_greylist_total{action=\"whitelist\",hostname=\"${HOSTNAME}\"} ${greylist_whitelist}"
echo ""
echo "# HELP postfix_greylist_reason_total Greylisting by reason"
echo "# TYPE postfix_greylist_reason_total counter"
grey_new=$(grep_count 'reason=new' "$LOG_FILE")
@@ -732,6 +799,7 @@ echo "postfix_greylist_reason_total{reason=\"new\",hostname=\"${HOSTNAME}\"} ${g
echo "postfix_greylist_reason_total{reason=\"early_retry\",hostname=\"${HOSTNAME}\"} ${grey_early}"
echo "postfix_greylist_reason_total{reason=\"triplet_found\",hostname=\"${HOSTNAME}\"} ${grey_triplet}"
echo ""
echo "# HELP postfix_greylist_delay_seconds Greylist delay statistics"
echo "# TYPE postfix_greylist_delay_seconds gauge"
avg_delay=$(grep -oP 'delay=\K\d+' "$LOG_FILE" 2>/dev/null | grep -v '^0$' | awk '{sum+=$1; count++} END {if(count>0) printf "%.0f", sum/count; else print 0}')
@@ -739,11 +807,13 @@ max_delay=$(grep -oP 'postgrey.*delay=\K\d+' "$LOG_FILE" 2>/dev/null | sort -rn
echo "postfix_greylist_delay_seconds{type=\"avg\",hostname=\"${HOSTNAME}\"} ${avg_delay:-0}"
echo "postfix_greylist_delay_seconds{type=\"max\",hostname=\"${HOSTNAME}\"} ${max_delay:-0}"
echo ""
echo "# HELP postfix_greylist_clients_total Unique greylisted client IPs"
echo "# TYPE postfix_greylist_clients_total gauge"
grey_clients=$(grep 'action=greylist' "$LOG_FILE" 2>/dev/null | grep -oP 'client_address=\K[^,]+' | sort -u | wc -l)
echo "postfix_greylist_clients_total{hostname=\"${HOSTNAME}\"} ${grey_clients:-0}"
echo ""
echo "# HELP postfix_greylist_top_senders Top greylisted sender domains"
echo "# TYPE postfix_greylist_top_senders counter"
grep 'action=greylist' "$LOG_FILE" 2>/dev/null | grep -oP 'sender=\K[^,]+' | sed 's/.*@//' | sort | uniq -c | sort -rn | head -10 | while read -r count domain; do
@@ -751,18 +821,21 @@ grep 'action=greylist' "$LOG_FILE" 2>/dev/null | grep -oP 'sender=\K[^,]+' | sed
done
# Cleanup daemon stats (total messages entering system)
echo ""
echo "# HELP postfix_cleanup_total Messages processed by cleanup daemon"
echo "# TYPE postfix_cleanup_total counter"
cleanup_count=$(grep_count 'message-id=' "$LOG_FILE")
echo "postfix_cleanup_total{hostname=\"${HOSTNAME}\"} ${cleanup_count}"
# Virtual mailbox errors
echo ""
echo "# HELP postfix_virtual_errors_total Virtual mailbox lookup errors"
echo "# TYPE postfix_virtual_errors_total counter"
virtual_not_found=$(grep_count 'mailbox not found\|User unknown in virtual' "$LOG_FILE")
echo "postfix_virtual_errors_total{hostname=\"${HOSTNAME}\"} ${virtual_not_found}"
# Address verification failures
echo ""
echo "# HELP postfix_address_verify_total Address verification events"
echo "# TYPE postfix_address_verify_total counter"
verify_fail=$(grep_count 'address verification failed' "$LOG_FILE")
@@ -771,6 +844,7 @@ echo "postfix_address_verify_total{result=\"failed\",hostname=\"${HOSTNAME}\"} $
echo "postfix_address_verify_total{result=\"success\",hostname=\"${HOSTNAME}\"} ${verify_success}"
# Postfix master process uptime (based on pid file age)
echo ""
echo "# HELP postfix_master_uptime_seconds Postfix master process uptime"
echo "# TYPE postfix_master_uptime_seconds gauge"
MASTER_PID_FILE="/var/spool/postfix/pid/master.pid"
@@ -787,6 +861,7 @@ fi
echo "postfix_master_uptime_seconds{hostname=\"${HOSTNAME}\"} ${uptime_seconds}"
# DNS lookup failures
echo ""
echo "# HELP postfix_dns_errors_total DNS lookup errors"
echo "# TYPE postfix_dns_errors_total counter"
dns_not_found=$(grep_count 'Host not found\|Name service error\|Host or domain name not found' "$LOG_FILE")
@@ -799,6 +874,7 @@ echo "postfix_dns_errors_total{type=\"servfail\",hostname=\"${HOSTNAME}\"} ${dns
# STARTTLS usage - count TLS connections vs total SMTP connections
# "used" = successful TLS connections (inbound + outbound)
# "total" = total SMTP connections for ratio calculation
echo ""
echo "# HELP postfix_starttls_total STARTTLS connection counts"
echo "# TYPE postfix_starttls_total counter"
starttls_inbound=$(grep_count 'TLS connection established from' "$LOG_FILE")
@@ -807,6 +883,7 @@ echo "postfix_starttls_total{type=\"inbound\",hostname=\"${HOSTNAME}\"} ${startt
echo "postfix_starttls_total{type=\"outbound\",hostname=\"${HOSTNAME}\"} ${starttls_outbound}"
# Sender/recipient access rejections
echo ""
echo "# HELP postfix_access_reject_total Sender/recipient access rejections"
echo "# TYPE postfix_access_reject_total counter"
sender_reject=$(grep_count 'Sender address rejected' "$LOG_FILE")
@@ -817,12 +894,14 @@ echo "postfix_access_reject_total{type=\"recipient\",hostname=\"${HOSTNAME}\"} $
echo "postfix_access_reject_total{type=\"client\",hostname=\"${HOSTNAME}\"} ${client_reject}"
# Queue filesystem usage
echo ""
echo "# HELP postfix_queue_filesystem_usage_percent Queue filesystem usage percentage"
echo "# TYPE postfix_queue_filesystem_usage_percent gauge"
queue_usage=$(df "${QUEUE_DIR}" 2>/dev/null | awk 'NR==2 {gsub(/%/,""); print $5}') || queue_usage=0
echo "postfix_queue_filesystem_usage_percent{hostname=\"${HOSTNAME}\"} ${queue_usage:-0}"
# Postfix file descriptor count (for master process)
echo ""
echo "# HELP postfix_file_descriptors Open file descriptors by postfix"
echo "# TYPE postfix_file_descriptors gauge"
if [[ -f "$MASTER_PID_FILE" ]]; then
@@ -839,6 +918,7 @@ echo "postfix_file_descriptors{hostname=\"${HOSTNAME}\"} ${fd_count}"
# Script execution time
# Dovecot IMAP/POP3 login metrics
echo ""
echo "# HELP dovecot_logins_total Successful logins by protocol"
echo "# TYPE dovecot_logins_total counter"
imap_logins=$(grep_count 'imap-login: Info: Login:' "$LOG_FILE")
@@ -846,6 +926,7 @@ pop3_logins=$(grep_count 'pop3-login: Info: Login:' "$LOG_FILE")
echo "dovecot_logins_total{protocol=\"imap\",hostname=\"${HOSTNAME}\"} ${imap_logins}"
echo "dovecot_logins_total{protocol=\"pop3\",hostname=\"${HOSTNAME}\"} ${pop3_logins}"
echo ""
echo "# HELP dovecot_login_auth_method_total Logins by authentication method"
echo "# TYPE dovecot_login_auth_method_total counter"
for method in PLAIN LOGIN CRAM-MD5 DIGEST-MD5; do
@@ -853,6 +934,7 @@ for method in PLAIN LOGIN CRAM-MD5 DIGEST-MD5; do
echo "dovecot_login_auth_method_total{method=\"${method}\",hostname=\"${HOSTNAME}\"} ${count}"
done
echo ""
echo "# HELP dovecot_login_tls_total Logins with/without TLS"
echo "# TYPE dovecot_login_tls_total counter"
tls_logins=$(grep -c 'Login:.*TLS' "$LOG_FILE" 2>/dev/null) || tls_logins=0
@@ -860,6 +942,7 @@ notls_logins=$(grep 'Login:' "$LOG_FILE" 2>/dev/null | grep -cv 'TLS') || notls_
echo "dovecot_login_tls_total{tls=\"yes\",hostname=\"${HOSTNAME}\"} ${tls_logins}"
echo "dovecot_login_tls_total{tls=\"no\",hostname=\"${HOSTNAME}\"} ${notls_logins}"
echo ""
echo "# HELP dovecot_login_failed_total Failed login attempts"
echo "# TYPE dovecot_login_failed_total counter"
imap_failed=$(grep_count 'imap-login: Info: Aborted login\|imap-login:.*auth failed' "$LOG_FILE")
@@ -867,12 +950,14 @@ pop3_failed=$(grep_count 'pop3-login: Info: Aborted login\|pop3-login:.*auth fai
echo "dovecot_login_failed_total{protocol=\"imap\",hostname=\"${HOSTNAME}\"} ${imap_failed}"
echo "dovecot_login_failed_total{protocol=\"pop3\",hostname=\"${HOSTNAME}\"} ${pop3_failed}"
echo ""
echo "# HELP dovecot_login_user_total Logins per user (top 20)"
echo "# TYPE dovecot_login_user_total counter"
grep -oP 'Login: user=<\K[^>]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -20 | while read -r count user; do
echo "dovecot_login_user_total{user=\"${user}\",hostname=\"${HOSTNAME}\"} ${count}"
done
echo ""
echo "# HELP dovecot_login_client_ip_total Logins per client IP (top 20)"
echo "# TYPE dovecot_login_client_ip_total counter"
grep -oP 'Login:.*rip=\K[^,]+' "$LOG_FILE" 2>/dev/null | sort | uniq -c | sort -rn | head -20 | while read -r count ip; do
@@ -883,10 +968,12 @@ local END_TIME
END_TIME=$(date +%s.%N)
local DURATION
DURATION=$(echo "$END_TIME - $START_TIME" | bc)
echo ""
echo "# HELP postfix_collector_duration_seconds Time taken to collect metrics"
echo "# TYPE postfix_collector_duration_seconds gauge"
echo "postfix_collector_duration_seconds{hostname=\"${HOSTNAME}\"} ${DURATION}"
echo ""
echo "# HELP postfix_collector_last_run_timestamp Unix timestamp of last collection"
echo "# TYPE postfix_collector_last_run_timestamp gauge"
echo "postfix_collector_last_run_timestamp{hostname=\"${HOSTNAME}\"} $(date +%s)"