accounts/scripts/install_opensmtpd_sendonly.sh

306 lines
9.3 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
#
# install_opensmtpd_sendonly.sh v1.2
# OpenSMTPD + OpenDKIM + TLSSend-Only 模式)
# --------------------------------------------------------
# ✅ 自动部署轻量级 MTA监听 25/587 端口(免认证)
# ✅ 集成 DKIM 签名、SPF/DMARC/rDNS/HELO 校验模板
# ✅ 兼容阿里云 / Cloudflare DNS 输出格式
# ✅ 适配 OpenSMTPD ≥ 6.8Ubuntu 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 <<EOF
📦 应用端 SMTP 配置:
----------------------------------------------------------
smtp:
host: "${HOSTNAME}"
port: 587
username: "${EMAIL}"
password: "${TMP_PASS}"
from: "XControl Account <${EMAIL}>"
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 <<EOF
Syslog yes
UMask 002
Mode sv
Canonicalization relaxed/simple
SubDomains no
KeyTable /etc/opendkim/key.table
SigningTable /etc/opendkim/signing.table
ExternalIgnoreList refile:/etc/opendkim/trusted.hosts
InternalHosts refile:/etc/opendkim/trusted.hosts
Socket inet:8891@localhost
UserID opendkim
EOF
cat >/etc/opendkim/key.table <<EOF
${DKIM_SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${DKIM_SELECTOR}:${DKIM_KEY_DIR}/${DKIM_SELECTOR}.private
EOF
cat >/etc/opendkim/signing.table <<EOF
*@${DOMAIN} ${DKIM_SELECTOR}._domainkey.${DOMAIN}
EOF
cat >/etc/opendkim/trusted.hosts <<EOF
127.0.0.1
localhost
${DOMAIN}
EOF
chown -R opendkim:opendkim /etc/opendkim
systemctl enable --now opendkim
}
# ------------------ OpenSMTPD ------------------
deploy_smtpd() {
verify_cert
log "🚀 写入 OpenSMTPD 配置 (仅启用 587 / STARTTLS)..."
mkdir -p /etc/smtpd
cat >/etc/smtpd/smtpd.conf <<EOF
pki ${HOSTNAME} cert "${CERT}"
pki ${HOSTNAME} key "${KEY}"
listen on 0.0.0.0 port 587
listen on ::0 port 587
# 启用 TLS 支持STARTTLS
tls enable
pki ${HOSTNAME}
# 允许本机及 svc.plus 域发信
accept from local for any relay
accept from any for domain "${DOMAIN}" relay
EOF
# 若系统自带默认配置,先禁用
if [ -f /etc/smtpd.conf ]; then
log "⚙️ 检测到系统默认 /etc/smtpd.conf自动禁用..."
mv /etc/smtpd.conf /etc/smtpd.conf.disabled 2>/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="<DKIM 公钥未生成>"
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