Postfix邮件服务器

TOC

邮件服务器

邮件服务器核心组件与协议

核心组件:

  • MUA (Mail User Agent)​​:用户直接交互的客户端软件,也就是负责撰写、发送、接收和管理邮件(比如:QQ邮箱、网易邮箱、outlook等)。
  • MTA (Mail Transfer Agent)​​:负责邮件路由和转发(服务器间通信)。​Postfix就是典型的MTA。
  • MDA (Mail Delivery Agent)​​:负责将MTA收到的邮件投递到用户邮箱(存储)。常与MTA集成或由MRA兼任。
  • MRA (Mail Retrieval Agent)​​:负责让用户读取已存储的邮件。​Dovecot是典型的MRA,提供POP3/IMAP服务。

关键协议:

  • SMTP(Simple Mail Transfer Protocol):用于发送和接收邮件,默认端口25(SSL加密端口465/587)。
  • POP3(Post Office Protocol 3):下载邮件到本地,默认端口110(加密端口995)。
  • IMAP(Internet Message Access Protocol):远程管理邮件并同步多设备,默认端口143(加密端口993)。
  • MIME(Multipurpose Internet Mail Extensions):多用途互联网邮件扩展,也就是扩展了SMTP协议,允许电子邮件传输多种内容(比如:文本、图片、音频、视频等)。

邮件发送接收流程

邮件服务发送和接收的架构图:
Mail-Server
邮件服务发送与接收全过程解析:
1.用户撰写邮件(MUA层)
用户在客户端(Outlook、Thunderbird、手机邮箱)填写:发件人 From: xxx@aaa.com、收件人 To: xxx@bbb.com、主题 Subject、正文和附件。

2.客户端提交发送到SMTP服务器(第一步)
客户端提交给发送邮件服务器,服务器允许认证用户提交邮件,防止开网关中继(open relay)。
MTA的工作:

    • 在队列中写入邮件(队列文件)
  • 添加 Received: 头(记录传输路径、时间、IP)
  • 做本地策略检查(是否允许中继、配额检查)
  • 反垃圾/病毒扫描(SpamAssassin、ClamAV)
  • DKIM 签名(如果配置),即在发出的邮件加入 DKIM-Signature: 头
  • 如果目标在同域可能直接本地投递,否则为远程投递做准备

注意:端口25是MTA之间的传输端口(通常不用于客户端提交);很多ISP会阻止入站或出站25端口以防滥用。

3.发件MTA决定发送去向是本地投递还是远程投递(第二步、第三步)
如果收件域是bbb.com(非 aaa.com),MTA要发到外网。
查询bbb.com的MX记录(返回多个MX,带优先级numeric)。选择优先级最低(数字最小)的MX。若没有MX,则回退到A/AAAA 记录(RFC 5321 规定)。解析到目标MX的IP(A/AAAA)并选择一个作为目标SMTP服务器。

4.发件MTA与收件域的MX建立SMTP连接并传输邮件(第四步)
发件MTA打开TCP连接到邮件目的地址的SMTP服务器,经过重要的安全/验证检查(通常由收件MTA在这里做)。
如果收件MAT接收了邮件(返回250),将把邮件加入到本地投递队列,经过本地策略、反垃圾过滤、病毒扫描,最终本地投递给用户的邮箱存储(使用LDA/LMTP,如 Dovecot LMTP、procmail、maildrop等)

5.邮件存储(第五步)
邮件服务器将以常见的邮箱格式对收到的邮件进行存储,比如:Maildir(目录结构:tmp/ → new/ → cur/,安全避免并发文件写入),典型路径 /home/bob/Maildir/new/xxxx或者- - mbox(单文件追加,较旧)

6.收件人客户端取信(第六步)
收件人客户端连接到收件服务器,使用IAMP协议(默认端口143,加密端口993)或者POP3协议(默认端口110,加密端口995)。
客户端进行同步或者下载邮件,若是IMAP,会保留服务器上的邮件(并标记已读/删除等);若是POP3 常常默认下载后删除。

7.失败、重试和退信(Bounce / NDR)

  • 如果某一步遇到临时错误(4xx),发件 MTA 会按重试策略轮询 MX 并重试(指数回退,一段时间后仍失败则生成退信或保留在队列)。
  • 永久错误(5xx)通常会立即返回退信(NDR)给 信封地址(MAIL FROM),而非 From: 头中的地址。
  • 若发件人使用空信封 MAIL FROM:<>(系统退信),则收件端不会对其返回退信,以避免循环。

