DNS&&邮件服务

6439 字
32 分钟
DNS&&邮件服务

DNS&&邮件服务#

[TOC]


DNS介绍#

  • DNS 原理
    • 域名结构:根域(.) -> 顶级域(.com, .cn) -> 次级域(baidu.com) -> 主机名(www)
    • 解析过程:客户端 -> 本地 Hosts -> 缓存名称服务器(本实验重点) -> 根服务器 -> 顶级域服务器 -> 权威服务器
  • 核心软件Unbound。这是一个验证、递归和缓存 DNS 解析器
  • 实验目标:配置一台 Linux 机器作为本地缓存名称服务器,能够为局域网内的其他机器提供域名解析服务,并能手动管理缓存

实验环境#

角色主机名IP 地址软件/职责
DNS 服务器Server10.0.0.101安装 Unbound,作为缓存服务器
客户端/测试机Client10.0.0.102修改 DNS 指向服务器,测试解析

搭建 DNS 服务器#

  1. 安装软件

    Terminal window
    dnf -y install unbound
  2. 修改主配置文件 编辑 /etc/unbound/unbound.conf

    • 关键配置:找到或添加 interfaceaccess-control,确保服务器监听在你的网卡 IP 上,并允许客户端网段访问

      interface: 10.0.0.101
      access-control: 10.0.0.0/24 allow
    • 进阶配置:如何配置“权威根域缓存”

      • 你需要找到 auth-zone 相关配置,或者在 server: 块中确保它能正常递归
  3. 启动服务

    Terminal window
    systemctl enable --now unbound
    # 检查端口是否监听 (53端口)
    ss -lntup | grep unbound

客户端测试#

  1. 修改 DNS 指向 将测试机的 DNS 指向你刚才搭建的服务器 IP

    Terminal window
    # 临时修改
    echo "nameserver 10.0.0.101" > /etc/resolv.conf
    # 或者使用 nmcli (NetworkManager)
    nmcli connection modify internal ipv4.dns 10.0.0.101
  2. 进行解析测试 使用 dig 命令测试域名解析,观察是否能获得结果

    Terminal window
    dig jd.com
    # 或者测试其他域名
    dig www.baidu.com

缓存管理#

  1. 查看当前缓存

    Terminal window
    # 导出所有缓存并查找特定域名
    unbound-control dump_cache | grep jd.com
  2. 清除缓存

    Terminal window
    # 清除特定域名
    unbound-control flush jd.com
    # 清除整个区域
    unbound-control flush_zone jd.com
  3. 导入/导出缓存

    Terminal window
    unbound-control dump_cache > dns_cache.txt # 备份
    unbound-control flush . # 清空
    unbound-control load_cache < dns_cache.txt # 恢复

搭建DNS服务器#

Terminal window
[root@Zabbix ~]# mkdir -p /ansible/roles
[root@Zabbix ~]# cd /ansible/roles
[root@Zabbix roles]# ansible-galaxy init Server
- Role Server was created successfully
[root@Zabbix roles]# cd Server/
[root@Zabbix Server]# rm -rf defaults/ meta/ tests/ README.md
================================
[root@Zabbix roles]# ansible-galaxy init Client
- Role Client was created successfully
[root@Zabbix roles]# cd Client/
[root@Zabbix Client]# rm -rf defaults/ meta/ tests/ README.md
  • vim Server/vars/main.yml
server_name: unbound
server_ip: 10.0.0.101
  • vim Server/tasks/main.yml
- name: Install "{{server_name}}" server
yum:
name: "{{server_name}}"
state: present
- name: Configure "{{server_name}}" file
blockinfile:
path: /usr/share/unbound/fedora-defaults.conf
block: |
interface: "{{server_ip}}"
access-control: 10.0.0.0/24 allow
marker: "# {mark} Ansible"
insertafter: "^.*interface-automatic"
notify: Restart "{{server_name}}"
- name: Start "{{server_name}}"
systemd:
name: "{{server_name}}"
state: started
enabled: yes
- name: Check "{{server_name}}"
shell: "ss -lntup | grep unbound"
register: check_re
- name: Print check_re
debug:
msg: "{{check_re.stdout_lines}}"
  • vim Server/handlers/main.yml
- name: Restart "{{server_name}}"
systemd:
name: "{{server_name}}"
state: restarted

  • vim Client/tasks/main.yml
- name: Ping test
command: 'ping -c1 -W2 www.baidu.com'
register: ping_re
- name: Print ping_re
debug:
msg: "{{ping_re.stdout_lines}}"
- name: Configuer dns file
lineinfile:
path: /etc/resolv.conf
regexp: '^nameserver'
line: 'nameserver 10.0.0.101'
state: present
- name: dig test
command: 'dig jd.com'
register: dig_re
- name: Print dig_re
debug:
msg: "{{dig_re.stdout_lines}}"

  • vim site.yml
- name: 配置 Server 主机
hosts: Server
roles:
- Server
- name: 配置 Client 主机
hosts: Client
roles:
- Client
- name: Server 测试验证
hosts: Server
tasks:
- name: Result test
shell: 'unbound-control dump_cache | grep jd.com'
register: result_end
when: ansible_hostname is match "Server"
- name: Print result_end
debug:
msg: "{{result_end.stdout_lines}}"
when: ansible_hostname is match "Server"

