やるきなし

2021/10/21 11:29 / Mailman 3.3.3 設定メモ

Mailman 2.1 設定メモの Mailman 3 版.Mailman 2.1 の環境を Debian GNU/Linux bullseye に移行した際,Mailman 2.1 環境は一式削除して,新たにインストール(あらかじめ list pseudo user が存在していた環境なので,クリーンインストール環境とは差があるかもしれない).

Mailman 3 インストール

Mailman 3 + Postfix + Apache を想定.

sudo apt install mailman3-full

これが以下に依存して,

さらに以下が install される.

Mailman 3 の Web I/F は以下で提供される.

/usr/share/doc/mailman3/README.Debian.gz/usr/share/doc/mailman3-web/README.Debian.gz に従って設定すればOKだが,Postfix 向け alias (mailman process への転送)は /var/lib/mailman3/data/postfix_lmtp* で提供され,mailman3 install 時に /etc/postfix/main.cf に以下のとおり(コメントアウトされた状態で)追記されるので,この箇所を有効にする.

# mailman
owner_request_special = no
transport_maps = hash:/var/lib/mailman3/data/postfix_lmtp
local_recipient_maps = proxy:unix:passwd.byname $alias_maps hash:/var/lib/mailman3/data/postfix_lmtp
relay_domains = ${{$compatibility_level} < {2} ? {$mydestination} : {}} hash:/var/lib/mailman3/data/postfix_domains

Apache 向けの snippet は /etc/mailman3/apache.conf にあり,これも mailman3-web install 時に適切に Apache に組み込まれる (/etc/apache2/ 以下に適切に symlink が生成される).が,手元では以下の patch が必要だった.

-       ProxyPass /mailman3 unix:/run/mailman3-web/uwsgi.sock|uwsgi://localhost/
+       ProxyPass /mailman3/ unix:/run/mailman3-web/uwsgi.sock|uwsgi://localhost/

HyperKitty に archive させるには Mailman への REST アクセスが必要で,その api_key を /etc/mailman3/mailman-hyperkitty.cfg に書き込む.これは以下で実現される.

sudo dpkg-reconfigure python3-mailman-hyperkitty

他,以下の通り微調整.

ML (list) 作成/削除/メンバ制御

ML (list) 作成時に作成されるエイリアスについて

mailman create/var/lib/mailman3/data/postfix_lmtp (およびそのdb)が以下の通り自動生成される.

# This file is generated by Mailman, and is kept in sync with the binary hash
# file.  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.

# Aliases which are visible only in the @myn.example.com domain.
testml@myn.example.com                          lmtp:[127.0.0.1]:8024
testml-bounces@myn.example.com                  lmtp:[127.0.0.1]:8024
testml-confirm@myn.example.com                  lmtp:[127.0.0.1]:8024
testml-join@myn.example.com                     lmtp:[127.0.0.1]:8024
testml-leave@myn.example.com                    lmtp:[127.0.0.1]:8024
testml-owner@myn.example.com                    lmtp:[127.0.0.1]:8024
testml-request@myn.example.com                  lmtp:[127.0.0.1]:8024
testml-subscribe@myn.example.com                lmtp:[127.0.0.1]:8024
testml-unsubscribe@myn.example.com              lmtp:[127.0.0.1]:8024

Envelope from は testml-bounces で,また,testml-owner にはなにがしか mailman から notification が届く.自動 subscripe や参加者 command を使わせないのであれば,それ以外はこの段階で disable しておいたほうが安心かもしれない.

以下で適当に comment out して,それを /var/lib/mailman3/data/postfix_lmtp に配置して,

cat /var/lib/mailman3/data/postfix_lmtp | sed 's,\(.*-\(confirm\|join\|leave\|request\|subscribe\|unsubscribe\)\),# \1,'

その後は以下で /var/lib/mailman3/data/postfix_lmtp.db を作成する.

sudo -u list postmap /var/lib/mailman3/data/postfix_lmtp

本当は手動で調整するのは微妙.mailman createmailman remove の後に自動で処理するようにしたほうが良いはず.

追記(2021/11/5): mailman3 が起動する際に /var/lib/mailman3/data/postfix_lmtp が生成されるので,その後に hook が必要.

ML (list) の設定

Mailman 2 と異なり Mailman 3 では /usr/sbin/config_list のような設定を直接操作するコマンドが用意されていない.Web I/F でポチポチ設定するということになるが,他は REST を使うか mailman shell の Python の shell を使う.他に Python の method を list を引数に呼び出すアプローチもある.

私にとって一番やりやすいのは上記の最後のアプローチで,以下の script を作成した.これを main.py とかでどこかに置いておいて,あとは /usr/lib/mailman3/bin に symlink を置いておけば,ユーザ権限でいろいろな操作を (method を書けば)可能になる.

from mailman.testing.documentation import dump_json
from mailman.testing.documentation import call_http
from mailman.interfaces.mailinglist import SubscriptionPolicy
from mailman.interfaces.archiver import ArchivePolicy


# default method
def main():
    dump_json('http://localhost:8001/3.1/lists')


# config dump
def getconf(mlist):
    url = 'http://localhost:8001/3.1/lists/%s/config' % mlist.fqdn_listname
    dump_json(url)


# private ml default configuration
def conf(mlist):
    mlist.send_welcome_message = False
    mlist.send_goodbye_message = False
    mlist.subject_prefix = ''
    mlist.include_rfc2369_headers = False
    mlist.digests_enabled = False
    mlist.archive_policy = ArchivePolicy.private
    mlist.require_explicit_destination = False
    mlist.collapse_alternatives = False
    mlist.max_message_size = 0
    mlist.subscription_policy = SubscriptionPolicy.moderate
    mlist.unsubscription_policy = SubscriptionPolicy.moderate
    mlist.max_num_recipients = 20

# site level で footer を disable する
def site_conf():
    r = call_http('http://localhost:8001/3.1/uris')
    for key in ['list:member:regular:footer', 'list:member:digest:footer']:
        flag = True
        if 'entries' in r:
            for i in r['entries']:
                if 'name' in i:
                    if i['name'] == key:
                        flag = False
                        break
        if flag:
            s = 'file:///var/lib/mailman3/templates/site/en/{}'.format(key)
            call_http('http://localhost:8001/3.1/uris',
                      {key: s}, method='PATCH')

以下で,上記の main() が処理される.list の list を得る.

sudo -u list mailman shell -r main

以下で,指定した list の個人的好みの default の設定を行う.上記の conf(mlist) がコマンドラインで指定した list を引数に呼ばれる.

sudo -u list mailman shell -r main.conf -l testml@myn.example.com

https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/rest/docs/listconf.html?highlight=send_goodbye_message#changing-a-partial-configuration のように dump_json を使っても良いかもしれない.

他,上記 script では以下用意した.

site_conf では site level で footer を disable する設定をしている.

を参照するようにしていて,ここにからのファイルを配置する.

追記(2021/11/5): SQLite での運用時,ファイルがなくてもなんとかなっていた(空として処理される)ような気がしたのだけれど,MySQL (MariaDB) に変更したらファイルがなければ配信が止まるようになった(HyperKitty には Archive される).ファイルを touch すると勢い良く配信が始まった.

個人的好みの default の設定

mailman 自体の Default 設定は /usr/lib/python3/dist-packages/mailman/styles/base.py 参照.

mailman 2 にあったような DontReceiveDuplicates は Web I/F でユーザ毎に設定可能.Default では DontReceiveDuplicates は Off の様子.

追記 (2021/11/5) Backend DB について

SQLite3 で運用していたら以下のような Error が頻出しており (ちょうど下にある Bounce 処理の確認をしている際),

Nov 05 20:28:28 2021 (688389) Uncaught runner exception: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurri
ng prematurely)
(sqlite3.OperationalError) database is locked
[SQL: UPDATE mailinglist SET last_post_at=?, post_id=? WHERE mailinglist.id = ?]
[parameters: ('2021-11-05 11:28:23.577152', 4, 2)]
(Background on this error at: http://sqlalche.me/e/13/e3q8)
Nov 05 20:28:28 2021 (688389) Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/sqlalchemy/engine/base.py", line 1276, in _execute_context
    self.dialect.do_execute(
  File "/usr/lib/python3/dist-packages/sqlalchemy/engine/default.py", line 609, in do_execute
    cursor.execute(statement, parameters)
sqlite3.OperationalError: database is locked
sqlalchemy.exc.OperationalError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(sqlite3.OperationalError) database is locked
[SQL: UPDATE mailinglist SET last_post_at=?, post_id=? WHERE mailinglist.id = ?]

/usr/share/doc/mailman3/README.Debian に書いてあるとおり,SQLite が怪しいので MySQL に backend を置き換えた.

Database backend ================

The default configured database backend is SQLite3. This is necessary to not break automated (non-interactive) installations without installing recommends.

For productive setups, SQLite3 is not recommended as database backend. PostgreSQL or MySQL should be used instead. The database configuration can be automated by installing ‘dbconfig-pgsql’ or ‘dbconfig-mysql’ and running ‘dpkg-reconfigure mailman3’. A respective local or remote database server has to be available in this case.

(SQLite だとメールが SHUNT されたり,それを sudo -u list mailman unshuntsudo /etc/init.d/mailman3 restart で吐き出させたり,いろいろややこしい状況になっていた; 上にあるテンプレートのファイルを置いていなかったのも原因の一つかもしれない)

ひとまず MariaDB (MySQL) をインストールして

sudo aptitude install mariadb-server

あとは以下を順番に行う.

sudo dpkg-reconfigure mailman3
sudo dpkg-reconfigure mailman3-web

SQLite3 か MySQL か聞かれるので,MySQL と答え,他は以下にした(ほぼ Default; 以下 mailman3 のみ,mailman3-web もほぼ同じ).

mailman3 は ModuleNotFoundError: No module named 'pymysql' で起動できなかったので sudo aptitude install python3-pymysql して,mailman3-web は django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module. でコケている雰囲気だったので sudo aptitude install python3-mysqldb した.

追記 (2021/11/5) Bounce 処理について

Mailman 3 の Default の Bounce 処理 (See /usr/lib/python3/dist-packages/mailman/model/bounce.py)は

手元では

にして,bounce したら即 remove することにした.

disable, removal の際に list owner に通知が届く.Template は以下にある.

bounce_notify_owner_on_disable および bounce_notify_owner_on_removal でこの通知の Enable/Disable を設定できるが,True (Enabled)が Default で,このままで通常問題ない.

が,上で作成した list では owner の設定ができてなくて,設定するには Web から設定するのがおそらく簡単.http://myn.example.com/mailman3/postorius/lists/testml.myn.example.com/members/owner/ から設定.一旦1人設定すると,Removing the last owner is impossible とか怒られて削除できなくなる.

CUI から owner を設定するには,以下を上で説明している main.py に書いておいて,

from mailman.interfaces.usermanager import IUserManager
from zope.component import getUtility
from mailman.interfaces.member import MemberRole

def set_owner(mlist):
    user_manager = getUtility(IUserManager)
    admin = user_manager.get_user('myn@myn.example.com')
    mlist.subscribe(admin.addresses[0], MemberRole.owner)

def members():
    dump_json('http://localhost:8001/3.1/members')

以下で呼び出す.

sudo -u list mailman shell -r main.set_owner -l testml@myn.example.com

たぶん address が既に mailman 内に無いと get_user でコケるかもしれない.Owner を削除するには

sudo -u list mailman shell -r main.members

で該当 Owner の member_id を探して,以下を呼び出すようにする (Leaving a mailing list参照).

dump_json('http://localhost:8001/3.1/members/<member_id>', method='DELETE')

なお,disable, removal の際の list owner への通知は以下のヘッダで届く (上の bounce したら即 remove の設定だと2通同時に届く).また,bounce の log は /var/log/mailman3/bounce.log で確認できる.

Return-Path: <testml-bounces@myn.example.com>
To: testml-owner@myn.example.com
Subject: myn-error@myn.example.com's subscription disabled on Testml
From: myn@myn.example.com

Return-Path: <testml-bounces@myn.example.com>
To: testml-owner@myn.example.com
Subject: myn-error@myn.example.com unsubscribed from Testml mailing list due to bounces
From: myn@myn.example.com

Related articles