#!/bin/bash ################################################################################ # Script Name: postfix-relay-install.sh # Version: 1.0 # Description: Automated Postfix satellite relay installation — configures # Postfix as a relay-only MTA with SASL auth, TLS, sender # rewriting, and firewall lockdown # # Author: Phil Connor # Contact: contact@mylinux.work # Website: https://mylinux.work # License: MIT # # Usage: # sudo ./postfix-relay-install.sh --relay smtp.example.com --port 587 # sudo ./postfix-relay-install.sh --relay smtp.example.com --user user --pass pass # sudo ./postfix-relay-install.sh --dry-run # ################################################################################ set -euo pipefail # ============================================================================ # DEFAULTS # ============================================================================ RELAY_HOST="" RELAY_PORT="587" SASL_USER="" SASL_PASS="" FROM_ADDRESS="" HOSTNAME_OVERRIDE="" ROOT_ALIAS="" TLS_MODE="may" NO_FIREWALL=false DRY_RUN=false # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # ============================================================================ # HELPER FUNCTIONS # ============================================================================ log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } log_step() { echo -e "${CYAN}[STEP]${NC} $*"; } show_usage() { cat </dev/null || true ;; rhel) dnf remove -y -q sendmail 2>/dev/null || true ;; esac } install_postfix() { log_step "Installing Postfix..." if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would install postfix and dependencies" return fi case "$OS_FAMILY" in debian) DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \ postfix libsasl2-modules mailutils ca-certificates >/dev/null 2>&1 ;; rhel) dnf install -y -q \ postfix cyrus-sasl-plain mailx ca-certificates >/dev/null 2>&1 ;; esac log_info "Postfix installed" } # ============================================================================ # CONFIGURATION # ============================================================================ configure_relay() { log_step "Configuring relay host: [$RELAY_HOST]:$RELAY_PORT" if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would configure main.cf" return fi local my_hostname my_hostname="${HOSTNAME_OVERRIDE:-$(hostname -f 2>/dev/null || hostname)}" local my_domain my_domain=$(echo "$my_hostname" | sed 's/^[^.]*\.//') # Build main.cf cat > /etc/postfix/main.cf < /etc/postfix/sasl_passwd postmap /etc/postfix/sasl_passwd chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db log_info "SASL credentials configured" } configure_sender_rewrite() { if [ -z "$FROM_ADDRESS" ]; then return fi log_step "Configuring sender rewriting: $FROM_ADDRESS" if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would create sender_canonical" return fi echo "/.+/ $FROM_ADDRESS" > /etc/postfix/sender_canonical_regexp postconf -e "sender_canonical_maps = regexp:/etc/postfix/sender_canonical_regexp" log_info "Sender rewriting configured" } configure_aliases() { if [ -z "$ROOT_ALIAS" ]; then return fi log_step "Configuring root alias: $ROOT_ALIAS" if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would set root alias" return fi if grep -q "^root:" /etc/aliases 2>/dev/null; then sed -i "s/^root:.*$/root: $ROOT_ALIAS/" /etc/aliases else echo "root: $ROOT_ALIAS" >> /etc/aliases fi newaliases log_info "Root alias set" } configure_firewall() { if [ "$NO_FIREWALL" = true ]; then return fi log_step "Configuring firewall (blocking inbound SMTP)..." if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would block inbound port 25" return fi # UFW if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "active"; then ufw deny in 25/tcp >/dev/null 2>&1 || true log_info "UFW: blocked inbound port 25" fi # firewalld if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld 2>/dev/null; then firewall-cmd --permanent --remove-service=smtp >/dev/null 2>&1 || true firewall-cmd --reload >/dev/null 2>&1 || true log_info "firewalld: removed smtp service" fi } # ============================================================================ # SERVICE MANAGEMENT # ============================================================================ start_postfix() { log_step "Starting Postfix..." if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would enable and start postfix" return fi systemctl enable postfix >/dev/null 2>&1 systemctl restart postfix if systemctl is-active --quiet postfix; then log_info "Postfix is running" else log_error "Postfix failed to start" journalctl -u postfix --no-pager -n 10 exit 1 fi } send_test() { log_step "Sending test email..." if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would send test email" return fi local test_to="${ROOT_ALIAS:-root}" echo "Test email from $(hostname) via Postfix relay through $RELAY_HOST" | \ mail -s "Postfix Relay Test — $(hostname) — $(date '+%Y-%m-%d %H:%M')" "$test_to" 2>/dev/null || true log_info "Test email queued (check $test_to inbox)" } # ============================================================================ # VERIFICATION # ============================================================================ verify() { log_step "Verifying configuration..." echo "" if systemctl is-active --quiet postfix; then log_info "✓ Postfix service: running" else log_error "✗ Postfix service: not running" fi log_info "Relay host: $(postconf -h relayhost 2>/dev/null)" log_info "TLS level: $(postconf -h smtp_tls_security_level 2>/dev/null)" log_info "SASL auth: $(postconf -h smtp_sasl_auth_enable 2>/dev/null)" log_info "Interfaces: $(postconf -h inet_interfaces 2>/dev/null)" echo "" log_info "Mail queue:" mailq 2>/dev/null | tail -5 echo "" log_info "Installation complete" echo "" log_info "Useful commands:" echo " mailq — view mail queue" echo " postqueue -f — flush mail queue" echo " tail -f /var/log/mail.log — watch mail log (Debian)" echo " tail -f /var/log/maillog — watch mail log (RHEL)" echo " echo test | mail -s test root — send test email" } # ============================================================================ # MAIN # ============================================================================ main() { parse_args "$@" check_root detect_os echo "" log_info "=== Postfix Relay Install Script v1.0 ===" echo "" if [ "$DRY_RUN" = true ]; then log_warn "DRY RUN MODE — no changes will be made" echo "" fi remove_conflicting_mta install_postfix configure_relay configure_sasl configure_sender_rewrite configure_aliases configure_firewall start_postfix send_test if [ "$DRY_RUN" != true ]; then verify fi } main "$@"