Files
linux-scripts/postfix-relay-install.sh
chiefgeek a1a17e81a1 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.
2026-05-25 03:31:08 +02:00

456 lines
12 KiB
Bash

#!/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 <<EOF
Usage: $0 [OPTIONS]
Installs and configures Postfix as a satellite relay (outbound only).
OPTIONS:
--relay HOST Relay host FQDN or IP (required)
--port PORT Relay port: 25, 465, 587 (default: 587)
--user USER SASL authentication username
--pass PASS SASL authentication password
--from ADDRESS Rewrite all sender addresses to this
--hostname NAME Set Postfix myhostname
--root-alias ADDR Forward root mail to this address
--tls MODE TLS mode: may, required, wrapper (default: may)
--no-firewall Skip firewall configuration
--dry-run Show what would be done
-h, --help Show this help
EXAMPLES:
sudo $0 --relay smtp.example.com --port 587
sudo $0 --relay smtp.example.com --user alerts@example.com --pass AppPass
sudo $0 --relay smtp.example.com --from noreply@example.com --tls required
EOF
exit 0
}
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help) show_usage ;;
--relay) RELAY_HOST="$2"; shift 2 ;;
--port) RELAY_PORT="$2"; shift 2 ;;
--user) SASL_USER="$2"; shift 2 ;;
--pass) SASL_PASS="$2"; shift 2 ;;
--from) FROM_ADDRESS="$2"; shift 2 ;;
--hostname) HOSTNAME_OVERRIDE="$2"; shift 2 ;;
--root-alias) ROOT_ALIAS="$2"; shift 2 ;;
--tls) TLS_MODE="$2"; shift 2 ;;
--no-firewall) NO_FIREWALL=true; shift ;;
--dry-run) DRY_RUN=true; shift ;;
*) log_error "Unknown option: $1"; exit 1 ;;
esac
done
if [ -z "$RELAY_HOST" ]; then
log_error "--relay is required"
echo ""
show_usage
fi
}
check_root() {
if [ "$(id -u)" -ne 0 ]; then
log_error "This script must be run as root"
exit 1
fi
}
# ============================================================================
# OS DETECTION
# ============================================================================
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS_FAMILY=""
case "$ID" in
debian|ubuntu) OS_FAMILY="debian" ;;
rhel|centos|rocky|almalinux|fedora) OS_FAMILY="rhel" ;;
*)
if [ -n "${ID_LIKE:-}" ]; then
case "$ID_LIKE" in
*debian*|*ubuntu*) OS_FAMILY="debian" ;;
*rhel*|*centos*|*fedora*) OS_FAMILY="rhel" ;;
esac
fi
;;
esac
if [ -z "$OS_FAMILY" ]; then
log_error "Unsupported OS: $ID"
exit 1
fi
log_info "Detected OS: $PRETTY_NAME (family: $OS_FAMILY)"
else
log_error "Cannot detect OS"
exit 1
fi
}
# ============================================================================
# INSTALLATION
# ============================================================================
remove_conflicting_mta() {
log_step "Removing conflicting MTAs..."
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would remove sendmail/exim if present"
return
fi
case "$OS_FAMILY" in
debian)
apt-get remove -y -qq exim4 exim4-base exim4-daemon-light sendmail 2>/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 <<EOF
# Postfix Satellite Relay Configuration
# Generated by postfix-relay-install.sh v1.0
# Identity
myhostname = $my_hostname
mydomain = $my_domain
myorigin = \$mydomain
# Relay configuration
relayhost = [$RELAY_HOST]:$RELAY_PORT
# Listen only on loopback — relay only, no inbound mail
inet_interfaces = loopback-only
inet_protocols = all
mydestination = \$myhostname, localhost.\$mydomain, localhost
# SASL authentication
smtp_sasl_auth_enable = $([ -n "$SASL_USER" ] && echo "yes" || echo "no")
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
# TLS configuration
smtp_use_tls = yes
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_tls_security_level = $TLS_MODE
smtp_tls_loglevel = 1
# Misc
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mailbox_size_limit = 0
recipient_delimiter = +
compatibility_level = 2
# Queue settings
maximal_queue_lifetime = 1d
bounce_queue_lifetime = 1d
EOF
# Handle TLS wrapper mode (port 465)
if [ "$TLS_MODE" = "wrapper" ]; then
postconf -e "smtp_tls_wrappermode = yes"
postconf -e "smtp_tls_security_level = encrypt"
fi
# Fix CA cert path for RHEL
if [ "$OS_FAMILY" = "rhel" ]; then
postconf -e "smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt"
fi
log_info "main.cf configured"
}
configure_sasl() {
if [ -z "$SASL_USER" ]; then
return
fi
log_step "Configuring SASL authentication..."
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would create sasl_passwd"
return
fi
echo "[$RELAY_HOST]:$RELAY_PORT $SASL_USER:$SASL_PASS" > /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 "$@"