Featured image of post 搭建一个自己的邮件服务器

搭建一个自己的邮件服务器

拥有一个自己的邮件服务器可以干什么?不用怕自己的个人邮箱被暴露,自己搭建的服务可以使用发送邮件通知等等

在很久以前就想自己搭建一个邮件服务器,正好现在一些服务需要邮件通知,所以这个需求迫在眉睫,于是我决定就开始搭建了。花了很多时间在GitHub上面找相关的项目,最终采用 maddy 搭建邮件系统,listmonk 搭建邮件订阅发送平台,Mailspring 作为邮件客户端,这已经可以满足绝大部分的需求了

🛠 准备工作

  • 可信度高的顶级域名,通常是价格稍微高一点,或者不是免费能得到的域名,例如 comcn
  • 域名提供商能解析 mxtxt 等记录
  • 一台未被封禁 25 端口的服务器

📌 前提说明

为了文章简洁,以下参数值在本教程中用作示例,无论在哪里看到,都需要将它们替换成你的实际值:

  • 域名:example.com
  • MX 域:mx1.example.com
  • IPv4 地址:1.2.3.4
  • 用户名:stellarisw
  • 用户密码:stellariswpass

✈ 部署

maddy

一个GO语言开发的ALL-IN-ONE邮件系统,主要功能是通过SMTP发送和接收邮件,通过IMAP,客户端可以实现访问,也支持DKIM、SPF、DMARC、DANE、MTA-STS等邮件相关的安全和反垃圾协议。对比配置传统软件,即使是像MailCow、Mail-in-a-Box等基于docker的现成方案,安装、配置也足够简单、开箱即用,可以说是懒人必备。

1. 获取域名证书

设置你的DNS提供商的API Key 环境变量, 具体参考: How to use DNS API

执行 cd 命令到你想要安装 maddy 的目录,然后执行以下命令:

1
curl https://get.acme.sh | sh
1
alias acme.sh ~/.acme.sh/acme.sh
1
acme.sh --set-default-ca --server letsencrypt
1
acme.sh --issue --dns dns_dp -d mx1.example.com
1
mkdir data/tls
1
2
3
4
acme.sh --force --install-cert -d example.com \
--cert-file $(pwd)/data/tls/cert.pem \
--key-file $(pwd)/data/tls/key.pem \
--fullchain-file $(pwd)/data/tls/fullchain.pem

2. 修改 maddy 配置文件

1
vim ./data/maddy.conf
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
## Maddy Mail Server - default configuration file (2022-06-18)
## This is the copy of maddy.conf with changes necessary to run it in Docker.
# Suitable for small-scale deployments. Uses its own format for local users DB,
# should be managed via maddyctl utility.
#
# See tutorials at https://maddy.email for guidance on typical
# configuration changes.

# ----------------------------------------------------------------------------
# Base variables

$(hostname) = example.com
$(primary_domain) = example.com
$(local_domains) = $(primary_domain) # 如果你还想创建多个服务器,可以设置其他邮件服务器的域名

tls file /data/tls/fullchain.pem /data/tls/key.pem

# ----------------------------------------------------------------------------
# Local storage & authentication

# pass_table provides local hashed passwords storage for authentication of
# users. It can be configured to use any "table" module, in default
# configuration a table in SQLite DB is used.
# Table can be replaced to use e.g. a file for passwords. Or pass_table module
# can be replaced altogether to use some external source of credentials (e.g.
# PAM, /etc/shadow file).
#
# If table module supports it (sql_table does) - credentials can be managed
# using 'maddyctl creds' command.

auth.pass_table local_authdb {
    table sql_table {
        driver sqlite3
        dsn credentials.db
        table_name passwords
    }
}

# imapsql module stores all indexes and metadata necessary for IMAP using a
# relational database. It is used by IMAP endpoint for mailbox access and
# also by SMTP & Submission endpoints for delivery of local messages.
#
# IMAP accounts, mailboxes and all message metadata can be inspected using
# imap-* subcommands of maddyctl utility.

storage.imapsql local_mailboxes {
    driver sqlite3
    dsn imapsql.db
}

# ----------------------------------------------------------------------------
# SMTP endpoints + message routing

hostname $(hostname)

table.chain local_rewrites {
    optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
    optional_step static {
        entry postmaster postmaster@$(primary_domain)
    }
    optional_step file /etc/maddy/aliases
}

msgpipeline local_routing {
    # Insert handling for special-purpose local domains here.
    # e.g.
    # destination lists.example.org {
    #     deliver_to lmtp tcp://127.0.0.1:8024
    # }

    destination postmaster $(local_domains) {
        modify {
            replace_rcpt &local_rewrites
        }

        deliver_to &local_mailboxes
    }

    default_destination {
        reject 550 5.1.1 "User doesn't exist"
    }
}

smtp tcp://0.0.0.0:25 {
    limits {
        # Up to 20 msgs/sec across max. 10 SMTP connections.
        all rate 20 1s
        all concurrency 10
    }

    dmarc yes
    check {
        require_mx_record
        dkim
        spf
    }

    source $(local_domains) {
        reject 501 5.1.8 "Use Submission for outgoing SMTP"
    }
    default_source {
        destination postmaster $(local_domains) {
            deliver_to &local_routing
        }
        default_destination {
            reject 550 5.1.1 "User doesn't exist"
        }
    }
}

