自托管邮局服务Mailcow

搭建一个功能强大的邮件服务器吧

前不久其实已经在另一台机器搭建过,但是当时没有记录,这次刚好那台机器到期也不续费了,索性重新搭建一遍并记录下来

Mailcow介绍和搭建准备


Mailcow是一个功能很齐全,体量也相对较大的邮局服务,支持Docker自托管

服务器要求(官方):

  • 支持KVM、ESX、Hyper-V等全虚拟化平台,不支持OpenVZ、Virtuozzo和LXC

  • 开放25号端口

  • 处理器:1Ghz

  • 内存:最小6G+1G Swap(部署完后实际大概占用4G左右内存,越大越好)

  • 磁盘:20G(越大越好)

  • 架构:x86_64, ARM64(新发布,可能存在问题)

Mailcow主要是比较吃内存,最好部署在内存较大的服务器上,如果你想减少对内存的占用可以尝试通过在mailcow.conf中设置SKIP_CLAMD=ySKIP_SOLR=y来禁用这两个比较吃内存的服务

Mailcow使用的TCP端口:25|80|110|143|443|465|587|993|995|4190

在搭建前尤其要测试自己服务器的25号端口的流量是否能够出站,因为目前许多IDC默认是屏蔽了25端口的出站流量,如果被屏蔽了可以发工单询问是否可以解除限制(PS:有些IDC就是不允许,发工单也没用)

可以使用telnet smtp.outlook.com 25测试25端口流量是否能顺利出站,一般返回220就表示没有问题:

$ telnet smtp.outlook.com 25
Trying 52.98.252.98...
Connected to smtp.outlook.com.
Escape character is '^]'.
220 FR4P281CA0131.outlook.office365.com Microsoft ESMTP MAIL Service ready at Sun, 21 Apr 2024 08:42:46 +0000

Mailcow搭建


NTP时间同步


执行timedatectl status查看服务器是否启动了NTP服务,显示如下的话就证明已启动:

System clock synchronized: yes
              NTP service: active

如果没有启动NTP服务,则需要执行timedatectl set-ntp true来启动,若提示Failed to set ntp: NTP not supported的话则需要先安装chrony这个NTP服务

$ apt install chrony
$ systemctl status chrony # 检查一下服务是否正常启动
$ systemctl enable chrony # 设置自启动
$ timedatectl set-ntp true # 启用NTP

安装后再执行timedatectl status检查一下,一般就没问题了

rDNS


reverse DNS(rDNS),指的是反向DNS,就是让你的ip解析到邮件服务器的域名(例如mail.example.com),设置这个能提高你的可信度,让你发的邮件有更大的概率进入收件箱而不是垃圾桶

rDNS是由你的VPS提供商提供的,一般在控制面板里设置,以Netcup为例:

image.png

设置完后可以通过nslookup server_ip来查看是否生效(可能需要点时间)

DNS记录


按下面所示设置好DNS记录:

名称 类型
@ A server_ip
mail A server_ip
autodiscover CNAME mail.example.com
autoconfig CNAME mail.example.com
@ MX 10 mail.example.com
@ TXT “v=spf1 mx a -all”
dkim._domainkey TXT “v=DKIM1; k=rsa; t=s; s=email; p=…”
_dmarc TXT “v=DMARC1; p=none; rua=mailto:[email protected]
  • 注意我们邮件服务器的域名是mail.example.com,但是也要将example.com@)解析到邮件服务器的ip

  • dkim._domainkey值中的p需要我们部署完后再填写

  • _dmarc

    • p 参数表示对于失败的DMARC检查应该采取什么措施,可以设置为以下三个值之一:

      1. none: 仅发送DMARC报告,不执行任何其他操作(宽松)

      2. quarantine: 将失败的邮件发送到垃圾邮件文件夹

      3. reject: 直接拒绝并丢弃失败的邮件(严格)

    • mailto:[email protected]可以填写一个我们自己的常用邮箱即可

docker-compose部署服务端


需要确保服务器已经安装了dockerdocker-compose

