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
これが以下に依存して,
- mailman3
- mailman3-web
- python3-mailman-hyperkitty
さらに以下が install される.
- python3-django-hyperkitty
- python3-django-mailman3
- python3-django-postorius
Mailman 3 の Web I/F は以下で提供される.
- list 制御: Postorius
- list archive: HyperKitty
/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
他,以下の通り微調整.
/etc/mailman3/mailman-web.py
のEMAILNAME
変数は install 時(debconf 時)に適切に設定されるはずだが,確認する.- mailman 3 の site level owner の default は
changeme@example.com
なので,/etc/mailman3/mailman.cfg
のsite_owner
で変更する. - HyperKitty の default のドメインは
example.com
になっているので,設定した host (myn.example.com
とする) に http://myn.example.com/mailman3/admin/ でアクセスすると Django administration page が現れるので,Sites
の箇所 (http://myn.example.com/mailman3/admin/sites/site/) で修正する(多分そのままでも問題ないのかもしれない).
ML (list) 作成/削除/メンバ制御
-
create ml
% sudo -u list mailman create testml@myn.example.com
-
remove ml
% sudo -u list mailman remove testml@myn.example.com
-
list member
sudo -u list mailman members testml@myn.example.com
-
add member
コマンドラインから1メールアドレスずつ追加する場合以下.ファイルにリストして追加する場合は
-
の箇所をファイル名にする(そして pipe しない).% echo myn@myn.example.com | sudo -u list mailman addmembers - testml@myn.example.com
以下のような welcome message が送付される.
To: myn@myn.example.com Subject: Welcome to the "Testml" mailing list From: testml-request@myn.example.com Welcome to the "Testml" mailing list! To post to this list, send your message to: testml@myn.example.com You can unsubscribe or make adjustments to your options via email by sending a message to: testml-request@myn.example.com with the word 'help' in the subject or body (don't include the quotes), and you will get back a message with instructions. You will need your password to change your options, but for security purposes, this password is not included here. If you have forgotten your password you will need to reset it via the web UI.
-
delete (remove) member
% sudo -u list mailman delmembers -l testml@myn.example.com -m myn@myn.example.com
以下のような goodbye message が送付される(body は空).
To: <myn@myn.example.com> Subject: You have been unsubscribed from the Testml mailing list From: <testml-bounces@myn.example.com>
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 create
や mailman remove
の後に自動で処理するようにしたほうが良いはず.
追記(2021/11/5): mailman3 が起動する際に /var/lib/mailman3/data/postfix_lmtp
が生成されるので,その後に hook が必要.
追記(2022/4/6): /var/lib/mailman3/data/postfix_lmtp
に作成されるエイリアスのリストは /usr/lib/python3/dist-packages/mailman/mta/aliases.py
で以下の通り定義されている.ここを修正すれば制御可能.
SUBDESTINATIONS = (
'bounces',
'confirm',
'join',
'leave',
'owner',
'request',
'subscribe',
'unsubscribe',
)
ということで以下にした.Package が upgrade される際に上書きされるけど.
SUBDESTINATIONS = (
'bounces',
'owner',
)
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 では以下用意した.
main.getconf
: list の configuration dumpsite_conf
: site level の設定
site_conf
では site level で footer を disable する設定をしている.
file:///var/lib/mailman3/templates/site/en/list:member:regular:footer
file:///var/lib/mailman3/templates/site/en/list:member:digest:footer
を参照するようにしていて,ここに空(zero file size)のファイルを配置する.
追記(2021/11/5): SQLite での運用時,ファイルがなくてもなんとかなっていた(空として処理される)ような気がしたのだけれど,MySQL (MariaDB) に変更したらファイルがなければ配信が止まるようになった(HyperKitty には Archive される).ファイルを touch すると勢い良く配信が始まった.
個人的好みの default の設定
mailman 自体の Default 設定は /usr/lib/python3/dist-packages/mailman/styles/base.py
参照.
-
mlist.send_welcome_message = False
,mlist.send_goodbye_message = False
welcome/goodbye message を disable する.See
/usr/lib/python3/dist-packages/mailman/app/membership.py
. -
mlist.subject_prefix = ''
Subject の先頭部分に追加される prefix.ML の存在をあまりユーザに気にさせたくないので,空(’’)にする.
-
mlist.include_rfc2369_headers = False
RFC2369のヘッダを付加するか否か.ユーザに操作させたくない(MLの存在もあまり気にさせたくない)ので,disable する.
-
mlist.digests_enabled = False
Digest は使わないので disable する.
-
mlist.archive_policy = ArchivePolicy.private
HyperKitty での Archive を
private
モードで運用 (Web でログインしないと見られない).Default はpublic
だったような気がする.ArchivePolicy
はenum
になっていて,他に取れる値はnever
.See/usr/lib/python3/dist-packages/mailman/interfaces/archiver.py
. -
mlist.require_explicit_destination = False
Default True.True だと,ML に Bcc で送付するなど(もしくは転送など),ML の email address が To や Cc に含まれない場合に admin proval が要求される.
-
mlist.collapse_alternatives = False
元のメールのまま配信したいので disable する.mlist.collapse_alternatives の Default は False.
-
mlist.max_message_size = 0
0 (無制限)にする.Default: 40 (kilobytes).
-
mlist.subscription_policy = SubscriptionPolicy.moderate
,mlist.unsubscription_policy = SubscriptionPolicy.moderate
Default は
confirm
.ユーザによる confirm があれば subscribe/unsubscribe 可能.勝手に join/leave して欲しくないのでmoderate
にする.SubscriptionPolicy
はenum
になっていて,他に取れる値はopen
,confirm_then_moderate
.See/usr/lib/python3/dist-packages/mailman/interfaces/mailinglist.py
. -
mlist.max_num_recipients = 20
この数字以上の宛先がメールに含まれている場合に admin proval を要求するというもの.0 だと制限がなくなる.Default は 10.
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 unshunt
や sudo /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 もほぼ同じ).
- Authentication plugin for MySQL database: default
- MySQL database name for mailman3: mailman.db
- MySQL username for mailman3: mailman3@localhost
- MySQL application password for mailman3: (empty)
- If left blank, a random password will be generated.
- Name of the database’s administrative user: root
- For MySQL, this is almost always “root”. Note that this is not the same as the Unix login “root”.
- Add the HyperKitty configuration to mailman.cfg?: Yes
- The content of /usr/share/mailman3/mailman_cfg_hyperkitty_snippet.cfg will be added to /etc/mailman3/mailman.cfg.
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 score が
bounce_score_threshold
(default: 5) を超えると配信が disable され,disable されたアドレスには disable warning が送信される.Warning の template は/usr/lib/python3/dist-packages/mailman/templates/en/list:user:notice:warning.txt
.Bounce するけど. -
Disable warning が
bounce_you_are_disabled_warnings
回 (default: 3) 送信されるた後に list から remove される-
Disable warning の送信 interval は
bounce_you_are_disabled_warnings_interval
で決まる(default: 7d). -
bounce_you_are_disabled_warnings
が 0 であれば,warning を送信せずに disable のタイミングで remove される
-
-
Bounce score は bounce 毎 1 increment されるが,Bounce が発生した日に既に increment されている場合は increment されない.
-
Bounce score は
bounce_info_stale_after
(default: 7d) の間に bounce が発生しなければ reset (0 に戻る)される.
手元では
bounce_score_threshold
: 1bounce_you_are_disabled_warnings
: 0
にして,bounce したら即 remove することにした.
disable, removal の際に list owner に通知が届く.Template は以下にある.
/usr/lib/python3/dist-packages/mailman/templates/en/list:admin:notice:disable.txt
/usr/lib/python3/dist-packages/mailman/templates/en/list:admin:notice:removal.txt
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