Terminal window
'一切准备就绪!!!'
# 恢复快照&&免密连接!!
[root@Zabbix ~]# ssh-copy-id -i ~/.ssh/my_key.pub 192.168.88.101
[root@Zabbix ~]# ssh-copy-id -i ~/.ssh/my_key.pub 192.168.88.102
=========================
[root@Zabbix ~]# tree /ansible/
/ansible/
└── roles
├── Client
│   ├── files
│   ├── handlers
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   └── vars
├── Server
│   ├── files
│   ├── handlers
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   └── vars
│   └── main.yml
└── site.yml
[root@Zabbix ~]# ansible all -m ping
Server | SUCCESS => {
"changed": false,
"ping": "pong"
}
Client | SUCCESS => {
"changed": false,
"ping": "pong"
}
[root@Zabbix ~]# ansible-playbook --syntax-check /ansible/roles/site.yml
playbook: /ansible/roles/site.yml
[root@Zabbix ~]# ansible-playbook /ansible/roles/site.yml
_____________________
< PLAY [配置 Server 主机] >
---------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
__________________________________________
< TASK [Server : Install "unbound" server] >
------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________________________
< TASK [Server : Configure "unbound" file] >
------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_________________________________
< TASK [Server : Start "unbound"] >
---------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_________________________________
< TASK [Server : Check "unbound"] >
---------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
________________________________
< TASK [Server : Print check_re] >
--------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Server] => {
"msg": [
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=9)) ",
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=7)) ",
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=5)) ",
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=3)) ",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=10))",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=8)) ",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=6)) ",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=2994,fd=4)) "
]
}
______________________________________________
< RUNNING HANDLER [Server : Restart "unbound"] >
----------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_____________________
< PLAY [配置 Client 主机] >
---------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
___________________________
< TASK [Client : Ping test] >
---------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Client]
_______________________________
< TASK [Client : Print ping_re] >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Client] => {
"msg": [
"PING www.a.shifen.com (180.101.51.73) 56(84) bytes of data.",
"64 bytes from 180.101.51.73: icmp_seq=1 ttl=128 time=29.9 ms",
"",
"--- www.a.shifen.com ping statistics ---",
"1 packets transmitted, 1 received, 0% packet loss, time 0ms",
"rtt min/avg/max/mdev = 29.884/29.884/29.884/0.000 ms"
]
}
____________________________________
< TASK [Client : Configuer dns file] >
------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Client]
__________________________
< TASK [Client : dig test] >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Client]
______________________________
< TASK [Client : Print dig_re] >
------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Client] => {
"msg": [
"",
"; <<>> DiG 9.18.33 <<>> jd.com",
";; global options: +cmd",
";; Got answer:",
";; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15200",
";; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1",
"",
";; OPT PSEUDOSECTION:",
"; EDNS: version: 0, flags:; udp: 1232",
";; QUESTION SECTION:",
";jd.com.\t\t\t\tIN\tA",
"",
";; ANSWER SECTION:",
"jd.com.\t\t\t60\tIN\tA\t111.13.149.108",
"jd.com.\t\t\t60\tIN\tA\t106.39.171.134",
"jd.com.\t\t\t60\tIN\tA\t211.144.27.126",
"jd.com.\t\t\t60\tIN\tA\t211.144.24.218",
"",
";; Query time: 428 msec",
";; SERVER: 10.0.0.101#53(10.0.0.101) (UDP)",
";; WHEN: Thu Apr 09 21:15:05 CST 2026",
";; MSG SIZE rcvd: 99"
]
}
____________________
< PLAY [Server 测试验证] >
--------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
____________________
< TASK [Result test] >
--------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_________________________
< TASK [Print result_end] >
-------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Server] => {
"msg": [
"jdcache.com.\t720\tIN\tSOA\tns1.jdcache.com. apollo.jd.com. 2015081101 10800 3600 604800 38400",
"ns5.jd.com.\t120\tIN\tA\t120.52.149.254",
"jd.com.\t86400\tIN\tNS\tns3.jdcache.com.",
"jd.com.\t86400\tIN\tNS\tns4.jdcache.com.",
"jd.com.\t86400\tIN\tNS\tns5.jdcache.com.",
"jd.com.\t86400\tIN\tNS\tns1.jd.com.",
"jd.com.\t86400\tIN\tNS\tns2.jd.com.",
"jd.com.\t86400\tIN\tNS\tns3.jd.com.",
"jd.com.\t86400\tIN\tNS\tns4.jd.com.",
"jd.com.\t86400\tIN\tNS\tns5.jd.com.",
"jd.com.\t86400\tIN\tNS\tns1.jdcache.com.",
"jd.com.\t86400\tIN\tNS\tns2.jdcache.com.",
"jd.com.\t60\tIN\tA\t211.144.24.218",
"jd.com.\t60\tIN\tA\t111.13.149.108",
"jd.com.\t60\tIN\tA\t106.39.171.134",
"jd.com.\t60\tIN\tA\t211.144.27.126",
"jdcache.com.\t86400\tIN\tNS\tns1.jd.com.",
"jdcache.com.\t86400\tIN\tNS\tns2.jd.com.",
"jdcache.com.\t86400\tIN\tNS\tns3.jd.com.",
"jdcache.com.\t86400\tIN\tNS\tns4.jd.com.",
"jdcache.com.\t86400\tIN\tNS\tns5.jd.com.",
"ns3.jd.com.\t86400\tIN\tA\t120.52.149.254",
"ns4.jd.com.\t86400\tIN\tA\t106.39.177.32",
"ns2.jd.com.\t86400\tIN\tA\t111.206.226.10",
"ns1.jd.com.\t86400\tIN\tA\t111.13.28.10",
"msg jd.com. IN NS 32896 1 120 3 1 0 5 6 ",
"jd.com. IN NS 0",
"ns1.jd.com. IN A 0",
"ns2.jd.com. IN A 0",
"ns3.jd.com. IN A 0",
"ns4.jd.com. IN A 0",
"ns5.jd.com. IN A 0",
"msg jd.com. IN A 32896 1 60 3 1 0 0 6 ",
"jd.com. IN A 0"
]
}
____________
< PLAY RECAP >
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Client: ok=5 changed=3 failed=0 skipped=0 ignored=0
Server: ok=8 changed=6 failed=0 skipped=0 ignored=0

  • ==ansible搭建邮箱服务==
    • 另一个实验

vars变量#

服务端#

server_1: unbound
server_2: postfix
server_3: dovecot
server_ip: 10.0.0.101
net: 10.0.0.0/24
user: jiu
uid: 1010
passwd: passwd

客户端#

server_1: thunderbird
server_ip: 10.0.0.101

templates配置#

服务端#

unbound服务#

  • 位置: /usr/share/unbound/fedora-defaults.conf
    • ==默认配置文件==
    • 被包含在 /etc/unbound/unbound.conf 主配置文件中!
server:
......
interface: "10.0.0.101"
access-control: 10.0.0.0/24 allow
.......
# 只需要添加这两行即可!

  • 位置: /etc/unbound/local.d/example-com.conf

  • ==子配置文件==—>自定义

  • 检测: unbound-checkconf

    • 关键字: no errors
  • 重载: unbound-control reload

# 定义本地域
local-zone: "example.com." static
# A 记录:将域名指向服务器IP
local-data: "dnsserver.example.com. IN A 10.0.0.101"
local-data: "mailserver.example.com. IN A 10.0.0.101"
local-data: "www.example.com. IN A 10.0.0.101"
# MX 记录:指定邮件服务器
local-data: "example.com. IN MX 10 mailserver.example.com."

Postfix服务#

  • 位置: /etc/postfix/main.cf

    • ==主配置文件==
  • 作用: 发信

# 配置文件兼容性级别(Postfix 3.8 语法)
compatibility_level = 3.8
# 邮件队列存储目录
queue_directory = /var/spool/postfix
# Postfix 可执行命令(如 postqueue, postsuper)所在目录
command_directory = /usr/sbin
# Postfix 守护进程(如 smtpd, cleanup)所在目录
daemon_directory = /usr/libexec/postfix
# Postfix 运行时数据(如缓存、会话)存储目录
data_directory = /var/lib/postfix
# 运行 Postfix 进程的系统用户
mail_owner = postfix
# 本邮件服务器的完全限定域名(FQDN)
myhostname = mailserver.example.com
# 默认邮件域名(发件人地址中 @ 后面的部分)
mydomain = example.com
# 发件人地址的默认域名(当发件人未指定域名时自动添加)
myorigin = $mydomain
# 监听的网络接口(all 表示所有 IPv4 和 IPv6 接口)
inet_interfaces = all
# 使用的 IP 协议版本(仅 IPv4,可改为 ipv6 或 all)
inet_protocols = ipv4
# 本地投递的域名列表(这些域名的邮件直接投递到本地邮箱)
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
# 当收件人为本地不存在的用户时,返回的 SMTP 错误代码
unknown_local_recipient_reject_code = 550
# 信任的客户端网络(来自这些网络的连接可以中继邮件,免 SASL 认证)
mynetworks = {{net}}, 127.0.0.0/8
# 邮件别名映射表(用于将虚拟名称映射到真实用户)
alias_maps = lmdb:/etc/aliases
# 邮件别名数据库(与 alias_maps 保持一致)
alias_database = lmdb:/etc/aliases
# 用户家目录下的邮件存储格式(Maildir 格式)
home_mailbox = Maildir/
# 外部邮箱投递命令(留空表示不使用外部命令投递)
mailbox_command =
# 邮箱投递传输方式(通过 Dovecot LMTP 进行本地投递)
mailbox_transport = lmtp:unix:private/dovecot-lmtp
# 调试信息详细级别(数字越大日志越详细)
debug_peer_level = 2
# 调试器启动命令(当进程崩溃时调用的调试工具)
debugger_command =
PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
ddd $daemon_directory/$process_name $process_id & sleep 5
# sendmail 兼容命令的路径
sendmail_path = /usr/sbin/sendmail.postfix
# newaliases 命令的路径(用于重建别名数据库)
newaliases_path = /usr/bin/newaliases.postfix
# mailq 命令的路径(用于查看邮件队列)
mailq_path = /usr/bin/mailq.postfix
# 用于执行特权操作的辅助组(如 postdrop)
setgid_group = postdrop
# HTML 文档目录(no 表示不安装 HTML 文档)
html_directory = no
# 手册页目录
manpage_directory = /usr/share/man
# 示例配置目录
sample_directory = /usr/share/doc/postfix/samples
# README 文档目录
readme_directory = /usr/share/doc/postfix/README_FILES
# 是否在接收邮件时启用 TLS 加密(此处为禁用)
smtpd_use_tls = no
# TLS 证书文件路径
smtpd_tls_cert_file = /etc/pki/tls/certs/postfix.pem
# TLS 私钥文件路径
smtpd_tls_key_file = /etc/pki/tls/private/postfix.key
# TLS 安全级别(may 表示尝试加密但不强制)
smtpd_tls_security_level = may
# 发送邮件时用于验证对方证书的 CA 证书目录
smtp_tls_CApath = /etc/pki/tls/certs
# 发送邮件时用于验证对方证书的 CA 证书文件
smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
# 发送邮件时的 TLS 安全级别(may 表示尝试加密但不强制)
smtp_tls_security_level = may
# 默认的数据库类型(lmdb 为轻量级内存映射数据库)
default_database_type = lmdb
# Postfix 动态链接库目录
shlib_directory = /usr/lib64/postfix
# 元配置文件目录(通常包含动态模块配置)
meta_directory = /etc/postfix
# SASL 认证后端类型(由 Dovecot 提供)
smtpd_sasl_type = dovecot
# Dovecot 认证套接字路径(相对于 Postfix 队列目录)
smtpd_sasl_path = private/auth
# 启用 SASL 认证
smtpd_sasl_auth_enable = yes
# SASL 安全选项(禁止匿名登录)
smtpd_sasl_security_options = noanonymous
# SASL 认证时使用的本地域名
smtpd_sasl_local_domain = $myhostname
# 收件人限制规则(允许信任网络和已认证用户,拒绝未授权目标)
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

Dovecot服务#

  • 位置: /etc/dovecot/dovecot.conf
    • ==主配置文件==
# 启用的服务协议(IMAP 收信、POP3 收信、LMTP 本地投递)
protocols = imap pop3 lmtp
# 监听的网络地址(* 表示所有 IPv4 地址,:: 表示所有 IPv6 地址)
listen = *, ::
# 字典服务配置块(用于配额、反垃圾等扩展功能的键值存储)
dict {
}
# 加载 conf.d 目录下的所有 .conf 配置文件
!include conf.d/*.conf
# 尝试加载 local.conf(如果文件存在则覆盖默认配置,通常用于本地自定义)
!include_try local.conf

  • 位置: /etc/dovecot/conf.d/10-mail.conf
    • ==邮件存储配置==
# ===== Dovecot 邮件存储与命名空间配置 =====
# 邮件存储位置和格式(使用 Maildir 格式,存储于用户家目录下的 Maildir 目录)
mail_location = maildir:~/Maildir
# 定义默认收件箱命名空间(IMAP 客户端看到的文件夹结构)
namespace inbox {
# 标记此命名空间为主收件箱
inbox = yes
# 定义“已删除”邮件箱
mailbox Trash {
# 客户端自动订阅此邮箱
auto = subscribe
# 符合 IMAP 标准的特殊用途属性(\Trash 表示垃圾箱)
special_use = \Trash
}
# 定义“已发送”邮件箱
mailbox Sent {
# 客户端自动订阅此邮箱
auto = subscribe
# \Sent 属性表示已发送邮件
special_use = \Sent
}
# 定义“草稿”邮件箱
mailbox Drafts {
# 客户端自动订阅此邮箱
auto = subscribe
# \Drafts 属性表示草稿
special_use = \Drafts
}
# 定义“垃圾邮件”邮件箱
mailbox Junk {
# 客户端自动订阅此邮箱
auto = subscribe
# \Junk 属性表示垃圾邮件
special_use = \Junk
}
}
# 具有邮件操作特权的系统组(通常为 mail 组)
mail_privileged_group = mail
# 允许访问邮件的有效用户 UID 最小值(低于此 UID 的用户将被拒绝访问,如 root)
first_valid_uid = 1000
# 针对 indexer-worker 协议的特定配置(此处为空,表示不覆盖默认设置)
protocol !indexer-worker {
}
# mbox 格式文件写入时的锁定方式(fcntl 为文件锁,兼容性好)
mbox_write_locks = fcntl

  • 位置: /etc/dovecot/conf.d/10-auth.conf
    • ==认证配置==
disable_plaintext_auth = no
auth_mechanisms = plain login
!include auth-system.conf.ext

  • 位置: /etc/dovecot/conf.d/auth-system.conf.ext
    • ==认证细节==
passdb {
driver = pam
}
userdb {
driver = passwd
}

  • 位置: /etc/dovecot/conf.d/10-ssl.conf
    • ==SSL配置==
ssl = no
ssl_min_protocol = TLSv1.2

  • 位置: /etc/dovecot/conf.d/10-master.conf
    • ==套接字配置==
# ===== Dovecot 主服务与监听配置 =====
# IMAP 登录服务(负责接受客户端 IMAP 连接)
service imap-login {
# IMAP 普通端口监听器(143 端口,无加密)
inet_listener imap {
}
# IMAPS 加密端口监听器(993 端口,SSL/TLS 加密)
inet_listener imaps {
}
}
# POP3 登录服务(负责接受客户端 POP3 连接)
service pop3-login {
# POP3 普通端口监听器(110 端口,无加密)
inet_listener pop3 {
}
# POP3S 加密端口监听器(995 端口,SSL/TLS 加密)
inet_listener pop3s {
}
}
# 邮件提交登录服务(用于客户端通过 SMTP 提交邮件,587 和 465 端口)
service submission-login {
# SMTP 提交端口(587 端口,通常配合 STARTTLS)
inet_listener submission {
}
# SMTPS 加密提交端口(465 端口,SSL/TLS 直接加密)
inet_listener submissions {
}
}
# LMTP 本地邮件投递服务(Postfix 通过此服务将邮件投递到用户邮箱)
service lmtp {
# Unix 套接字,供 Postfix 连接进行本地投递
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600 # 套接字权限(仅 owner 可读写)
user = postfix # 套接字所有者
group = postfix # 套接字所属组
}
}
# IMAP 主服务(登录后的 IMAP 会话处理)
service imap {
}
# POP3 主服务(登录后的 POP3 会话处理)
service pop3 {
}
# 邮件提交主服务(处理客户端提交邮件的后台逻辑)
service submission {
}
# 认证服务(验证用户密码,提供用户信息)
service auth {
# Unix 套接字,用于 Dovecot 内部用户数据库查询
unix_listener auth-userdb {
mode = 0666 # 允许所有用户读写(内部通信使用)
}
# Unix 套接字,供 Postfix 进行 SASL 认证
unix_listener /var/spool/postfix/private/auth {
mode = 0666 # 允许 Postfix 读写
}
}
# 认证辅助工作进程(执行密码哈希等耗时操作)
service auth-worker {
}
# 字典服务(用于存储配额、反垃圾等扩展数据)
service dict {
# Unix 套接字,供其他 Dovecot 进程访问字典数据
unix_listener dict {
}
}

Terminal window
[root@Server ~]# ss -lnutp | egrep "[:](53|25|143)"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
0.0.0.0:143 0.0.0.0:* users:(("dovecot"
0.0.0.0:25 0.0.0.0:* users:(("master",
10.0.0.101:53 0.0.0.0:* users:(("unbound"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
10.0.0.101:53 0.0.0.0:* users:(("unbound"
[::]:143 [::]:* users:(("dovecot"
[::]:25 [::]:* users:(("master",

task任务#

服务端#

- name: "下载安装 {{server_1}} server"
yum:
name: "{{server_1}}"
state: present
- name: "{{server_1}} 默认配置文件"
blockinfile:
path: /usr/share/unbound/fedora-defaults.conf
block: |
interface: {{server_ip}}
access-control: {{net}} allow
marker: "# {mark} Ansible"
insertafter: "^.*interface-automatic"
- name: "{{server_1}} 子配置文件"
template:
src: example-com.conf.j2
dest: /etc/unbound/local.d/example-com.conf
- name: "检测配置是否有误"
shell: "unbound-checkconf"
register: unbound_check
ignore_errors: yes
- name: "{{server_1}}启动&&开机自启动"
systemd:
name: "{{server_1}}"
state: started
enabled: yes
- name: "测试{{server_1}}"
shell: "ss -lntup | grep unbound"
register: check_re
- name: "输出测试结果"
debug:
msg: '恭喜! "{{server_1}}"服务配置检测成功,启动准备就绪!'
when: check_re.stdout_lines is search "10.0.0.101:53"
- name: "修改本地DNS"
copy:
content: 'nameserver {{server_ip}}'
dest: /etc/resolv.conf
- name: "nslookup 解析测试"
shell: 'nslookup mailserver.example.com'
register: nslookup_end
- name: "解析结果"
debug:
msg: "{{nslookup_end.stdout_lines}}"
- name: "下载 {{server_2}} && {{server_3}}"
yum:
name: "{{item}}"
state: present
loop:
- "{{server_2}}"
- "{{server_3}}"
- name: "{{server_2}} 主配置文件"
template:
src: main.cf.j2
dest: /etc/postfix/main.cf
- name: "{{server_2}}启动&&开机自启动"
systemd:
name: "{{server_2}}"
state: started
enabled: yes
- name: "批量配置{{server_3}}"
template:
src: "{{item.s}}"
dest: "{{item.d}}"
loop:
- s: dovecot.conf.j2
d: /etc/dovecot/dovecot.conf
- s: 10-mail.conf.j2
d: /etc/dovecot/conf.d/10-mail.conf
- s: 10-auth.conf.j2
d: /etc/dovecot/conf.d/10-auth.conf
- s: auth-system.conf.ext.j2
d: /etc/dovecot/conf.d/auth-system.conf.ext
- s: 10-ssl.conf.j2
d: /etc/dovecot/conf.d/10-ssl.conf
- s: 10-master.conf.j2
d: /etc/dovecot/conf.d/10-master.conf
- name: "{{server_3}}启动&&开机自启动"
systemd:
name: "{{server_3}}"
state: started
enabled: yes
- name: "所有服务端口测试"
shell: "ss -lnutp | egrep '[:](53|25|143)'"
register: result
- name: "打印输出最后结果"
debug:
msg: "{{result.stdout_lines}}"
- name: Create group {{user}}
group:
name: "{{user}}"
gid: "{{uid}}"
- name: Create user {{user}}
user:
name: "{{user}}"
group: "{{user}}"
uid: "{{uid}}"
shell: /bin/bash
create_home: true
- name: Echo passwd {{user}}
shell: 'echo "{{passwd}}" | passwd --stdin "{{user}}"'
- name: Test {{user}}
shell: "tail -1 /etc/passwd"
register: user_re
- name: Print user_re
debug:
var: user_re.stdout_lines

客户端#

- name: Configuer dns file
copy:
content: 'nameserver {{server_ip}}'
dest: /etc/resolv.conf
- name: dig test
command: 'dig -t MX example.com'
register: dig_re
- name: Print dig_re
debug:
msg: "{{dig_re.stdout_lines}}"
- name: Install {{server_1}} server
yum:
name: "{{server_1}}"
state: present

handler触发器#

服务端#

- name: Restart "{{server_1}}"
systemd:
name: "{{server_1}}"
state: restarted
when: unbound_check.stdout_lines is search "no errors"

主任务#

- name: 配置 Server 主机
hosts: Server
roles:
- Server
- name: 配置 Client 主机
hosts: Client
roles:
- Client

Start#

Terminal window
1)目录结构
[root@Zabbix roles]# tree /ansible/roles/
/ansible/roles/
├── Client
│   ├── files
│   ├── handlers
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   └── vars
│   └── main.yml
├── Server
│   ├── files
│   ├── handlers
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   │   ├── 10-auth.conf.j2
│   │   ├── 10-mail.conf.j2
│   │   ├── 10-master.conf.j2
│   │   ├── 10-ssl.conf.j2
│   │   ├── auth-system.conf.ext.j2
│   │   ├── dovecot.conf.j2
│   │   ├── example-com.conf.j2
│   │   └── main.cf.j2
│   └── vars
│   └── main.yml
└── site.yml
2)语法检测
[root@Zabbix roles]# ansible-playbook --syntax-check site.yml
playbook: site.yml
3)运行剧本
'✅️全部恢复快照--->重头开始✅️'
[root@Zabbix roles]# ansible-playbook site.yml
_____________________
< PLAY [配置 Server 主机] >
---------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
_____________________________________
< TASK [Server : 下载安装 unbound server] >
-------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
________________________________
< TASK [Server : unbound 默认配置文件] >
--------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_______________________________
< TASK [Server : unbound 子配置文件] >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________
< TASK [Server : 检测配置是否有误] >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________________
< TASK [Server : unbound启动&&开机自启动] >
----------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
___________________________
< TASK [Server : 测试unbound] >
---------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
________________________
< TASK [Server : 输出测试结果] >
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Server] => {
"msg": "恭喜! \"unbound\"服务配置检测成功,启动准备就绪!"
}
_________________________
< TASK [Server : 修改本地DNS] >
-------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_______________________________
< TASK [Server : nslookup 解析测试] >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
______________________
< TASK [Server : 解析结果] >
----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Server] => {
"msg": [
"Server:\t\t10.0.0.101",
"Address:\t10.0.0.101#53",
"",
"Name:\tmailserver.example.com",
"Address: 10.0.0.101"
]
}
_______________________________________
< TASK [Server : 下载 postfix && dovecot] >
---------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server] => (item=postfix)
changed: [Server] => (item=dovecot)
_______________________________
< TASK [Server : postfix 主配置文件] >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________________
< TASK [Server : postfix启动&&开机自启动] >
----------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_____________________________
< TASK [Server : 批量配置dovecot] >
-----------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server] => (item={'s': 'dovecot.conf.j2', 'd': '/etc/dovecot/dovecot.conf'})
changed: [Server] => (item={'s': '10-mail.conf.j2', 'd': '/etc/dovecot/conf.d/10-mail.conf'})
changed: [Server] => (item={'s': '10-auth.conf.j2', 'd': '/etc/dovecot/conf.d/10-auth.conf'})
changed: [Server] => (item={'s': 'auth-system.conf.ext.j2', 'd': '/etc/dovecot/conf.d/auth-system.conf.ext'})
changed: [Server] => (item={'s': '10-ssl.conf.j2', 'd': '/etc/dovecot/conf.d/10-ssl.conf'})
changed: [Server] => (item={'s': '10-master.conf.j2', 'd': '/etc/dovecot/conf.d/10-master.conf'})
__________________________________
< TASK [Server : dovecot启动&&开机自启动] >
----------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________
< TASK [Server : 所有服务端口测试] >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________
< TASK [Server : 打印输出最后结果] >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Server] => {
"msg": [
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=9)) ",
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=7)) ",
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=5)) ",
"udp UNCONN 0 0 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=3)) ",
"tcp LISTEN 0 100 0.0.0.0:25 0.0.0.0:* users:((\"master\",pid=5473,fd=13)) ",
"tcp LISTEN 0 100 0.0.0.0:143 0.0.0.0:* users:((\"dovecot\",pid=7419,fd=39))",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=10))",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=8)) ",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=6)) ",
"tcp LISTEN 0 256 10.0.0.101:53 0.0.0.0:* users:((\"unbound\",pid=3211,fd=4)) ",
"tcp LISTEN 0 100 [::]:143 [::]:* users:((\"dovecot\",pid=7419,fd=40))"
]
}
__________________________________
< TASK [Server : Create group jiu] >
----------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_________________________________
< TASK [Server : Create user jiu] >
---------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_________________________________
< TASK [Server : Echo passwd jiu] >
---------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
__________________________
< TASK [Server : Test jiu] >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Server]
_______________________________
< TASK [Server : Print user_re] >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Server] => {
"user_re.stdout_lines": [
"jiu❌1010:1010::/home/jiu:/bin/bash"
]
}
_____________________
< PLAY [配置 Client 主机] >
---------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
____________________________________
< TASK [Client : Configuer dns file] >
------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Client]
__________________________
< TASK [Client : dig test] >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [Client]
______________________________
< TASK [Client : Print dig_re] >
------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Client] => {
"msg": [
"",
"; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.16 <<>> -t MX example.com",
";; global options: +cmd",
";; Got answer:",
";; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36826",
";; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1",
"",
";; OPT PSEUDOSECTION:",
"; EDNS: version: 0, flags:; udp: 1232",
";; QUESTION SECTION:",
";example.com.\t\t\tIN\tMX",
"",
";; ANSWER SECTION:",
"example.com.\t\t3600\tIN\tMX\t10 mailserver.example.com.",
"",
";; Query time: 0 msec",
";; SERVER: 10.0.0.101#53(10.0.0.101)",
";; WHEN: 六 4月 11 18:26:01 CST 2026",
";; MSG SIZE rcvd: 67"
]
}
____________________________________________
< TASK [Client : Install thunderbird server] >
--------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [Client]
____________
< PLAY RECAP >
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Client: ok=4 changed=2 failed=0
Server: ok=22 changed=18 failed=0

截图验证#

image-20260411175424081
image-20260411175424081

image-20260411175542690
image-20260411175542690

image-20260411175631492
image-20260411175631492

image-20260411182913569
image-20260411182913569

image-20260411181148176
image-20260411181148176

image-20260411181408048
image-20260411181408048

实验扩展#

特性之前实验老师的优化版本
邮件域数量example.comexample.com + test.com
DNS 类型local-zone (非权威)auth-zone (权威区域)
Postfix 域处理mydestination 直接包含 example.com使用 virtual_alias_domains 统一管理多域
通信安全全程明文 (25, 143)全程加密 (465, 993) + 强加密策略
证书无或未启用自签名证书 (带SAN)
客户端配置明文端口加密端口 + 证书例外

扩展DNS#

Terminal window
[root@Server ~]# vim /etc/unbound/conf.d/test-com.conf
auth-zone:
name: "test.com"
for-downstream: yes
for-upstream: yes
zonefile: "/etc/unbound/local.d/test-com.zone"
[root@Server ~]# vim /etc/unbound/local.d/test-com.zone
$TTL 3600
@ IN SOA dnsserver.test.com. admin.test.com. ( 2026041301 3600 300 86400 600 )
@ IN NS dnsserver.test.com.
@ IN MX 10 mailserver.test.com.
mailserver IN A 10.0.0.101
dnsserver IN A 10.0.0.101
[root@Server ~]# unbound-checkconf
unbound-checkconf: no errors in /etc/unbound/unbound.conf
[root@Server ~]# unbound-control reload
ok
[root@Server ~]# systemctl restart unbound
# 重启服务

配置 Postfix 支持多域#

  • 目的:让 Postfix 能接收并投递 @example.com@test.com 的邮件
Terminal window
1)修改配置文件
[root@Server ~]# vim /etc/postfix/main.cf
'有替换,有添加'
# 注释掉原有的 mydomain
# mydomain = example.com
# 在文件末尾添加
virtual_alias_domains = example.com, test.com
virtual_alias_maps = lmdb:/etc/postfix/virtual
2)创建虚拟用户映射文件
[root@Server ~]# vim /etc/postfix/virtual
# 清空里面的内容
jiu@example.com jiu
heima@test.com heima
3)创建系统用户并生成映射数据库
[root@Server ~]# useradd heima
[root@Server ~]# echo oldboy123.com | passwd --stdin heima
[root@Server ~]# postmap lmdb:/etc/postfix/virtual
[root@Server ~]# systemctl restart postfix

配置 TLS/SSL 加密#

目的:保护邮件传输过程中的用户名、密码和邮件内容

Terminal window
[root@Server ~]# vim san.cnf
prompt = no
default_md = sha256
req_extensions = v3_req
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
C = CN
ST = Beijing
L = Beijing
O = MailServer
CN = mailserver.example.com
[ v3_req ]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = mailserver.example.com
DNS.2 = mailserver.test.com
req: Can't open "/etc/ssl/private/mail.key" for writing, No such file or directory'
[root@Server ~]# mkdir /etc/ssl/private/
# 执行 openssl 命令生成证书和密钥
openssl req -new -x509 -days 365 -nodes \
-newkey rsa:2048 \
-keyout /etc/ssl/private/mail.key \
-out /etc/ssl/certs/mail.crt \
-config san.cnf -extensions v3_req
.+......+.....+...............+...+......+.+.........+...+......+..+++++++++++++++++++++++++++++++++++++++*.+...+.+..+.......+.....+.+......+...+..+.+..+.............+.........+........+...+.........+.+.....+...+......+.+.....+....+.........+++++++++++++++++++++++++++++++++++++++*........+...+....+...+..+.+......+...+.....+.....................+......+.+...+..........................+......+.............+...........+....+...........+...+
[root@Server ~]# vim /etc/postfix/main.cf
'有修改,有添加'
# 在接收邮件时启用 TLS 加密
smtpd_use_tls = yes
# 由no--->yes
# TLS 证书文件路径
smtpd_tls_cert_file = /etc/ssl/certs/mail.crt
# TLS 私钥文件路径
smtpd_tls_key_file = /etc/ssl/private/mail.key
# 这两个证书位置也要改变!
# TLS 安全级别(may 表示尝试加密但不强制)
smtpd_tls_security_level = may
# 新增安全强化
smtpd_tls_ciphers = high
smtp_tls_ciphers = high
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
[root@Server ~]# vim /etc/postfix/master.cf
# ⚠️注意后面还有个s-->smtps⚠️
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
[root@Server ~]# vim /etc/dovecot/conf.d/10-ssl.conf
ssl = yes
ssl_min_protocol = TLSv1.2
ssl_cert = </etc/ssl/certs/mail.crt
ssl_key = </etc/ssl/private/mail.key
[root@Server ~]# vim /etc/dovecot/conf.d/10-auth.conf
'添加以下配置'
auth_username_format = %n
# 去掉邮箱域名,只保留本地用户名(如 jiu@example.com → jiu)
[root@Server ~]# cat /etc/dovecot/conf.d/10-auth.conf | grep -v "^#" | grep -v "^$"
disable_plaintext_auth = no
auth_mechanisms = plain login
auth_username_format = %n
!include auth-system.conf.ext
[root@Server ~]# ls -l /etc/dovecot/conf.d/auth-system.conf.ext
-rw-r--r-- 1 root root 56 Apr 13 16:26 /etc/dovecot/conf.d/auth-system.conf.ext
[root@Server ~]# chmod 600 /etc/dovecot/conf.d/auth-system.conf.ext
[root@Server ~]# systemctl restart postfix dovecot

命令行测试与验证#

Terminal window
'服务端测试!'
[root@Server ~]# doveadm auth test jiu passwd
passdb: jiu auth ❌️failed❌️
extra fields:
user=jiu
# 用户认证失败❌️
[root@Server ~]# doveadm auth test jiu passwd
passdb: jiu auth succeeded
extra fields:
user=jiu
这说明 Dovecot 的认证已经完全成功!
jiu 的密码确实是 oldboy123.com,且 PAM 认证工作正常!
==================================
1. Thunderbird 中用户名填写错误
错误:只填 jiu
正确:必须填 完整邮箱地址 jiu@example.com
💡 虽然 Dovecot 用系统用户 jiu,但 Postfix 的虚拟域配置要求客户端使用 完整邮箱 登录(因为你在 /etc/postfix/virtual 中定义的是 jiu@example.com)
==================================
'客户端测试'
[root@test ~]# openssl s_client -connect mailserver.example.com:465 -quiet
depth=0 C = CN, ST = Beijing, L = Beijing, O = MailServer, CN = mailserver.example.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = CN, ST = Beijing, L = Beijing, O = MailServer, CN = mailserver.example.com
verify error:num=21:unable to verify the first certificate
verify return:1
220 mailserver.example.com ESMTP Postfix
虽然有证书警告(verify error),但这是自签名证书的正常现象。
最关键的是:收到了 220 ... ESMTP Postfix 响应 说明 SMTPS 服务完全可用
'下面是完整的显示证书!'
[root@test ~]# openssl s_client -connect mailserver.example.com:465
---
SSL handshake has read 1669 bytes and written 415 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: EA38BC7F3521EDEE77DB1F7E0F82A8D55A6AE88574FFE8BA83F938DC7511D5D1
Session-ID-ctx:
Master-Key: 9DB77C325F246D8774B59C680313AE5940CA02C8AD43A024C1989D30C0A990E532776C7747DEA213F7C209B7E8D503A5
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
TLS session ticket lifetime hint: 7200 (seconds)
Start Time: 1776079858
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---
220 mailserver.example.com ESMTP Postfix
[root@test ~]# openssl s_client -connect mailserver.example.com:993 -quiet
......
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
# 说明 IMAP 服务正常,且证书可接受(即使有警告)
a LOGIN jiu@example.com passwd
# 测试登录
a NO [AUTHENTICATIONFAILED] Authentication failed.❌️
📌 关键真相:
Dovecot 默认 不会自动剥离域名!
当客户端发送 LOGIN jiu@example.com passwd,Dovecot 会尝试用 PAM 认证用户名 jiu@example.com —— 而系统中根本没有这个用户!所以失败
正确解决方案:让 Dovecot 自动提取本地用户名(去掉 @example.com)
vim /etc/dovecot/conf.d/10-auth.conf
# 去掉邮箱域名,只保留本地用户名(如 jiu@example.com → jiu)
auth_username_format = %n
[root@Server ~]# systemctl restart dovecot
[root@test ~]# openssl s_client -connect mailserver.example.com:993 -quiet
depth=0 C = CN, ST = Beijing, L = Beijing, O = MailServer, CN = mailserver.example.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = CN, ST = Beijing, L = Beijing, O = MailServer, CN = mailserver.example.com
verify error:num=21:unable to verify the first certificate
verify return:1
# 并再次测试
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
a LOGIN jiu@example.com passwd
a 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

截图验证#

image-20260413211658066
image-20260413211658066

image-20260413205857557
image-20260413205857557

image-20260413205741040
image-20260413205741040

image-20260413205823961
image-20260413205823961

image-20260413173040914
image-20260413173040914

image-20260413173139593
image-20260413173139593

image-20260413173204698
image-20260413173204698

image-20260413183510277
image-20260413183510277

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

DNS&&邮件服务
https://www.kpyun.fun/posts/basics/netops/netops06/
作者
久棹
发布于
2025-10-28
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
久棹
只要胆子大,天天寒暑假!
公告
欢迎来到久棹的技术小站!本站专注 Linux 运维学习笔记分享,如有问题欢迎交流探讨 🎉
分类
标签
站点统计
文章
98
分类
11
标签
203
总字数
244,453
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录