核心概念与角色类比​

邮件系统是一个典型的​C/S(客户端/服务器)架构,涉及多个服务器和协议协同工作。一个经典的类比是传统邮政系统​:

邮政系统 邮件系统 说明
写信人 邮件客户端 (MUA)​ 如 Outlook, Foxmail, Thunderbird, 手机邮件App
本地邮局 发件服务器 (MTA)​ 如 ​Postfix, Exim。负责接收用户邮件并转发出去
运输网络 互联网 (SMTP)​ 邮件服务器之间通过SMTP协议通信
目的地邮局 收件服务器 (MTA)​ 如 ​Postfix。接收来自其他服务器的邮件
邮局邮箱 邮件存储 (MDA)​ 服务器上的存储空间,如 Maildir/目录
邮递员 收件服务器 (MRA)​ 如 ​Dovecot。负责将邮箱里的邮件交给最终用户
收信人 邮件客户端 (MUA)​ 用户最终在自己的设备上阅读邮件

Postfix服务简介

什么是Postfix服务?

Postfix(MTA, Mail Transfer Agent)是一个开源的邮件传输代理,它的主要功能是接收、路由和发送电子邮件。在整个邮件系统里,Postfix 是负责“传递邮件”的核心组件,属于邮件服务器的“大脑和中枢”。

Postfix的扮演角色

  • 发送邮件:当本地主机或客户端提交邮件给 Postfix,它会决定该邮件是本地投递(交给本地用户邮箱)还是远程投递(通过 SMTP 发往其他邮件服务器)。
  • 接收邮件:它监听 SMTP 端口(25、587、465),接受来自外部邮件服务器或者内部客户端的邮件。
  • 队列管理:如果对方服务器暂时不可达,Postfix 会把邮件放到队列中,按照一定重试策略再次发送。
  • 邮件过滤和安全:可以结合 SpamAssassin、Amavis、ClamAV 做反垃圾邮件、反病毒扫描。支持 TLS 加密、SASL 用户认证,保障传输安全。
  • 日志与监控:提供详细的日志,方便运维人员排查邮件收发问题。

补充说明:Postfix服务在架构中扮演的角色主要就是SMTP服务器的应用。SMTP协议=一种传输邮件的“规则”。Postfix=一个完整的软件,实现了SMTP协议,并在此基础上扩展了队列管理、本地投递、策略控制等功能。

个人邮件服务搭建示例

搭建思路构思

主要架构采用Postfix+Dovecot来实现邮件服务系统的搭建,由于不是在公网上,在内部虚拟环境下,所以还需要自己搭建一个DNS服务来完成解析。

搭建环境实验步骤:

  • 1.准备服务器,系统为Rocky Linux,IP地址为192.168.101.201,准备在上面搭建邮件服务和DNS服务。
  • 2.设置域名解析,解析域名为example.com,邮件服务为mail.example.com,IP地址为192.168.101.201
  • 3.安装Postfix服务,然后配置相关配置文件(由于测试,所以不开启ssl认证)。
  • 4.安装Dovecot服务,然后配置相关配置文件(由于测试,所以不开启ssl认证)。
  • 创建测试用户,并使用Telnet工具进行邮件发送和接收测试。

完整邮件服务器DNS检查清单

记录类型 主机名 设置位置 验证命令
A mail 192.168.101.201 您的DNS服务商 dig mail.example.com
MX @ mail.example.com. 您的DNS服务商 dig MX example.com
TXT (SPF)​ @ v=spf1 mx ~all 您的DNS服务商 dig TXT example.com
TXT (DKIM)​ default._domainkey v=DKIM1; p=... 您的DNS服务商 dig TXT default._domainkey.example.com
TXT (DMARC)​ _dmarc v=DMARC1; p=none... 您的DNS服务商 dig TXT _dmarc.example.com
PTR 192.168.101.201 mail.example.com IP提供商控制台 dig -x 192.168.101.201

DNS服务搭建

首先我们需要先安装DNS软件包

yum -y install bind bind-utils

配置DNS配置文件加入正向解析区域和反向解析区域相关配置,如下所示:

// 正向解析配置
zone "example.com" IN {
    type master;
    file "example.com.zone";
    allow-update { none; };
};
// 反向解析配置
zone "101.168.192.in-addr.arpa" IN {
    type master;
    file "192.168.101.zone";
    allow-update { none; };
};