执行umask命令确保输出0022(默认一般都是这个值)

创建存放容器的目录,然后拉取官方项目目录并进入

$ mkdir /docker
$ cd /docker
$ git clone https://github.com/mailcow/mailcow-dockerized
$ cd mailcow-dockerized

执行./generate_config.sh脚本初始化Mailcow并生成配置文件,执行后会要求我们填写邮件服务器域名,也就是之前A记录设置的mail.example.com,时区的话就选择Asia/Shanghai,分支的话选择主分支master branch

执行脚本后生成的mailcow.conf就是配置文件,我们可以打开按照自己需要修改一些配置,如果我们使用反向代理的话可以修改:

HTTP_PORT=8080
HTTP_BIND=127.0.0.1
HTTPS_PORT=8443
HTTPS_BIND= 127.0.0.1

然后依次执行docker-compose pulldocker-compose up -d就搭建好了

Nginx反代


先为我们的服务器申请一个SSL证书:

$ certbot certonly --standalone --agree-tos -d mail.example.com

确保系统已经安装了Nginx,然后进入配置文件目录/etc/nginx/conf.d新建一个配置为mailcow.conf,写入下面内容,注意把域名改为自己的邮件服务器域名:

server {
  listen 80;
  listen [::]:80;
  server_name mail.example.com autodiscover.* autoconfig.*;
  return 301 https://$host$request_uri;
}
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name mail.example.com autodiscover.* autoconfig.*;

  ssl_certificate /etc/letsencrypt/live/mail.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/mail.example.com/privkey.pem;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_session_tickets off;

  # See https://ssl-config.mozilla.org/#server=nginx for the latest ssl settings recommendations
  # An example config is given below
  ssl_protocols TLSv1.2;
  ssl_ciphers HIGH:!aNULL:!MD5:!SHA1:!kRSA;
  ssl_prefer_server_ciphers off;

  location /Microsoft-Server-ActiveSync {
    proxy_pass http://127.0.0.1:8080/Microsoft-Server-ActiveSync;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_connect_timeout 75;
    proxy_send_timeout 3650;
    proxy_read_timeout 3650;
    proxy_buffers 64 512k; # Needed since the 2022-04 Update for SOGo
    client_body_buffer_size 512k;
    client_max_body_size 0;
  }

  location / {
    proxy_pass http://127.0.0.1:8080/;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    client_max_body_size 0;
  # The following Proxy Buffers has to be set if you want to use SOGo after the 2022-04 (April 2022) Update
  # Otherwise a Login will fail like this: https://github.com/mailcow/mailcow-dockerized/issues/4537
    proxy_buffer_size 128k;
    proxy_buffers 64 512k;
    proxy_busy_buffers_size 512k;
  }
}

然后systemctl start nginx就完成了

登录并配置服务端管理面板


登录https://mail.example.com进入管理页面,默认用户名为admin,密码为moohoo

登陆后最好赶紧修改密码以及添加两步验证,点击右上角System-Configuration,我这里已经修改好了

image.png

点击右上角E-Mail-Configuration,找到Add Domain添加域,Domain设置为example.com(注意不是mail.example.com),然后点击最下面Add domain and restart SOGo

image.png

点击右上角System-Configuration,找到Options-ARC/DKIM keys点击进入,复制右面的Private Key,填入我们前面配置DNS记录dkim._domainkey的值

image.png

至此,Mailcow服务端基本配置就完成了

最后,点击右上角E-Mail-Configuration,点击页面的Mailboxes找到Add mailbox就可以添加账户了,登陆的话可以直接访问https://mail.example.com找到Webmail点进去登录即可

image.png

去Mail-tester测试一下


可以去Mail-tester这个网站测试一下,我的显示7.9/10分,主要扣分是因为域名注册时间太短,不是大问题

image.png

客户端配置


如果想在其他客户端登录邮箱可以按照下面协议和对应端口配置即可:

协议 加密 主机名 端口
IMAP STARTTLS mail.example.com 143
IMAPS SSL mail.example.com 993
POP3 STARTTLS mail.example.com 110
POP3S SSL mail.example.com 995
SMTP STARTTLS mail.example.com 587
SMTPS SSL mail.example.com 465

解决无法收信的问题


经测试发现发邮件没太大问题,但是经常收不到邮件,最后发现是被RSPAMD这个服务认为是垃圾邮件给拒收了

解决方法是首先登录管理面板,点击右上角System-Configuration,找到Access-Rspamd UI点击进入

image.png

进入Rspamd UI面板后,点击History就可以看到所有的收发邮件记录

我们需要点击Configuration,把greylistaddheaderreject这三个值设置大一点,保存再测试一下基本就没问题了

image.png

数据备份


Mailcow提供了自己的备份脚本(/docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh),使用方法如下:

# Syntax:
/docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup (vmail|crypt|redis|rspamd|postfix|mysql|all|--delete-days)

# Backup all, delete backups older than 3 days
/docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all --delete-days 3

# Backup vmail, crypt and mysql data, delete backups older than 30 days
/docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup vmail crypt mysql --delete-days 30

# Backup vmail
/docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup vmail

# MULTITHREADING
THREADS=14 /docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all

# BACKUP PATH
MAILCOW_BACKUP_LOCATION=/tmp /docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all

# MULTITHREADING + BACKUP PATH
MAILCOW_BACKUP_LOCATION=/opt/backup THREADS=14 /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all

这里我是设置定时任务执行脚本并使用rclone上传到我的网盘,rclone的配置还是比较简单的就不在这里讲了,以下是我的配置mail_backup.sh,该备份将上传到我的两个网盘中,并最多只保留两个最近的备份文件

#!/bin/bash

delete_mail_files() {
    echo "开始删除 /tmp 下所有以 mailcow 开头的文件和目录..."
    for item in /tmp/mailcow*; do
        if [ -f "$item" ]; then
            echo "正在删除文件: $item"
            rm -f "$item"
        elif [ -d "$item" ]; then
            echo "正在删除目录: $item"
            rm -rf "$item"
        fi
    done
    echo "删除操作完成。"
}

SOURCE_DIR="/tmp"
DEST_DIR1="DEST_DIR1:/LocalStorage/Backups/MailData/"
DEST_DIR2="DEST_DIR2:/LocalStorage/Backups/MailData/"

CURRENT_DATE=$(date +"%Y-%m-%d")

delete_mail_files

THREADS=3 MAILCOW_BACKUP_LOCATION=/tmp /docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all

TARGET=$(find /tmp -maxdepth 1 -type d -name "mailcow*" -print -quit)

tar -zcf "/tmp/mailcow_backup_$CURRENT_DATE.tar.gz" "$TARGET"

rclone copy "/tmp/mailcow_backup_$CURRENT_DATE.tar.gz" "$DEST_DIR1"
rclone copy "/tmp/mailcow_backup_$CURRENT_DATE.tar.gz" "$DEST_DIR2"

# 删除临时文件
delete_mail_files

# 只保留最近的两个备份
rclone lsf --format tp --separator " " "$DEST_DIR1" | sort -rk1 | awk 'NR>2 {print $NF}' | while read -r file; do
    rclone deletefile "$DEST_DIR1/${file}"
done

rclone lsf --format tp --separator " " "$DEST_DIR2" | sort -rk1 | awk 'NR>2 {print $NF}' | while read -r file; do
    rclone deletefile "$DEST_DIR2/${file}"
done

然后执行crontab -e,设置每天0点备份一次

0 0 * * * /root/scripts/mail_backup.sh >> /root/scripts/mail_backup.log 2>&1

写在最后


Mailcow总的来说体验还是不错的(只要你的服务器性能够顶!),我测试了一下Gmail和QQ都能顺利收到邮件,只有Outlook会进垃圾桶,不过听说Outlook是白名单模式,进垃圾桶是没办法的事

发表了43篇文章 · 总计78.96k字
·
Built with Hugo
主题 StackJimmy 设计