问题

问题

我正在寻找一个简单的 Postfix 服务器,可以修改某些特定SMTP消息的内容和收件人
到目前为止,我对自己的方法还不是很有信心,感觉自己缺少一些 Postfix 工作原理的基础知识,所以我希望得到一些指导。


语境

我工作的组织的设置对于像我这样从未真正使用过 Postfix 的人来说最初有点令人畏惧,所以希望我的解释是有意义的。

我了解到,DMZ 网络中的一台 Linux 计算机{1}正在运行 Postfix 服务器,并且所有传入电子邮件都集中到该服务器。
然后,服务器在其文件中定义一些规则,根据域确定将消息中继到/etc/postfix/transport哪些其他邮件服务器{3..N}(例如@domain1.company.org,,@domain2.company.org。如果域名只是基本名称(例如@company.org,然后消息被传递到{2}员工登录并检查邮件等的主电子邮件服务器。

INTERNET   |     DMZ    |   INTERNAL 
   SMTP ---|---> {1} ---|---> {2} 
                        \---> {3..N}

我提到的“特定 SMTP 消息”是来自一些工程师正在设置的一些简单设备的集合。这些设备可以发送的内容受到一些限制。

工程师:我无法更改主题内容或电子邮件主体的某些区域。

当这些设备发送消息时,我的理解是网络会将消息汇集到{1},然后将消息转发到适当的电子邮件服务器。

所要求的解决方案

工程师:我们是否可以要求,当来自特定地址且主题和主体简单的电子邮件在转发给收件人之前由我们的系统拦截和修改?例如,在主题和正文中,将发送“leak”一词,我们可以在转发消息之前将其更改为“freezer”吗?

所以,我本质上需要修改消息的内容它会被转发到任何其他电子邮件服务器。在消息到达 之前,需要在{1}或附近发生一些事情。{1}{2..N}

到目前为止我的方法

考虑到一切都会顺利进行{1},并且我不想对邮件流经这台机器的方式进行任何大规模更改,我想我应该启动另一台 Linux 机器来运行另一个 Postfix 服务器(我将其称为mailmunger,如图所示)作为{1a})。
然后,工程师将配置设备并指定它将发送的消息的收件人,例如[email protected]

就我而言,我会添加另一条规则(/etc/postfix/transport我认为在 中),{1}以便*@mailmunger.company.org将邮件转发到我的新 Linux 计算机。

然后{1a},我会使用类似的东西队列后内容过滤器处理消息,根据请求修改内容,从收件人地址中删除mailmunger.,然后将消息传递回{1}
这是因为Postfix 服务器relay_host的属性{1a}将使用 server 的名称进行配置{1}

INTERNET   |     DMZ    |   INTERNAL 
   SMTP ---|---> {1} ---|---> {2} 
                 / ^    \---> {3..N}
                |  |
                v  /
                {1a}

我认为这种方法是尽可能安全的,因为它会限制编写错误的过滤器的影响。但是,也许我在这个设计中犯了一个我没有注意到的错误。
我的希望是所有正常的邮件流都应按预期继续,并且只有 的邮件[email protected]会卡在我的新mailmunger机器上。


问题

  1. 我的“队列后内容过滤器”是否走在正确的轨道上?或者我应该考虑一种不同的机制,比如队列前内容过滤器
  2. {1a}服务器实际上需要transport配置其文件吗?或者我误解了该属性的用途?
  3. 这些简单的设备如何知道将 SMTP 消息发送到哪个服务器?
  4. 有什么我应该注意的问题吗?

答案1

经过一番苦苦思索和仔细测试后,我找到了解决方案!

其不足之处在于:

  • 我误解了 MX 记录,因此必须为我的mailmunger服务器创建一些新记录。
  • 不,mailmunger服务器不需要/etc/postfix/transport修改其文件。仅/etc/postfix/main.cf需要/etc/postfix/master.cf进行修改。
  • 没有太多的陷阱,只是当消息未能到达其预期目的地时进行一些令人沮丧的调试。

更详细的解决方案

所以第一步就是修改/etc/postfix/transport服务器的{1}。我需要在传输映射中添加一行,以便将消息[email protected]定向到我的mailmunger服务器。

/mailmunger.company.org/  relay:[mailmunger.company.org]

我的传输映射文件是一个正则表达式表,因此该main.cf文件{1}包含以下行:

transport_maps = regexp:/etc/postfix/transport

下一步是配置mailmunger

mailmungermain.cf

在该main.cf文件中,有两个我需要的关键部分。

relayhost = {1}.company.org
local_recipient_maps =

定义relayhost变量是告诉服务器“如果你不是应该接收消息的人,请将其传递给这个人”。
这是一个关键部分,因为在我修改消息后,我将它放回到队列中不同的接受者。

local_recipient_maps变量需要一个空值,因为否则,当服务器在我的过滤器脚本可以处理邮件之前找不到指定收件人的邮箱时,它将拒绝邮件。
我不在乎收件人是否在服务器上没有帐户,因为我的目的是修改邮件内容收件人地址。
如果我的脚本失败,消息将被退回给发件人。

mailmungermaster.cf

我在这里所做的更改基本上正是队列后内容过滤器指南指定。

smtp      inet  n       -       y       -       -       smtpd
  -o content_filter=filter:dummy
...
filter    unix  -       n       n       -       10      pipe
  flags=Rq  user=postfix-filter  null_sender=
  argv=/opt/postfix-filters/start-filter -f ${sender} -- ${recipient}

smtp服务添加了一个额外的选项,它将消息定向到filter管道服务。
filter服务以我创建的特殊postfix-filter用户身份运行我的脚本,并根据脚本的退出代码,将消息放回队列或将其弹回到发件人。

其他的东西都在mailmunger

您可能会在服务配置中注意到filter,还需要创建/配置一些其他内容。

  1. 第一步是创建一个新目录/opt/postfix-filters/.我只是喜欢那个位置。
  2. 接下来是创建postfix-filter用户。我使用 shell 指定了该用户/usr/sbin/nologin,并将其主目录指定为/opt/postfix-filter
  3. 然后我使用以下逻辑 start-filter在目录中创建了该文件:/opt/postfix-filters/
    #!/bin/bash
    
    ##############################################################################
    #
    # Based on documented example here:
    #  http://www.postfix.org/FILTER_README.html
    #
    # Example Execution:
    #  /path/to/script -f sender -- recipient... <message-file
    #
    ##############################################################################
    
    shopt -s extglob
    
    ## Making of copy of the positional arguments
    _ARGS=("$@")
    
    ## Defining paths to directories that we will be using
    _BASE_DIR="/opt/postfix-filters"
    
    _TMP_DIR="${_BASE_DIR}/tmp"
    [ -d ${_TMP_DIR} ] || {
        echo "${_TMP_DIR} does not exist"; exit $EX_TEMPFAIL; }
    _TMP_FILE="${_TMP_DIR}/in.$$"
    
    _LOG_FILE="${_BASE_DIR}/log"
    
    _FILTERS_DIR="${_BASE_DIR}/filters"
    SENDMAIL="/usr/sbin/sendmail -G -i " # NEVER NEVER NEVER use "-t" here.
    
    
    ## Exit codes from <sysexits.h>
    EX_TEMPFAIL=75
    EX_UNAVAILABLE=69
    
    ## Clean up when done or when aborting.
    function _cleanup() {
      #echo ${_ARGS[@]}
      #cat ${_TMP_FILE}
      rm -f ${_TMP_FILE}
    }
    trap "_cleanup" 0 1 2 3 15
    
    
    ## Creating a temp file (in the INSPECT_DIR directory) with a name that uses
    ## the current Process ID ($$).
    ## This script assumes that the message content is coming in via stdin.
    cat >${_TMP_FILE} || {
        echo "Cannot save mail to file"; exit $EX_TEMPFAIL; }
    
    ## Pass the file and args to each script in the directory containing
    ## filter scripts. If any of them fails with exit code 1, then exit
    ## this parent script. Assume 0 and all other error codes mean
    ## continue.
    for _SCRIPT in ${_FILTERS_DIR}/!(README*) ; do
        ## A quick test that the path is a file and is executable
        ( [ -f "${_SCRIPT}" ] && [ -x "${_SCRIPT}" ] ) || continue
        ## Now we run the script
        echo "running: ${_SCRIPT} -- ${_TMP_FILE} ${_ARGS[@]}" >> "${_LOG_FILE}"
        _NEWARGS=$( ${_SCRIPT} "${_TMP_FILE}" ${_ARGS[@]} )
        if [ "$?" -eq 0 ]
        then
            _ARGS=("${_NEWARGS}")
            echo "newargs? ${_ARGS[@]}" >> "${_LOG_FILE}"
        elif [ "$?" -eq 2 ]
        then
            echo "args not modified" >> "${_LOG_FILE}"
        else
            ## If the script exits with any exit code other than 0 or 2, then we
            ## will "reject" the message based on the content. To do the rejection,
            ## we exit this script with the EX_UNAVAILABLE exit code.
            echo "Message content rejected"
            exit $EX_UNAVAILABLE
        fi
    done
    
    ## Putting the mail back in the queue using the args given to this
    ## script by Postfix.
    $SENDMAIL ${_ARGS[@]} <"${_TMP_FILE}"
    
    ## Exiting with the return status of sending the mail
    exit $?
    
  4. 还有一些其他目录需要在 中创建/opt/postfix-filters/,例如filters/tmp/
  5. 然后,剩下的就是在该filters/目录中创建一个或多个可由用户执行的脚本postfix-filter。我会让你弄清楚如何做到这一点,但我会留下脚本的建议修改传入参数(最终传递给sendmail命令)并应返回以下任一退出代码:
    • 0(关于成功参数sendmail已修改)
    • 2(成功时,但参数未修改)
    • 无论如何(有什么问题,退出并退回消息)。

开始做事

剩下的就是确保 Postfixmailmunger使用我的新配置启动。

[root@mailmunger /etc/postfix]# postfix reload

答案2

为什么不使用米尔特,例如邮件蒙格?这使您可以准确地完成您需要的操作,而无需使用第二个 Postfix 实例或其他奇怪的脚本和 hack。

相关内容