accounts/scripts/install_stalwart_mailserver.sh
2025-11-01 20:27:32 +08:00

297 lines
7.6 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# install_stalwart_mailserver.sh v2.3
# --------------------------------------------------------
# ✅ 安装 Stalwart Mail ServerRust版官方 install.sh
# ✅ SMTPS(465) + IMAPS(993),全程 TLS-only
# ✅ 支持 S3 / MinIO 存储
# ✅ 支持 JSON 用户认证(默认)
# ✅ 自动生成 DKIM + SPF/DMARC DNS 模板
# ✅ Systemd 管理
# ✅ 无 WebUI由外部 Dashboard 对接)
# --------------------------------------------------------
# Author: Pan Haitao @ svc.plus
#
set -euo pipefail
DOMAIN="svc.plus"
HOSTNAME="mail.${DOMAIN}"
SERVER_IP="$(curl -s https://api.ipify.org || echo 127.0.0.1)"
CERT_DIR="/etc/ssl"
CERT="${CERT_DIR}/${DOMAIN}.pem"
KEY="${CERT_DIR}/${DOMAIN}.key"
STALWART_DIR="/etc/stalwart"
STALWART_BIN="/usr/local/bin/stalwart-mail"
STALWART_CFG="${STALWART_DIR}/stalwart.toml"
DKIM_KEY_DIR="${STALWART_DIR}/dkim"
DKIM_SELECTOR="mail"
SERVICE_USER="stalwart"
SERVICE_GROUP="stalwart"
ACTION="${1:-help}"
# S3 存储参数
S3_ENDPOINT="https://minio.svc.plus:9000"
S3_BUCKET="svcplus-mail"
S3_ACCESS_KEY="MINIO_ACCESS_KEY"
S3_SECRET_KEY="MINIO_SECRET_KEY"
ADMIN_EMAIL="admin@${DOMAIN}"
log(){ echo -e "\033[1;36m$*\033[0m"; }
die(){ echo "$*"; exit 1; }
check_root(){ [ "$EUID" -eq 0 ] || die "请以 root 运行"; }
# ------------------ 依赖 ------------------
ensure_packages(){
log "📦 安装依赖..."
apt update -qq
apt install -y curl openssl jq dnsutils opendkim opendkim-tools swaks tar
}
# ------------------ 安装 Stalwart ------------------
install_stalwart(){
if ! command -v stalwart-mail &>/dev/null; then
log "⬇️ 通过官方脚本安装 Stalwart Mail Server..."
curl --proto '=https' --tlsv1.2 -sSf https://get.stalw.art/install.sh -o /tmp/install.sh
bash /tmp/install.sh
else
log "✅ 已检测到 Stalwart$(stalwart-mail --version)"
fi
}
# ------------------ 证书 ------------------
verify_cert(){
mkdir -p "$CERT_DIR"
if [[ -f "$CERT" && -f "$KEY" ]]; then
log "🔐 使用现有证书"
else
log "⚠️ 未检测到证书,生成自签..."
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-subj "/CN=${HOSTNAME}" -keyout "$KEY" -out "$CERT"
fi
}
# ------------------ DKIM ------------------
deploy_dkim(){
log "🔏 生成 DKIM 密钥..."
mkdir -p "${DKIM_KEY_DIR}"
cd "${DKIM_KEY_DIR}"
if [ ! -f "${DKIM_SELECTOR}.private" ]; then
opendkim-genkey -s "${DKIM_SELECTOR}" -d "${DOMAIN}"
chmod 600 "${DKIM_SELECTOR}.private"
fi
}
# ------------------ 配置文件 ------------------
generate_config(){
log "⚙️ 生成 stalwart.toml..."
mkdir -p "${STALWART_DIR}"
cat >"${STALWART_CFG}" <<EOF
# =====================================================
# Stalwart Mail Server Configuration (Core Only)
# =====================================================
[server]
hostname = "${HOSTNAME}"
data-dir = "/var/lib/stalwart"
user = "${SERVICE_USER}"
[tls]
cert = "${CERT}"
key = "${KEY}"
# SMTPS 465
[smtp]
listen = ["0.0.0.0:465"]
require_tls = true
tls_implicit = true
auth = "users"
# IMAPS 993
[imap]
listen = ["0.0.0.0:993"]
require_tls = true
auth = "users"
# 存储后端S3/MinIO
[store]
backend = "s3"
bucket = "${S3_BUCKET}"
endpoint = "${S3_ENDPOINT}"
access-key = "${S3_ACCESS_KEY}"
secret-key = "${S3_SECRET_KEY}"
region = "us-east-1"
path-style = true
tls = true
# JSON 用户认证
[auth.users]
backend = "json"
path = "${STALWART_DIR}/users.json"
# DKIM
[dns.dkim]
selector = "${DKIM_SELECTOR}"
private-key = "${DKIM_KEY_DIR}/${DKIM_SELECTOR}.private"
domain = "${DOMAIN}"
# Metrics & Logs
[metrics]
listen = ["127.0.0.1:9090"]
[log]
level = "info"
output = "journald"
EOF
# 默认账户
if [[ ! -f "${STALWART_DIR}/users.json" ]]; then
cat >"${STALWART_DIR}/users.json" <<JSON
{
"users": [
{
"email": "demo@${DOMAIN}",
"password": "\$2y\$12\$1UZ7dEK3T2xKqacbPO5KUOJdyq8JcKAAwPKZt8SKmwbT39IM7Ch1O"
}
]
}
JSON
log "✅ 默认账户demo@${DOMAIN} / 密码demo123"
fi
}
# ------------------ Systemd ------------------
setup_systemd(){
log "🧩 配置 systemd 服务..."
cat >/etc/systemd/system/stalwart.service <<EOF
[Unit]
Description=Stalwart Mail Server (Core)
After=network-online.target
[Service]
ExecStart=${STALWART_BIN} serve -c ${STALWART_CFG}
Restart=on-failure
User=${SERVICE_USER}
Group=${SERVICE_GROUP}
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
EOF
id "${SERVICE_USER}" &>/dev/null || useradd -r -s /bin/false "${SERVICE_USER}"
chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${STALWART_DIR}" /var/lib/stalwart || true
systemctl daemon-reload
systemctl enable --now stalwart.service
}
# ------------------ DNS 模板 ------------------
show_dns_record(){
log "🌐 DNS 模板SPF / DKIM / DMARC / rDNS"
local DKIM_FILE="${DKIM_KEY_DIR}/${DKIM_SELECTOR}.txt"
local DKIM_VAL="<未生成>"
[[ -f "$DKIM_FILE" ]] && DKIM_VAL=$(grep -v '^;' "$DKIM_FILE" | tr -d '\n' | sed -E 's/.*p=//;s/"//g')
cat <<EOF
----------------------------------------------------------
A mail.${DOMAIN} ${SERVER_IP}
MX ${DOMAIN} mail.${DOMAIN}.
SPF @ "v=spf1 a:mail.${DOMAIN} -all"
DKIM ${DKIM_SELECTOR}._domainkey "v=DKIM1; k=rsa; p=${DKIM_VAL}"
DMARC _dmarc "v=DMARC1; p=none; rua=mailto:postmaster@${DOMAIN}"
rDNS (反向解析 ${SERVER_IP} -> ${HOSTNAME})
HELO (应输出 ${HOSTNAME})
----------------------------------------------------------
EOF
}
# ------------------ 应用端配置 ------------------
show_app_config(){
cat <<EOF
📦 应用端配置:
----------------------------------------------------------
IMAP:
host: ${HOSTNAME}
port: 993
username: demo@${DOMAIN}
password: demo123
tls: true
SMTP:
host: ${HOSTNAME}
port: 465
username: demo@${DOMAIN}
password: demo123
tls: true
----------------------------------------------------------
EOF
}
# ------------------ 检查 ------------------
check_self(){
log "🔍 检查服务状态..."
systemctl is-active --quiet stalwart && log "✅ stalwart 正在运行" || die "❌ 未运行"
ss -tlnp | grep -E ':465|:993' || die "❌ 端口未监听"
log "✅ 服务运行正常"
}
check_send_email(){
log "✉️ 测试发信..."
swaks --server "${HOSTNAME}:465" \
--tls --auth LOGIN \
--auth-user "demo@${DOMAIN}" \
--auth-password "demo123" \
--from "demo@${DOMAIN}" \
--to "${ADMIN_EMAIL}" \
--header "Subject: ✅ Stalwart 测试 $(date '+%F %T')" \
--body "测试发信成功 $(date '+%F %T')"
}
# ------------------ 卸载 ------------------
uninstall_reset(){
log "🧹 卸载 Stalwart..."
systemctl disable --now stalwart || true
rm -f /etc/systemd/system/stalwart.service
rm -rf "${STALWART_DIR}" /var/lib/stalwart
log "✅ 已清理完成(证书保留)"
}
# ------------------ 主逻辑 ------------------
check_root
case "${ACTION}" in
deploy)
ensure_packages
install_stalwart
verify_cert
deploy_dkim
generate_config
setup_systemd
show_dns_record ;;
upgrade)
log "⬆️ 升级并重启..."
systemctl stop stalwart || true
install_stalwart
generate_config
systemctl restart stalwart
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}" ;;
esac