Last modified: Monday, February 26, 2024

GNU Mailman3インストールメモ

Mailman3, FreeBSD, sendmail, apache, postorius


極々身内だけの小規模なメーリングリストをこれまでGNU Mailman2で運用してきたのですが、 python2系もmailman2もEoLということで、渋々mailman3に移行しました。 有名どころでもあるのでそれほど苦も無く入るかと思ったのですが、 非っ常に面倒くさかったので忘れないうちにメモとして残しておきます。

誤解されないように書いておくと、 Linux+Postfixな環境だと多分それほど問題無く入るように思います。 参考にできるページもGoogleでよくヒットしますし、 パッケージでサクッと入るんではないかと。 ところがFreeBSDになると参考資料激減、 加えてsendmailとなるとほとんど見当たらないという状況でした。 途中、諦めてsympaに乗り換えようかと思ったのですが、 あっちもpostfix前提のような感じ(portsでは)だったので、止めました。







Mmm3lmtp,       P=[IPC], F=PSXmnz9, S=EnvFromSMTP/HdrFromSMTP,
                R=EnvToMM3, E=\r\n, L=1024,
                A=TCP $h 8024

同じくmcファイルのLOCAL_RULESETSに以下を追加。 (2行目のR$+と$:、3行目の$*と$:の間はタブで区切る)

R$+     $: $>EnvToSMTP $1
R$+ < @ example . com . private . > $*       $: $1 < @ example . com . > $2

/etc/mail/mailertable に以下を追加。        mm3lmtp:[localhost]

/usr/local/lib/python3.9/site-packages/mailman/mta/ を以下の内容で作成。

# Copyright (C) 2001-2022 by the Free Software Foundation, Inc.
# This file is part of GNU Mailman.
# GNU Mailman 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 3 of the License, or (at your option)
# any later version.
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along with
# GNU Mailman.  If not, see .

"""Creation/deletion hooks for the Sendmail MTA."""

import os

from collections import defaultdict
from contextlib import contextmanager
from flufl.lock import Lock
from mailman.config import config
from mailman.config.config import external_configuration
from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.listmanager import IListManager
from mailman.interfaces.mta import (
    IMailTransportAgentAliases, IMailTransportAgentLifecycle)
from mailman.utilities.datetime import now
from operator import attrgetter
from public import public
from zope.component import getUtility
from zope.interface import implementer

ALIASTMPL = '{0}	%1%3@{2}.private'

def atomic(path):
    # Write a new file and then atomically rename it.
    new_path = path + '.new'
        with open(new_path, 'w', encoding='utf-8') as fp:
            yield fp
    except:                                      # noqa: E722 pragma: nocover
        os.rename(new_path, path)
        os.chmod(path, 0o664)

def _get_alias_domain(domain):
    domain_manager = getUtility(IDomainManager)
    d = domain_manager.get(domain)
    if d is not None and d.alias_domain:
        return d.alias_domain
    return domain

class _FakeList:
    """Duck-typed list for the `IMailTransportAgentAliases` interface."""

    def __init__(self, list_name, mail_host):
        self.list_name = list_name
        self.true_mail_host = mail_host
        self.mail_host = _get_alias_domain(mail_host)
        self.posting_address = '{}@{}'.format(list_name, self.mail_host)

class LMTP:
    """Connect Mailman to Sendmail via LMTP."""

    def __init__(self):
        # Locate and read the Postfix specific configuration file.
        mta_config = external_configuration(config.mta.configuration)

    def create(self, mlist):
        """See `IMailTransportAgentLifecycle`."""
        # We can ignore the mlist argument because for LMTP delivery, we just
        # generate the entire file every time.

    delete = create

    def regenerate(self, directory=None):
        """See `IMailTransportAgentLifecycle`."""
        # Acquire a lock file to prevent other processes from racing us here.
        if directory is None:
            directory = config.DATA_DIR
        lock_file = os.path.join(config.LOCK_DIR, 'mta')
        with Lock(lock_file):
            lmtp_path = os.path.join(directory, 'sendmail_virtusertable')
            with atomic(lmtp_path) as fp:

    def _generate_lmtp_file(self, fp):
        # The format for Postfix's LMTP transport map is defined here:
        # Sort all existing mailing list names first by domain, then by
        # local part.  For Postfix we need a dummy entry for the domain.
        list_manager = getUtility(IListManager)
        utility = getUtility(IMailTransportAgentAliases)
        by_domain = {}
        sort_key = attrgetter('list_name')
        for list_name, mail_host in list_manager.name_components:
            mlist = _FakeList(list_name, mail_host)
            by_domain.setdefault(mlist.mail_host, []).append(mlist)
# This file is generated by Mailman.  YOU SHOULD NOT MANUALLY EDIT THIS
# FILE unless you know what you're doing, and can keep the two files properly
# in sync.  If you screw it up, you're on your own.
""".format(now().replace(microsecond=0)), file=fp)
        for domain in sorted(by_domain):
# Aliases which are visible only in the @{} domain.""".format(domain),
            for mlist in sorted(by_domain[domain], key=sort_key):
                aliases = list(utility.aliases(mlist))
                # width = max(len(alias) for alias in aliases) + \
                #    aliases[0].count('.') + 10
                                       config, domain), file=fp)
                for alias in aliases:
                    print(ALIASTMPL.format(alias, config, domain), file=fp)
            #print(f"@{domain}	error:5.1.1:550 User unknown", file=fp)


# PLB started with exim4.cfg
# Additional configuration variables for sendmail

# sendmail doesn't need any additional configuration yet.


incoming: mailman.mta.sendmail.LMTP
outgoing: mailman.mta.deliver.deliver
lmtp_port: 8024
smtp_host: localhost
smtp_port: 25
configuration: python:mailman.config.sendmail

これで /usr/local/mailman/data/sendmail_virtusertable に以下のような内容のファイルが生成されるようになる。 このファイルを /etc/mail/virtusertable として参照する。

以上がsendmail対応の大筋になります。 多分、ここに書いた以外のところでエラーがたくさん出ると思いますが、 エラーメッセージに従って直していくことになります(たくさんありすぎて憶えていない…)


こちらの作業の方がさらに面倒でした。 最も参考になった情報は以下のものです。

基本はこの手順に従って(自分のサイトに合わせて適宜書き換えながら)進めるだけですが、 ここでも不足モジュールのエラー等が多発するので言われた通りに対処していきます。 言語やタイムゾーンの設定は要修正かと思います。

あとはapacheの設定ファイル。mod_wsgiモジュールをLoadModuleして、 以下の設定を/usr/local/etc/apache24/Includes/ 以下に置きます。

Alias /static /usr/local/mailman/static
<Directory "/usr/local/mailman/static/">
    Require ip 適切なIPアドレス

WSGIScriptAlias /mailman3 /usr/local/mailman/ process-group=mailman-web
WSGIScriptAlias /postorius /usr/local/mailman/ process-group=mailman-web

WSGIPythonHome /usr/local
WSGIPythonPath /usr/local/mailman
WSGIDaemonProcess mailman-web display-name=mailman-web maximum-requests=1000 umask=0002 user=mailman group=mailman python-path=/usr/local/mailman home=/usr/local

<Directory "/usr/local/mailman">
		Require ip 適切なIPアドレス
	WSGIProcessGroup mailman-web

エラーが出るたびに設定ファイルを修正しますが、 uwsgiやapache, mailmanなどのサービスを再起動しないと変更が有効にならないときがありました(restartでは不十分で、stop & startが必要)。


以上、FreeBSD+Sendmail+Apacheな環境でMailman3+Postoriusを動かすための情報でした。 もう次に同じようにインストールできる自信が無いですね。 とにかくパッケージとモジュールの依存が多すぎて、大したことを求めているわけでもないのに明らかにオーバスペックです。 大昔の majordomo は分かりやすかったのに、どうしてこうなったのか… 次はmlmmjに乗り換えるかもしれません。

布目 淳 (