提醒:本页面将不再更新、维护或者支持,文章、评论所叙述内容存在时效性,涉及技术细节或者软件使用方面不保证能够完全有效可操作,请谨慎参考!

之前一直使用AWS SES (Simple Email Service)作为邮件发送后端,按照 官方说明 配置,测试下来能收到就以为配置没有问题了。直到最近某短信邮件联动端只提示收到短信而没有邮件,这才发现SES存在丢件情况,我是配置的QQ邮箱作为接收端,通过QQ邮箱的自助查询,找到了收件丢失的原因:对于AWS来说邮件是正常投递成功(这一点后台统计也有说明),关键在于QQ邮箱,其拒收了来自AWS的邮件,理由是:

Action: failed
Final-Recipient: rfc822;
Diagnostic-Code: smtp; 550 Ip frequency limited [******** = Blocked IP 54.240.7.20]. http://service.mail.qq.com/cgi-bin/help?subtype=1&id=20022&no=1000725
Status: 5.3.0

字面上看是因为该发信IP频率过高因此被屏蔽,可能是因为AWS SES和他人共享了发信IP导致的,当然,通过 链接 查询到的原因证实了猜想。

当然最简单粗暴的方法就是采用专门独立IP,当然AWS SES也提供这项服务,当然 价格 也是非常美丽了,一个IP每月租用价格是24.95 USD,对于我这种用量不是很大的用户来说,不如租用Linode自己架设邮件服务器,实际上前期确实也是这么做的,发信系统是用Postfix在Linode上架设,一直很稳定,后期主要因为成本原因以第三方服务为主(主要还是信了AWS SES的邪)。

当然现在AWS的SES是不能再使用了,也不知道前期丢了多少件,回想起先前网站部署在阿里云上面,阿里云虽然封锁了25端口但是提供了邮件推送服务,当时试用下来邮件抵达率还是可以的,遂考虑使用阿里云的服务先顶上。

对于先前已经配置好的sendmail服务,我们只需要依葫芦画瓢的照搬阿里云的配置要求就可以了,测试25端口发信成功。

但是需要注意的是25是SMTP明文发送协议端口,肯定是不安全的,所以要采用TLS进行加密,阿里云的邮件推送也支持加密SMTPS(SMTP-over-SSL)端口号为465,那么是否可以配置sendmail采用465的加密端口进行通讯呢?我修改sendmail.mc配置如下:

