#!/bin/bash # Postfix IP-warmup scheduler. # # Gradually expands the outbound sending-IP rotation pool over a warmup window, # so we don't blast 20 brand-new IPs at consumer ISPs on day one (which looks # like snowshoe spam and torches reputation). # # IP layout: # .90 / mta01 -> dedicated Yahoo/AOL recovery IP (transport: yahooslow) # .91-.109 / mta02-mta20 -> rotation pool (transports: out02..out20) # # transport_maps = hash:/etc/postfix/transport, randmap:{} # - hash routes yahoo/aol -> yahooslow (the dedicated .90 trickle) # - randmap round-robins the source IP across the *active* rotation pool # # This script (run daily by cron) recomputes the active pool from the warmup # start date and reloads Postfix only when the pool changes. It never shrinks. # # Run once with `--start` to stamp the start date. set -euo pipefail STATE=/etc/postfix/pw-warmup-start MAINCF=/etc/postfix/main.cf POSTCONF=/usr/sbin/postconf POSTFIX=/usr/sbin/postfix # Rotation transports. .90 is Yahoo-dedicated, not in rotation. out02/03/04 # (.91/.92/.93) were reputation-torched by the May 30-31 over-volume blast and # are retired. Warmup starts at fresh out05 (.94). ALL=(out05 out06 out07 out08 out09 out10 out11 \ out12 out13 out14 out15 out16 out17 out18 out19 out20) if [ "${1:-}" = "--start" ]; then date +%s | sudo tee "$STATE" >/dev/null echo "warmup start stamped: $(date)" fi [ -f "$STATE" ] || date +%s | sudo tee "$STATE" >/dev/null START=$(cat "$STATE"); NOW=$(date +%s); DAYS=$(( (NOW - START) / 86400 )) # Warmup schedule: day-since-start -> active rotation IPs if [ "$DAYS" -le 3 ]; then N=3 elif [ "$DAYS" -le 7 ]; then N=5 elif [ "$DAYS" -le 11 ]; then N=8 elif [ "$DAYS" -le 17 ]; then N=12 elif [ "$DAYS" -le 24 ]; then N=16 else N=16; fi # Safety: never request more transports than exist in ALL. if [ "$N" -gt "${#ALL[@]}" ]; then N=${#ALL[@]}; fi POOL="" for ((i=0; i/dev/null || echo "") if [ "$CUR" != "$NEWVAL" ]; then sudo $POSTCONF -e "transport_maps=${NEWVAL}" sudo $POSTFIX reload >/dev/null 2>&1 || true logger -t pw-warmup "day $DAYS -> $N rotation IPs active" echo "$(date '+%F %T') warmup: day=$DAYS active_rotation_ips=$N" else echo "$(date '+%F %T') warmup: day=$DAYS active_rotation_ips=$N (no change)" fi