从零开始学习邮件系统搭建(一)——正经发出一封邮件

前言
  出于工作原因,需要学习搭建邮件系统,为避免日后生疏最后忘了,这里会记录我整个学习的过程,包括中途遇到的不解之处,当然也会有一个完整的搭建成品的步骤。
  网上可以搜到不少关于搭建邮件系统的文章,质量参差不齐,我本人更希望自己能了解到更多,而非照着步骤啪啪啪搭起来就算了,以后遇到问题能比较有效率地寻找解决方案。
  我一边学一边写,会有理解错的,后面发现之后会回头修正。欢迎留言指教。

涉及到概念(ps.直接看没意思,先往后看,发现不明白的英文名词再回来这里查一下吧

名词 解释
MUA (Mail User Agent)用户邮件代理,用于接收邮件。如foxmail。
MTA (Mail Transfer Agent)邮件传输代理,用于接受发邮件请求并完成转发动作。如postfix。
MDA (Mail Delivery Agent)邮件分发代理,用于将收到的邮件放进收件人信箱中,具有过滤邮件、自动回复等功能。主要的MTA程序都有自己的MDA功能,也有比较强大的第三方MDA,如procmail等。
MRA (Mail Retrieval Agent)邮件取回代理,提供POP3/IMAP协议,供MUA将邮件取出。
POP3 (Post Office Protocol version 3)邮局协议第三版。对于MUA拉取邮件到本地的动作,可以设置拉取完后删除拉取后不删除两种方式,对邮件得下载到本地后才能进行管理。
IMAP (Internet Message Access Protocol)网络信息访问协议。相比POP3,可以远程访问邮件服务器管理邮件,不一定非得先下载到本地。
SMTP (Simple Mail Transfer Protocol)简单邮件传输协议。MTA就是SMTP的一个实现,端口号通常是25。SMTP协议很早就出现,比HTTP还早,本身没有对发送方的身份验证,所以后来出现了SPF、DKIM、DMARC等东西来弥补。
Postfix 一个开源的MTA程序,负责通过SMTP协议发送邮件。是作者为改善sendmail开发的,如今很流行。
Cyrus SASL Cyrus Simple Authentication and Security Layer的缩写,是一个辅助的程序,针对SMTP的认证,它提供了saslauthd来进行账号密码的比对。同时,它还支持两个auxprop插件:sasldb,sql。反正就是提供不同方式的身份验证。后面会学到。
Dovecot 一个开源的MRA程序,按照POP3/IMAP协议提供邮件取回服务,同时,它支持对用户的身份进行验证,貌似也用来协助SMTP服务器做身份验证,而且可以做到比cyrus sasl更好?
MX 邮件交换记录。发邮件时,会根据收件人的地址后缀去DNS服务器查到MX记录,从而定位收件地址所在的服务器。
SPF (Sender Policy Framework)是为了防范垃圾邮件而提出来的一种DNS记录类型。用于登记某个域名拥有的用来外发邮件的所有IP地址。收方邮件服务器可能会根据发方的域名向DNS服务器索取发方域名的SPF记录,跟发方的IP做匹配,如果不能匹配上,那么说明这封邮件并非由真正的发方域名的服务器发出,可能会判为垃圾邮件。
PTR 反向域名解析。使得可以通过发方的IP地址反查到域名,也是判断发件人是否正常的手段。

名词的解释暂时到这可好。。另外还有那些DKIM之类,后面要用到的时候再说。

邮件系统工作原理图(ps.看这些图觉得烦,不妨先往后看吧,想要知道收发邮件的背后机制时再来这看

在简书里的这篇文章从零开始邮件服务器搭建给出了几个流程图,特别好地描述了邮件系统的工作原理,推荐过去阅读,有对流程图的详细讲解。我在下面仅把图贴过来(如果原作者不愿我把图贴过来,可以留言,我删掉便是),方便查看。

邮件系统架构
邮件服务器接收邮件
用户查收邮件过程
用户发送邮件过程

我的工作环境

服务器是阿里云上的ECS,操作系统是Centos 6.8 (64位)。下面一步步地,是从一个全新的由公共镜像创建的ECS开始,慢慢部署,琢磨。
首先,为了好看些,vim /etc/bashrc,末尾添加如下代码:

#color

use_color=true

if ${use_color}; then
    if [[ ${EUID} == 0 ]]; then
        PS1='\[\033[01;31m\]\h\[\033[01;34m\] \W \$\[\033[00m\] '
    else
        PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
    fi
else
    if [[ ${EUID} == 0 ]]; then
        PS1='\u@\h \W \$ '
    else
        PS1='\u@\h \w \$ '
    fi
fi

alias ll='ls -la'
alias vi='vim'
alias grep='grep --color=auto'

然后,修改hostname,比原先那样好看些吧。虽说Postfix会用到hostname,但因为可以在main.cf(如果你不知道这个,可以先不管,后面会说)里设置,所以,在这里修改hostname,我能想到的好处,就是好看。

vim /etc/sysconfig/network;
        HOSTNAME=mail.howard.org  #修改HOSTNAME
vim /etc/hosts;
        127.0.0.1 mail.howard.org #hosts对hostname本身关系不大,增加一个mapping,便于有些程序对hostsname的dns解析
hostname mail.howard.org;  #上面的修改需要重启主机才会生效,这里不想重启,就直接改内存里的吧。需要退出当前会话重新进来才可以看到生效

hostname要符合FQDN,可以自行百度。比如我这台主机是邮件服务器,hostname写成mail.howard.org,属于全称域名,domain就是howard.org,主机名是mail,表示该主机在域名树的位置。

安装一些软件

说明:下面安装的软件并非全是必要的,为了后面的学习,先装好了。

  1. 安装postfix:yum install postfix。我执行后发现是升级原有的版本。设置开机启动chkconfig postfix on
  2. 删除原有的sendmail:rpm -e sendmail or yum remove sendmail。本ECS没安装sendmail。
  3. 安装Cyrus SASL相关的软件:yum install cyrus-sasl cyrus-sasl-plain cyrus-sasl-md5 cyrus-sase-sql
  4. 安装dovecot:yum install dovecot。我安装的版本是1:2.0.9-22.el6。
  5. 安装MySQL:
    由于ECS默认的源里的MySQL版本是5.1,所以下面先改一下源:
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm;
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm;
rpm -ivh *.rpm;
vim /etc/yum.repos.d/remi.repo;
        [remi] enabled=1 #设置remi下面的enabled为1
yum install mysql-server; #现在安装的就是MySQL 5.5版本了
/etc/init.d/mysqld start; #启动MySQL
chkconfig mysqld on; #开机自启动
mysql -u root -p; #看到等待输入密码时,直接按回车,因为是全新安装的,所以无密码。进入后是mysql的控制台
mysql>drop database test; #删除test库
mysql>use mysql; #切换至mysql库
mysql>delete from user where user=''; #删除匿名账户
mysql>update user set password=PASSWORD('123456') where user='root';#设置root密码
mysql>grant all privileges on *.* to root@'%' identified by '123456';#设置允许远程访问(为方便才这样设的,你懂的)
mysql>flush privileges;
mysql>exit

配置防火墙

对于阿里云主机,用安全组也是不错的选择。下面是配置iptables的方法:

ll /etc/sysconfig/iptables; #本ECS默认没有配置iptables,所以这里会报找不到文件
iptables-save > /etc/sysconfig/iptables; #生成一个空的iptables配置文件
#在 :OUTPUT ACCEPT [107:11768] 跟 COMMIT 两行之间插入下面这些内容
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 993 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 995 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
#上面的意思大概就是,只对tcp连接开放22、3306、25、993、995端口,其他都封禁。
/etc/init.d/iptables restart

下面先让服务器可以正常发出一封邮件

先试试现在能不能发送了(ps.简直迫不及待啊):
  1. 启动postfix:/etc/init.d/postfix start;
  2. netstat -tlnp |grep 25;可以看到
  3. yum install telnet; 我发现ECS还没安装telnet。
  4. 用telnet跟postfix交互(不妨tail -f /var/log/maillog看输出什么log,有时候很有用)
  5. 检查一下收件箱,收到啦!虽然是在垃圾箱里。是的,只需要postfix就可以完成发邮件的事情了!不过我们也发现了两个问题,一个是这个过程没有要求验证身份;第二是在/var/log/maillog里输出不少warning。下面就来做一些配置,让发邮件这个任务完成得更好些。
修改默认邮件传输代理(MTA)

输入alternatives --display mta;看第一行,发现提示的第一行是mta - 状态是自动。现在我们把它改成手动的强制指向postfix:alternatives --config mta,根据提示,选择sendmail.postfix。这个时候再输入alternatives --display mta;,现在可以看到第一行是mta - 状态是手工

配置postfix(/etc/postfix/main.cf)

关于postfix的历史以及/etc/postfix目录下各个文件的意义,请移步看鸟哥的文章鸟哥的Linux私房菜(第二十二章)。这里备注一下修改main.cf需要注意的地方:

  • #符号是注释符
  • 给变量赋值的写法,要注意=号的两边得留空格。而这一行的开头不能有空白字符。
    如:myhostname = xxx.xxx.com
  • 可以使用$符号来延伸变量的使用,如:myorigin = $myhostname
  • 如果要给一个变量赋值多个值,值的写法建议用逗号+空格来隔开。
    如:mydestination = $myhostname, $mydomain, xxx.xxx.com
  • 可以使用多行来表示同一个设定值,只要前一行末尾有逗号,下一行开头有空白字符就行。所以前面说第一行的开头处不要有空白字符。
  • 如果对同一个设定项重复做了配置,以后面写的配置为准。
  1. 好习惯:cd /etc/postfix; cp main.cf main.cf.bk;
  2. vim main.cfps.如果没能搜到,就在文件末尾添加即可。下面所有的设定项可以通过man 5 postconf查看其意思。
      #主机名;会被后面很多设定项引用,务必使用FQDN,即完整主机名
myhostname = mail.howard.org
      #域名;一般是主机名去掉第一个.前面那截剩下的,后面也被很多设定项引用
mydomain = howard.org
      #发信源主机;对应邮件标头上的“mail from”;默认是$myhostname,如果多台主机使用同一个domain,那么就设成$mydomain
myorigin = $mydomain
      #postfix的监听接口(极重要);默认只开放给本机localhost,如果要监听整个internet的话,设为all
inet_interfaces = all
      #postfix的监听IP协议;默认是all,即同时监听IPv4和IPv6,但如果服务器的网络只支持IPv4,那就设为ipv4
inet_protocols = ipv4
      #能收信的主机名(极重要);DNS里的MX指向的主机名也要写到这里来
      #如果是邮件域网关,要加上$mydomain,其他smtp节点就不用加$mydomain
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
      #约定信任的用户端(极重要);规定了本MTA可以为哪些用户端进行Relay,就是转发了
      #千万不要open relay,会让本MTA的IP进入各种黑名单
      #鸟哥建议下面这种写法,使用hash:/etc/postfix/access,就是个外部的哈希库,可以参考鸟哥的文章👆了解详情
mynetworks = 127.0.0.0/8, hash:/etc/postfix/access
      #默认收件箱在/var/spool/mail/user,所有的邮件存放在同一个文件中
      #改成Maildir/,收件会存储在/home/user/Maildir/下,每封邮件是一个文件
      #如果改成Mailbox,则收件会存储在/home/user/Mailbox中,都存在同一个文件里
home_mailbox = Maildir/
      #在SMTP服务器的欢迎标语上显示软件版本。Postfix本身对这个不关心
smtpd_banner = $myhostname ESMTP
      #邮件最大尺寸,单位:字节
message_size_limit = 52428800
      #规定收件箱最大容量,单位:字节
mailbox_size_limit = 1073741824
  1. 由于mynetworks设置了hash:/etc/postfix/access,所以需要执行postmap hash:/etc/postfix/access来生成对应的哈希库/etc/postfix/access.db
  2. /etc/init.d/postfix restart #使配置文件生效 因为改动了inet_protocols和inet_interfaces,所以需要restart,否则reload即可
  3. 设置DNS解析,增加两条A记录,之所以其中一个主机记录写mail,因为前面$myhostname写了mail.howard.org啊。
  4. 设置DNS解析,增加一条MX记录,记录值跟$myhostname一致。因为只有一条MX记录,所以MX优先级是多少不重要啦。
  5. 设置DNS解析,增加一条SPF记录,其实是按照SPF格式增加一条TXT记录,有助于提高服务器IP信誉度。


  6. 发工单跟阿里云请求对ECS的IP跟主机域名mail.howard.org做反向域名解析,有助于提高服务器IP的信誉度。
关于MX、SPF、PRT

这几项基本都是为了增加服务器IP的信誉度的。参考这个文章的说法:如何避免你外发的电子邮件被误判为垃圾邮件。或者自己网上搜搜,很容易能理解。

关于Open Relay

在上面配置/etc/postfix/main.cf时提到Open Relay这个名词。
  如果开启了Open Relay,那么就是说,任何人都可以连接到你的SMTP服务器进行转发将邮件寄出去,不管客户端是哪里、发件人是谁、收件人是谁。这种情况是非常糟糕的,意味着你的服务器会成为发送垃圾邮件的人的工具。所以绝大部分收件服务器对Open Relay的SMTP服务器是零容忍,同时也有不少第三方机构会扫描记录这种SMTP服务器,并将其扔进黑名单。所以,不要开启Open Relay。
  那么怎么禁止Open Relay呢?换句话说就是,要怎么配置才可以限制不能被随意利用来转发邮件。无非就是,限制客户端、限制发件人、限制收件人等等。其实,现在安装的postfix,默认就是禁止Open Relay的。下面我们来理解一下是怎么禁止Open Relay的。
  在我的理解里,主要通过配置下面三个设定项来达到目的:smtpd_recipient_restrictionssmtpd_client_restrictionssmtpd_sender_restrictions。通过man 5 postconf可以查到这三个设定项的含义和用法。通过postconf -h xxxx可以查看当前配置里设定项xxxx的值。

  • smtpd_client_restrictions,用于限制发信客户端,默认值是空,即不限制。如果是给内部固定的主机使用,可以设置这项,比如:smtpd_client_restrictions = permit_mynetworks, reject_unknown_client_hostname
  • smtpd_sender_restrictions,用于限制发信人,对应MAIL FROM指令的内容,默认值是空,即不限制。 由于MAIL FROM可以很简单地伪造,所以,这个参数没啥作用其实,一般不用。
  • smtpd_recipient_restrictions,用于限制收件人,对应RCPT TO指令的内容,默认值为permit_mynetworks, reject_unauth_destination,即允许客户端属于mynetworks的发信请求,拒绝不在mydestination范围内的发信请求,这两条约定不是且的关系,而是先到先得的关系,即如果发现符合mynetworks,那么不管是否符合mydestination,都通过;相反,就算不符合mynetworks,但因为第一项并没有明说拒绝,那么继续用第二项做判断,可见permit_xxxreject_xxx的区别哈。这个设定项被用得比较多,对于版本号小于2.10的postfix,还有需要注意设置不当导致relay open的问题,参考《Postfix SMTP relay and access control》

搭配上面这三个设定项的,一般是mynetworksmydestination。所以这两项的设定极其重要。

正经地发送一封成熟的邮件

行文至此,对于一个内部使用的SMTP服务器已经完成,可以通过mynetworks设置只允许内部使用。那么下面来正经地发一封邮件,还是用telnet,顺便看一下有哪些常见的格式。

正经发一封邮件

最后

上面那个截图,用的都是英文,如果要用中文的话,需要指定编码格式之类,挺烦的,不妨打开一封从公共邮件商寄过来的邮件,看看邮件源代码。不过后面我们会借助第三方的软件来帮忙发送邮件,所以我们这里点到即止吧。
  现在已经可以正常发邮件了,还缺点啥呢?两点:1、如果我想用如foxmail之类的邮件客户端来远程连接到本邮件服务器发邮件,要怎么做呢?毕竟前面的配置,限制了mynetworks,但我们邮件客户端这头的IP可是不定的啊。2、通过TCP来连接这个SMTP服务器发邮件,这中间的数据传输都是明文的,如果邮件内容很私密很重要,就会担心被窃听,怎么办呢?我打算在下一篇文章里了解怎么解决这两个问题。

<br />
<br />
<br />

参考的文章:
  1. 鸟哥的Linux私房菜(第二十二章)
  2. 从零开始邮件服务器搭建
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容