submission tls://0.0.0.0:465 tcp://0.0.0.0:587 {
    limits {
        # Up to 50 msgs/sec across any amount of SMTP connections.
        all rate 50 1s
    }

    auth &local_authdb

    source $(local_domains) {
        check {
            authorize_sender {
                prepare_email &local_rewrites
                user_to_email identity
            }
        }

        destination postmaster $(local_domains) {
            deliver_to &local_routing
        }
        default_destination {
            modify {
                dkim $(primary_domain) $(local_domains) default
            }
            deliver_to &remote_queue
        }
    }
    default_source {
        reject 501 5.1.8 "Non-local sender domain"
    }
}

target.remote outbound_delivery {
    limits {
        # Up to 20 msgs/sec across max. 10 SMTP connections
        # for each recipient domain.
        destination rate 20 1s
        destination concurrency 10
    }
    mx_auth {
        dane
        mtasts {
            cache fs
            fs_dir mtasts_cache/
        }
        local_policy {
            min_tls_level encrypted
            min_mx_level none
        }
    }
}

target.queue remote_queue {
    target &outbound_delivery

    autogenerated_msg_domain $(primary_domain)
    bounce {
        destination postmaster $(local_domains) {
            deliver_to &local_routing
        }
        default_destination {
            reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
        }
    }
}

# ----------------------------------------------------------------------------
# IMAP endpoints

imap tls://0.0.0.0:993 tcp://0.0.0.0:143 {
    auth &local_authdb
    storage &local_mailboxes
}

3. 创建邮箱账户

1
2
3
4
5
docker run --rm -it \
-v $(pwd)/data:/data \
--entrypoint /bin/maddy \
foxcpp/maddy:latest \
creds create stellarisw@example.com
1
2
3
4
5
docker run --rm -it \
-v $(pwd)/data:/data \
--entrypoint /bin/maddy \
foxcpp/maddy:latest \
imap-acct create stellarisw@example.com

4. 运行镜像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
docker run \
--name maddy \
-d \
-v $(pwd)/data:/data \
-p 25:25 \
-p 143:143 \
-p 465:465 \
-p 587:587 \
-p 993:993 \
-e MADDY_HOSTNAME=example.com \
-e MADDY_DOMAIN=example.com \
foxcpp/maddy:latest

5. 开放端口

25:smtp inbound 端口

465:smtp ssl 加密端口

587:smtp-msa 端口

143:imap 非加密端口

993:imap ssl 加密端口

详细参考:常用邮件端口说明

6. 设置 DNS 解析

  1. 主域名解析

    example.com. A 1.2.3.4

  2. MX 记录

    example.com. MX 10 mx1.example.com.

  3. MX 域 解析

    mx1.example.com A 1.2.3.4

  4. SPF 记录

    example.com TXT v=spf1 mx ~all

    mx1.example.com TXT v=spf1 mx ~all

  5. DMARC 记录

    _dmarc TXT v=DMARC1; p=quarantine; ruf=mailto:report@example.com

  6. DKIM 签名

    1
    
    cat ./data/dkim_keys/example.com_default.dns
    

    default._domainkey TXT …. (这里填刚刚命令所输出的内容)

想设置更高级的配置,请看:maddy-setting-up

关于记录类型的说明,请看:Maddy打造个人邮箱服务

listmonk

一个独立的,自托管的通讯和邮件列表管理器,有美观的仪表板,速度快,功能丰富,简单来说就是能给你的订阅者们群发邮件更方便更有效率。

1. 修改 listmonk 配置文件

执行 cd 命令到你想要安装 listmonk 的目录,然后执行以下命令:

1
mkdir config
1
vim ./config/config.toml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[app]
address = "0.0.0.0:9000"
admin_username = "stellarisw"
admin_password = "password" # 你的仪表盘界面的登入密码

# Database.
[db]
host = "listmonk_db"
port = 5432
user = "listmonk"
password = "password" # 数据库密码
database = "listmonk"
ssl_mode = "disable"
max_open = 25
max_idle = 25
max_lifetime = "300s"

上述密码最好使用 openssl rand -base64 16 随机生成一个字符串然后存入,记住保存,推荐保存在 vaultwarden 里面,详细请看我文章 Vaultwarden - 一个强大的密码管理服务器

2. 创建 docker-compose.yml

1
vim ./docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# NOTE: This docker-compose.yml is meant to be just an example guideline
# on how you can achieve the same. It is not intented to run out of the box
# and you must edit the below configurations to suit your needs.

version: "3.7"

x-app-defaults:
  restart: always
  image: listmonk/listmonk:latest
  container_name: listmonk_app
  depends_on:
    - db
  volumes:
    - ./config/config.toml:/listmonk/config.toml
  ports:
    - "9000:9000"
  networks:
    - listmonk
  environment:
    - TZ=Asia/Shanghai

x-db-defaults:
  image: postgres:13
  container_name: listmonk_db
  volumes:
    - ./data:/var/lib/postgresql/data
  ports:
    - "5432:5432"
  networks:
    - listmonk
  environment:
    - POSTGRES_PASSWORD=password # 这里填刚刚的数据库密码
    - POSTGRES_USER=listmonk
    - POSTGRES_DB=listmonk
  restart: always
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U listmonk"]
    interval: 10s
    timeout: 5s
    retries: 6

networks:
  listmonk:
1
docker-compose up -d

Mailspring

一个开源的邮件客户端,支持 Mac、Linux 和 Windows,主要是看起来美观才选这个

  1. 下载 Mailspring

    进入 官网 下载合适的版本

  2. 点击 IMAP / SMTP

    image-20220907181402174

  3. 填写邮箱账号密码

    image-20220907181511943

  4. 填写服务器信息

    image-20220907181554128

📝 参考链接

Maddy打造个人邮箱服务

搭建基于 Maddy 和 Rainloop 的邮件服务器

Maddy - 开箱即用的邮件服务端

渝ICP备2022001449号
本站总访问量