#!/usr/bin/env bash # # install_opensmtpd_sendonly.sh v1.2 # OpenSMTPD + OpenDKIM + TLS(Send-Only 模式) # -------------------------------------------------------- # ✅ 自动部署轻量级 MTA,监听 25/587 端口(免认证) # ✅ 集成 DKIM 签名、SPF/DMARC/rDNS/HELO 校验模板 # ✅ 兼容阿里云 / Cloudflare DNS 输出格式 # ✅ 适配 OpenSMTPD ≥ 6.8(Ubuntu 22.04+) # -------------------------------------------------------- # Author: Pan Haitao @ svc.plus # set -euo pipefail DOMAIN="svc.plus" HOSTNAME="smtp.${DOMAIN}" SERVER_IP="52.196.108.28" EMAIL="no-reply@${DOMAIN}" CERT="/etc/ssl/${DOMAIN}.pem" KEY="/etc/ssl/${DOMAIN}.key" DKIM_SELECTOR="mail" DKIM_KEY_DIR="/etc/opendkim/keys/${DOMAIN}" TMP_PASS="$(openssl rand -base64 12)" ACTION="${1:-help}" log(){ echo -e "\033[1;36m$*\033[0m"; } die(){ echo "❌ $*"; exit 1; } check_root(){ [ "$EUID" -eq 0 ] || die "请用 root 运行"; } # ------------------ 应用端配置 ------------------ show_app_config(){ cat <" tls: mode: "auto" insecureSkipVerify: false auth: "login" ---------------------------------------------------------- EOF echo "首发密码(仅本次显示):${TMP_PASS}" } check_send_email() { local SMTP_HOST="${HOSTNAME:-smtp.svc.plus}" local SMTP_PORT=587 local SMTP_USER="${EMAIL:-no-reply@svc.plus}" local SMTP_PASS="${TMP_PASS:-$(grep -m1 "${EMAIL:-no-reply@svc.plus}" /etc/smtpd/auth 2>/dev/null | awk -F: '{print $2}' || echo '')}" local TEST_TO="${1:-${EMAIL:-no-reply@svc.plus}}" local SUBJECT="📨 SMTP Deliverability Test — $(date '+%Y-%m-%d %H:%M:%S')" local BODY="✅ Automated deliverability test from ${SMTP_HOST} Environment: - HELO: $(hostname -f) - Source IP: $(curl -s ifconfig.me 2>/dev/null || echo 'unknown') - TLS: STARTTLS on ${SMTP_PORT} - Auth: LOGIN (${SMTP_USER}) If you received this message intact, DKIM/DMARC/SPF validation succeeded." echo echo "🔍 Testing outbound mail via ${SMTP_HOST}:${SMTP_PORT}" echo "-------------------------------------------------------------" # 检测 swaks 是否支持 --hide-password local SWAKS_HIDE="" if swaks --help 2>&1 | grep -q -- '--hide-password'; then SWAKS_HIDE="--hide-password" fi swaks --server "${SMTP_HOST}:${SMTP_PORT}" \ --tls --protocol ESMTP \ --auth LOGIN \ --auth-user "${SMTP_USER}" \ --auth-password "${SMTP_PASS}" \ --from "${SMTP_USER}" \ --to "${TEST_TO}" \ --header "From: XControl Mail System <${SMTP_USER}>" \ --header "Reply-To: ${SMTP_USER}" \ --header "Subject: ${SUBJECT}" \ --body "${BODY}" \ --timeout 15 ${SWAKS_HIDE} --quit-after "." echo "-------------------------------------------------------------" echo "✅ Check inbox (${TEST_TO}) for DKIM/SPF validation results." } # ------------------ 安装依赖 ------------------ ensure_packages(){ log "📦 安装 OpenSMTPD + OpenDKIM..." apt update -qq DEBIAN_FRONTEND=noninteractive apt install -y \ opensmtpd opendkim opendkim-tools dnsutils curl openssl swaks } # ------------------ SSL 证书检测 ------------------ verify_cert(){ if [[ -f "$CERT" && -f "$KEY" ]]; then log "🔐 使用自有 SSL 证书:$CERT" openssl x509 -noout -subject -dates -in "$CERT" || true else log "⚠️ 未检测到 ${CERT}/${KEY},生成自签证书..." mkdir -p /etc/ssl openssl req -x509 -nodes -newkey rsa:2048 -days 365 \ -subj "/CN=${HOSTNAME}" -keyout "$KEY" -out "$CERT" fi } # ------------------ DKIM ------------------ deploy_dkim(){ log "🔏 配置 OpenDKIM..." mkdir -p "${DKIM_KEY_DIR}" cd "${DKIM_KEY_DIR}" if [ ! -f "${DKIM_SELECTOR}.private" ]; then opendkim-genkey -s "${DKIM_SELECTOR}" -d "${DOMAIN}" chown opendkim:opendkim "${DKIM_SELECTOR}.private" "${DKIM_SELECTOR}.txt" chmod 600 "${DKIM_SELECTOR}.private" fi cat >/etc/opendkim.conf </etc/opendkim/key.table </etc/opendkim/signing.table </etc/opendkim/trusted.hosts </etc/smtpd/smtpd.conf </dev/null || true fi ln -sf /etc/smtpd/smtpd.conf /etc/smtpd.conf # 语法校验 if ! smtpd -n -f /etc/smtpd/smtpd.conf > /tmp/smtpd_check.log 2>&1; then log "⚠️ 配置语法检测失败:" cat /tmp/smtpd_check.log die "配置无效,已终止启动。" fi # 启动并启用服务 systemctl enable --now opensmtpd || die "❌ 启动 opensmtpd 失败" sleep 1 # 若仍监听 25 端口则强制关闭 if ss -tlnp | grep -qE ':25\s'; then log "🚫 检测到 25 端口仍在监听,强制关闭..." fuser -k 25/tcp 2>/dev/null || true systemctl restart opensmtpd fi # 再次确认状态 if ss -tlnp | grep -qE ':587\s'; then log "✅ OpenSMTPD 6.8 已启用并监听 587 端口(STARTTLS Send-Only 模式)" else die "❌ 端口 587 未成功监听,请检查日志:journalctl -xeu opensmtpd.service" fi } # ------------------ DNS 模板 ------------------ show_dns_record(){ log "🌐 生成 DNS 模板(SPF / DKIM / DMARC / rDNS / HELO)..." local DKIM_TXT DKIM_PUB LINE LEN=255 if [[ -f "${DKIM_KEY_DIR}/${DKIM_SELECTOR}.txt" ]]; then DKIM_TXT=$(tr -d '\n' < "${DKIM_KEY_DIR}/${DKIM_SELECTOR}.txt" | sed 's/"//g') DKIM_PUB=$(echo "${DKIM_TXT}" | sed -n 's/.*p=\(.*\)$/\1/p' | tr -d ' ') else DKIM_PUB="" fi echo "----------------------------------------------------------" echo "A smtp.${DOMAIN} ${SERVER_IP}" echo "MX ${DOMAIN} smtp.${DOMAIN}." echo "SPF @ \"v=spf1 a:smtp.${DOMAIN} -all\"" echo -n "DKIM ${DKIM_SELECTOR}._domainkey " echo "\"v=DKIM1; k=rsa; p=" while [[ -n "$DKIM_PUB" ]]; do LINE=${DKIM_PUB:0:$LEN} DKIM_PUB=${DKIM_PUB:$LEN} echo "\"${LINE}\"" done echo "DMARC _dmarc \"v=DMARC1; p=none; rua=mailto:postmaster@${DOMAIN}\"" echo "rDNS (请让 ${SERVER_IP} 反查为 ${HOSTNAME})" echo "HELO (EHLO 输出应为 ${HOSTNAME})" echo "----------------------------------------------------------" } # ------------------ 自检 ------------------ check_self(){ log "🔍 自检 SPF / DKIM / DMARC / rDNS / 端口 ..." echo echo "SPF:"; dig +short TXT ${DOMAIN} | grep -i spf || echo "⚠️ 未配置 SPF"; echo echo "DKIM:"; dig +short TXT ${DKIM_SELECTOR}._domainkey.${DOMAIN} || echo "⚠️ 未配置 DKIM"; echo echo "DMARC:"; dig +short TXT _dmarc.${DOMAIN} || echo "⚠️ 未配置 DMARC"; echo echo "rDNS:"; dig +short -x ${SERVER_IP} || echo "⚠️ 未配置反向解析"; echo echo "端口监听:"; ss -tlnp | grep -E '(:25|:587|:8891)\s' || echo "⚠️ 端口未监听"; echo echo "OpenDKIM testkey:"; opendkim-testkey -d "${DOMAIN}" -s "${DKIM_SELECTOR}" -vvv || true } # ------------------ 卸载 ------------------ uninstall_reset(){ log "🧹 停止并清理..." systemctl stop opensmtpd || true systemctl stop opendkim || true apt purge -y opensmtpd opendkim opendkim-tools || true apt autoremove -y || true rm -rf /etc/smtpd /etc/opendkim /var/log/mail* log "✅ 已清理完成(证书未动)。" } # ------------------ 主流程 ------------------ check_root case "${ACTION}" in deploy) ensure_packages deploy_dkim deploy_smtpd show_dns_record ;; upgrade) log "⬆️ 更新配置并重启..." deploy_dkim deploy_smtpd show_dns_record ;; show) case "${2:-}" in dns_record) show_dns_record ;; app_config) show_app_config ;; *) echo "用法: $0 show {dns_record|app_config}" ;; esac ;; check) case "${2:-}" in self) check_self ;; send_email) check_send_email ;; *) echo "用法: $0 check {self|send_email}" ;; esac ;; uninstall|reset) uninstall_reset ;; help|--help|-h) echo "用法: $0 {deploy|upgrade|show {dns_record|app_config}|check {self|send_email}|uninstall}" ;; *) echo "用法: $0 {deploy|upgrade|show {dns_record|app_config}|check {self|send_email}|uninstall}" ;; esac