define(`SMART_HOST', `smtpdm.aliyun.com')dnl
define(`RELAY_MAILER_ARGS', `TCP $h 465')dnl
define(`ESMTP_MAILER_ARGS', `TCP $h 465')dnl
define(`confAUTH_OPTIONS', `A p')dnl
TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl

很遗憾, /var/log/maillog 提示 stat=Deferred: Connection reset by smtpdm.aliyun.com ,最后通过检索找到了这么 一篇文章 ,借鉴其使用stunnel代理SMTP的465端口思路配置成功!具体步骤如下:

1、安装stunnel,直接使用 sudo yum install stunnel 命令。

2、配置stunnel,完成TLS隧道的建立。

openssl genrsa -out privkey.pem 2048
sudo openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
sudo cat privkey.pem cacert.pem >> /etc/stunnel/stunnel.pem
sudo chmod 600 /etc/stunnel/stunnel.pem
sudo chown nobody.nobody /var/run/stunnel

使用命令 sudo nano /etc/stunnel/stunnel.conf 编辑配置文件如下:

cert = /etc/stunnel/stunnel.pem
chroot = /var/run/stunnel/
pid = /stunnel.pid
setuid = nobody
setgid = nobody
output = stunnel.log

[smtps]
client = yes
accept = 2525
connect = smtpdm.aliyun.com:465

其中 [smtps] 节,则是建立SMTPS隧道的部分配置。

3、如果安装完成后无法以服务形式启动,则配置stunnel为服务,具体步骤如下:

sudo nano /etc/init.d/stunnel
#!/bin/bash
#
# Script to run stunnel in daemon mode at boot time.
#
# Check http://www.gaztronics.net/ for the
# most up-to-date version of this script.
#
# This script is realeased under the terms of the GPL.
# You can source a copy at:
# http://www.fsf.org/copyleft/copyleft.html
#
# Please feel free to modify the script to suite your own needs.
# I always welcome email feedback with suggestions for improvements.
# Please do not email for general support. I do not have time to answer
# personal help requests.

# Author: Gary Myers MIIE MBCS
# email: http://www.gaztronics.net/webform/
# Revision 1.0 - 4th March 2005

#====================================================================
# Run level information:
#
# chkconfig: 2345 99 99
# description: Secure Tunnel
# processname: stunnel
#
# Run "/sbin/chkconfig --add stunnel" to add the Run levels.
# This will setup the symlinks and set the process to run at boot.
#====================================================================

#====================================================================
# Paths and variables and system checks.

# Source function library (It's a Red Hat thing!)
. /etc/rc.d/init.d/functions

# Check that networking is up.
#
[ ${NETWORKING} ="yes" ] || exit 0

# Path to the executable.
#
SEXE=`which stunnel`

# Path to the configuration file.
#
CONF=/etc/stunnel/stunnel.conf

# Check the configuration file exists.
#
if [ ! -f $CONF ] ; then
  echo "The configuration file cannot be found!"
exit 0
fi

CHROOT=`grep '^chroot' /etc/stunnel/stunnel.conf | head -n 1 | sed 's/ //g' | awk -F= '{ print $2 }'`
PIDFILE=`grep '^pid' /etc/stunnel/stunnel.conf | head -n 1 | sed 's/ //g' | awk -F= '{ print $2 }'`
if [ -n "$CHROOT" ]; then
    PIDFILE=$CHROOT/$PIDFILE
fi

# Path to the lock file.
#
LOCK_FILE=/var/lock/subsys/stunnel

#====================================================================

#====================================================================
# Run controls:

prog=$"stunnel"

RETVAL=0

# Start stunnel as daemon.
#
start() {
  if [ -f $LOCK_FILE ]; then
    echo "stunnel is already running!"
    exit 0
  else
    echo -n $"Starting $prog: "
    $SEXE $CONF
  fi

  RETVAL=$?
  [ $RETVAL -eq 0 ] && success
  echo
  [ $RETVAL -eq 0 ] && touch $LOCK_FILE
  return $RETVAL
}


# Stop stunnel.
#
stop() {
  if [ ! -f $LOCK_FILE ]; then
    echo "stunnel is not running!"
    exit 0

  else

    echo -n $"Shutting down $prog: "
    killproc -p $PIDFILE stunnel
    RETVAL=$?
    [ $RETVAL -eq 0 ]
     rm -f $LOCK_FILE
    echo
    return $RETVAL

  fi
}

# See how we were called.
case "$1" in
   start)
  start
  ;;
   stop)
  stop
  ;;
   restart)
  stop
  start
  ;;
   condrestart)
  if [ -f $LOCK_FILE ]; then
     stop
     start
     RETVAL=$?
  fi
  ;;
   status)
  status -p $PIDFILE stunnel
  RETVAL=$?
  ;;
   *)
    echo $"Usage: $0 {start|stop|restart|condrestart|status}"
    RETVAL=1
esac

exit $RETVAL

配置stunnel为自动启动:

sudo chmod +x /etc/init.d/stunnel
sudo chkconfig --add stunnel
sudo service stunnel start

如果没有提示错误的话,使用netstat -ano命名将会看到端口2525正在被监听,这时候sendmail或者其他发件客户端就可以像使用明文SMTP端口(25)那样直接使用本地2525端口,对于sendmail来说配置文件需要略微修改如下:

define(`SMART_HOST', `[localhost]')dnl
define(`RELAY_MAILER_ARGS', `TCP $h 2525')dnl
define(`ESMTP_MAILER_ARGS', `TCP $h 2525')dnl

最后别忘记修改 /etc/mail/authinfo 配置:

AuthInfo:localhost.localdomain "U:root" "I:USERNAME" "P:PASSWORD" "M:LOGIN"

保存并使用下面命令生成哈希文件:

sudo makemap hash /etc/mail/authinfo.db < /etc/mail/authinfo

最后重新生成 sendmail.cf

sudo chmod 666 /etc/mail/sendmail.cf
sudo m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf
sudo chmod 644 /etc/mail/sendmail.cf

重启sendmail客户端:

sudo /etc/init.d/sendmail restart

实际上这样配置后加密部分已经由stunnel代管了,对于邮件客户端来说这里其实是透明的。

参考资料: