Help us understand the problem. What is going on with this article?

Mailman で特定のメールサーバから投稿されたメールは全て配送する

More than 3 years have passed since last update.

概要

Mailman でメーリングリストを運用している時、自組織内から発信されたメールは全て受理・配送したいということがあります。例えば、自組織向けの問い合わせ窓口として Mailman を使っている場合など。本稿では、Mailman の司会判定用モジュールを拡張して、特定のメールサーバから投稿されたメールは全て受理・配送する方法を説明します。

背景

Mailman でメーリングリストを運用している時、メーリングリスト非参加者からのメールを受信するには、(1) generic_nonmember_action オプションを「承認」に設定する、または、(2) accept_these_nonmembers オプションに適当な正規表現を設定する、などの選択肢があります。しかし、前者(1)はあらゆるメールを受け付けますし、後者(2)も詐称が容易なヘッダー From に依存していますから、spam に対して脆弱な設定になってしまいます。

本稿では、

  1. 自組織の構成員が利用する送信用メールサーバを固定されている。
  2. 送信用メールサーバから Mailman に至るまでの経路上のメールサーバは、いずれも信用できる。

という前提のもとで、Received ヘッダの内容に基づいて、送信用メールサーバから投稿されたメールは全て配送するという設定を紹介します。

設定例

以下のようなコードを、/usr/lib/mailman/Mailman/Handlers/TrustedServerModerate.py として配置します。なお、パス /usr/lib/mailman/ の部分は、各自の環境にしたがって適宜に読み替えてください。

TrustedServerModerate.py
# Copyright (C) 2015 by TSUCHIYA Masatoshi <tsuchm@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

"""This module implements server based moderation.  If a user used one
of trusted remote servers to send his/her message, and only if it has
been delivered through trusted local servers, it is approved as a
trusted message.  Otherwise, it will be handled by the default rule of
Mailman.

USAGE:

Add following codes to mm_cfg.py:

TRUSTEDLOCALSERVER = [ "127.0.0.1",
                       "^192\.168\.10\." ]

TRUSTEDREMOTESERVER = [ "192.168.10.1" ]

GLOBAL_PIPELINE[GLOBAL_PIPELINE.index('Moderate')] = 'TrustedServerModerate'

"""

import re
from Mailman import mm_cfg
from Mailman.Handlers.Moderate import process as moderate_process
from Mailman.Logging.Syslog import syslog

def process(mlist, msg, msgdata):
    def extract_ipaddr(str):
        m = re.search('\[(\d+\.\d+\.\d+\.\d+)\]', str)
        if m:
            return m.group(1)
        m = re.search('\[((?:[a-fA-F0-9]{4}:+)+[a-fA-F0-9]{4})\]', str)
        if m:
            return m.group(1)
        if '[IPv6:::1]'in str:
            return '127.0.0.1'
        else:
            return ''

    def matches_p(ipaddr, patterns):
        for pat in patterns:
            if pat.startswith('^'):
                if re.match(pat, ipaddr):
                    return 1
            else:
                if pat==ipaddr:
                    return 1
        return 0

    def trusted_message_p(message):
        received=[extract_ipaddr(x) for x in message.get_all('Received') if re.match("^from", x)]
        syslog('error', 'Servers which deliver this message are %s', ' / '.join(received))
        received.reverse()
        try:
            while received and matches_p(received[-1], mm_cfg.TRUSTEDLOCALSERVER):
                received.pop()
            remote=0
            while received and matches_p(received[-1], mm_cfg.TRUSTEDREMOTESERVER):
                received.pop()
                remote+=1
            if remote > 0:
                if len(received) <= 1:
                    syslog('error', 'This message is trusted')
                    return 1
                else:
                    return 0
            else:
                return 0
        except AttributeError:
            return 0

    if mlist.generic_nonmember_action == 1 and trusted_message_p(msg):
        return
    return moderate_process(mlist, msg, msgdata)

その上で、/etc/mailman/mm_cfg.py に以下の3つの指定を記述します。第1に、構成員が利用する送信用メールサーバの IP アドレスを以下のように指定します。

TRUSTEDREMOTESERVER = [ "192.168.10.1",
                        "192.168.10.2" ]

なお、本稿の設定では、ここで指定された送信用メールサーバの経由情報に基づいてメール配送の可否を決定します。ですから、送信用メールサーバでは、SMTP AUTH などの方法により認証されたメールのみを受け付ける必要があります。

第2に、送信用メールサーバから、Mailman にたどり着くまでの既知の信頼できるメールサーバの IP アドレスを以下のように指定します。

TRUSTEDLOCALSERVER = [ "127.0.0.1",
                       "192.168.10.3",
                       "192.168.10.4",
                       "192.168.10.5" ]

なお、既知の信頼できるメールサーバには、Mailman が稼働しているサーバ自身の IP アドレスを含む必要があります。

文字列の先頭に ^ があれば正規表現として解釈しますので、上記の指定は以下の指定と等価です。

TRUSTEDREMOTESERVER = [ "^192\.168\.10\.[12]$" ]
TRUSTEDLOCALSERVER  = [ "127.0.0.1", "^192\.168\.10\.[345]$" ]

第3に、Mailman 本体に付属している Mailman/Handlers/Moderate.py の代わりに、上記のモジュールを使うように指定します。

GLOBAL_PIPELINE[GLOBAL_PIPELINE.index('Moderate')] = 'TrustedServerModerate'

解説

上記の設定例では、以下のような経路で投稿されたメールを、受理・配送します。

MUA
 ↓
送信用メールサーバ(192.168.10.1 または 2)
 ↓
中継用メールサーバ(192.168.10.3 または 4)
 ↓
Mailman サーバ(192.168.10.5)

それに対して、以下のような経路の場合、送信用メールサーバによるユーザ認証が行われていないので、配送しません。

MUA
 ↓
未知メールサーバ
 ↓
送信用メールサーバ(192.168.10.1 または 2)
 ↓
中継用メールサーバ(192.168.10.3 または 4)
 ↓
Mailman サーバ(192.168.10.5)

また、以下のような経路の場合も、Received ヘッダの偽造の可能性があるので、配送しません。

MUA
 ↓
送信用メールサーバ(192.168.10.1 または 2)
 ↓
未知メールサーバ
 ↓
中継用メールサーバ(192.168.10.3 または 4)
 ↓
Mailman サーバ(192.168.10.5)

すなわち、送信用メールサーバ・中継用メールサーバおよび Mailman サーバは自組織の管理下にあって、そのメールサーバが付加する Received ヘッダは信頼できるという前提のもとで経路情報を取り出し、送信用メールサーバでユーザ認証されたメールのみを配送する、という設定になっています。

tsuchm
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away