正向解析文件:example.com.zone,默认配置中设置路径在/var/name目录下,如下所示:

$TTL    86400                     ; 默认TTL(1天)
@       IN      SOA     dns.example.com. admin.example.com. (
                2024090101      ; 序列号 (格式:YYYYMMDDNN)
                3600            ; 刷新时间 (1小时)
                1800            ; 重试时间 (30分钟)
                604800          ; 过期时间 (1周)
                86400           ; 最小TTL (1天)
                )

; ------------ 基础记录 ------------
@       IN      NS      dns.example.com.       ; 域名服务器记录
dns     IN      A       192.168.101.201        ; DNS服务器IP
; ------------ 邮件服务器必需记录 ------------
@       IN      MX      10 mail.example.com.   ; MX记录(优先级10)
mail    IN      A       192.168.101.201        ; 邮件服务器IP
; ------------ 反垃圾邮件记录(SPF/DKIM/DMARC)------------
@       IN      TXT     "v=spf1 mx ~all"    ; SPF记录
mail._domainkey IN TXT  "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC..."  ; DKIM公钥
_dmarc  IN      TXT     "v=DMARC1; p=none; rua=mailto:postmaster@example.com"             ; DMARC策略
; ------------ 其他常用记录 ------------
@       IN      A       192.168.101.201     ; 根域名A记录(网站)
; ------------ CNAME类型记录 ------------
www     IN      CNAME   example.com.        ; WWW别名

反向解析文件:101.168.192.in-addr.arpa,默认配置中设置路径在/var/name目录下,如下所示:

$TTL 86400
@   IN  SOA dns.example.com. admin.example.com. (
        2025082701 ; 序列号
        3600       ; 刷新
        1800       ; 重试
        604800     ; 过期
        86400 )    ; 最小TTL

; ---------- 名称服务器 ----------
      IN  NS    dns.example.com.
; ----------- PTR 记录 -----------
201   IN  PTR   email.example.com.

安装配置Postfix

安装postfix软件包

yum -y install postfix

配置postfix配置文件:/etc/postfix/main.cf
如果有以#符号为头的需要在配置文件中注释掉。

# 指定邮件服务器的主机名(FQDN,全限定域名)
myhostname = mail.example.com
# 设置本地域名(通常是主机名去掉前缀部分)
mydomain = example.com
# 指定本地用户发信时,邮件地址的“域”部分
myorigin = $myhostname
# 控制Postfix在哪些网络接口上监听SMTP
inet_interfaces = all
#inet_interfaces = localhost
#mydestination = $myhostname, localhost.$mydomain, localhost
# 定义哪些域名算作“本地域”
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
# 定义哪些IP地址/网段被认为是“可信网络”
mynetworks = 192.168.101.0/24, 127.0.0.0/8
#home_mailbox = Mailbox
# 定义用户邮件的存储格式,每封邮件一个文件,存放在~/Maildir/目录下,常配合Dovecot使用
home_mailbox = Maildir/

# SASL(身份认证)配置
smtpd_tls_security_level = none
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
# 收件限制策略
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
# 控制Postfix发送端(smtp)的TLS策略
smtp_tls_security_level = none

配置:启用Submission端口(明文587)​​:/etc/postfix/master.cf

submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=none  # 禁用加密
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

启动服务

systemctl enable postfix --now

安装配置Dovecot

安装dovecot软件服务。

yum -y install dovecot dovecot-devel clucene-core

配置dovecot配置文件:/etc/dovecot/dovecot.conf

!include conf.d/*.conf
protocols = imap pop3 lmtp

配置认证:/etc/dovecot/conf.d/10-auth.conf

auth_mechanisms = plain login
!include auth-system.conf.ext  # 使用系统用户认证
disable_plaintext_auth = no

配置邮件存储:/etc/dovecot/conf.d/10-mail.conf

mail_location = maildir:~/Maildir

禁用SSL/TLS:/etc/dovecot/conf.d/10-ssl.conf

ssl = no

配置Postfix认证集成:/etc/dovecot/conf.d/10-master.conf​

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

修改完需要的配置之后启动服务,如下所示:

systemctl enable dovecot --now

测试阶段

新增用户

创建两个用户作为测试用户

useradd -m user1 -s /sbin/nologin
useradd -m user2 -s /sbin/nologin

登录邮件服务器

登录具体操作

# 步骤一:连接STMP服务器
telnet mail.example.com 25
# 步骤二:标注信息
EHLO test
# 步骤三:登录账号
AUTH LOGIN
# 步骤四:输入用户账号和密码
用户账号的base64字符串
用户密码的base64字符串

具体登录邮件服务器的操作过程,如下所示:

$ telnet mail.example.com 25
Trying 192.168.101.201...
Connected to mail.example.com.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
EHLO test
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
AUTH LOGIN
334 VXNlcm5hbWU6
dXNlcjE=
334 UGFzc3dvcmQ6
MTIzNDU2
235 2.7.0 Authentication successful

发送邮件

发送邮件具体操作

# 步骤一:标注信息
EHLO test
# 步骤二:指定发件人地址
MAIL FROM: <user1@example.com>
# 步骤三:指定收件人地址
RCPT TO: <user2@example.com>
# 步骤四:撰写邮件内容
DATA
# 步骤五:撰写标题
Subject: Test Email
# 步骤六:撰写邮件内容
This is a test email.
# 步骤七:使用.标明撰写结束
.
# 步骤八:退出
QUIT

具体发送邮件的操作过程,如下所示:

EHLO test send email
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
MAIL FROM: <user1@example.com>
250 2.1.0 Ok
RCPT TO: <user2@example.com>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: Test Email
This is a test email.
.
250 2.0.0 Ok: queued as 2E9282098164
QUIT
221 2.0.0 Bye
Connection closed by foreign host.

查收邮件

查收邮件具体步骤(下面的a1、a2...是客户端生成的任意标签)

# 步骤一:连接到IMAP服务器​
$ telnet 服务地址 服务端口
# 步骤二:登录账号
a1 LOGIN 用户名 用户密码
# 步骤三:列出邮箱文件夹
a2 LIST "" "*"
# 步骤四:选择收件箱
a3 SELECT INBOX
# 步骤五:查看邮件列表
a4 FETCH 1:* (FLAGS BODY.PEEK[HEADER])
# 步骤六:读取具体邮件内容
a5 FETCH 1 BODY[]
# 步骤七:退出登录
a6 LOGOUT

具体查收邮件的操作过程,如下所示:

$ telnet 192.168.101.201 143
Trying 192.168.101.201...
Connected to email.example.com.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
a1 LOGIN user2 1234567
a1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE] Logged in
a2 LIST "" "*"
* LIST (\HasNoChildren) "." INBOX
a2 OK List completed (0.006 + 0.000 + 0.005 secs).
a3 SELECT INBOX
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft \*)] Flags permitted.
* 1 EXISTS
* 1 RECENT
* OK [UNSEEN 1] First unseen.
* OK [UIDVALIDITY 1757870899] UIDs valid
* OK [UIDNEXT 2] Predicted next UID
a3 OK [READ-WRITE] Select completed (0.004 + 0.000 + 0.003 secs).
a4 FETCH 1:* (FLAGS BODY.PEEK[HEADER])
* 1 FETCH (FLAGS (\Recent) BODY[HEADER] {439}
Return-Path: <user1@example.com>
X-Original-To: user2@example.com
Delivered-To: user2@example.com
Received: from test?send?email (unknown [192.168.101.201])
	by mail.example.com (Postfix) with ESMTPA id 2E9282098164
	for <user2@example.com>; Mon, 15 Sep 2025 01:17:01 +0800 (CST)
Subject: Test Email
Message-Id: <20250914171718.2E9282098164@mail.example.com>
Date: Mon, 15 Sep 2025 01:17:01 +0800 (CST)
From: user1@example.com

)
a4 OK Fetch completed (0.002 + 0.000 + 0.001 secs).
a5 FETCH 1 BODY[]
* 1 FETCH (FLAGS (\Seen \Recent) BODY[] {462}
Return-Path: <user1@example.com>
X-Original-To: user2@example.com
Delivered-To: user2@example.com
Received: from test?send?email (unknown [192.168.101.201])
	by mail.example.com (Postfix) with ESMTPA id 2E9282098164
	for <user2@example.com>; Mon, 15 Sep 2025 01:17:01 +0800 (CST)
Subject: Test Email
Message-Id: <20250914171718.2E9282098164@mail.example.com>
Date: Mon, 15 Sep 2025 01:17:01 +0800 (CST)
From: user1@example.com

This is a test email.
)
a5 OK Fetch completed (0.002 + 0.000 + 0.001 secs).
a6 LOGOUT
* BYE Logging out
a6 OK Logout completed (0.001 + 0.000 secs).
Connection closed by foreign host.