概要
Mailman でメーリングリストを運用している時、非会員からもメールを受理・配送したい場合があります。しかし、非会員によって投稿された全てのメールを単純に受理・配送すると、spam が大量にあふれかえってしまい、使い物になりません。本稿では、Mailman の司会依頼メールの仕組みを利用して、spam ボットによる投稿を抑止しながらも、任意の投稿者からのメールを受理・配送する方法を説明します。
背景
Mailman でメーリングリストを運用している時、非会員からもメールを受理・配送したい事例があります。例えば、筆者の所属部門では、事務室の問合せ窓口に Mailman を使っています。これには2つの理由があります。第1は、過去の問合せ及び対応の記録を残すためです。窓口アドレスを、単純なエイリアスとして実装すると、過去の問合せ及び回答の記録が残らず、事務室メンバーの交代時に引継ぎが困難になります。少なくとも、新人がアクセスできる過去メールの書庫が必要です。第2は、メール以外のコミュニケーション手段と併用する場合の利便性です。タスク管理システムやチャット、Wikiなどのメール以外のコミュニケーション手段を併用している場合、そのコミュニケーション手段から個別のメールを参照するには、個別のメールに対してURLが発行されている(言い換えれば、ウェブ上に書庫が存在している)必要があります。この2つの理由により、窓口アドレスを Mailman を用いたメーリングリストとして実装していますが、当然ながら、部門外の所属組織構成員だけでなく、所属組織外の関係者からもメールを受け付ける必要があります。
Mailman でメーリングリストを運用している時、非会員によって投稿されたメールを受理・配送するには、(1) generic_nonmember_action オプションを「承認」に設定する、または、(2) accept_these_nonmembers オプションに適当な正規表現を設定する、などの選択肢があります。しかし、前者(1)はあらゆるメールを受け付けますし、後者(2)も詐称が容易なヘッダー From に依存していますから、spam に対して脆弱な設定になってしまいます。
それに対して、所属組織構成員が利用する送信用メールサーバは固定されているという前提が利用できる場合には、特定のメールサーバから投稿されたメールは全て配送するという設定が可能です。しかし、この設定にも2つの問題がありました。第1は、所属組織の送信用メールサーバを経由せずに、Gmail やプロバイダのメールサーバからメールを送信する所属組織構成員が多いという問題です。第2は、所属組織外の関係者からのメールについては、送信用メールサーバが固定されていないため、この設定では原理的に対応できないという問題です。この2つの問題により、メーリングリストサーバ管理者による司会作業が追いつかずに、メールの滞留が多発していました。
提案手法
本稿では、Mailman の司会依頼メールの仕組みを利用して、投稿者自身による確認を組み込むことにより、任意の投稿者・メールサーバからメールを受理・配送する方法を説明します。
具体的には、以下のような挙動を実現します。
- Mailman の司会依頼メールを、アップロードスクリプトを経由して、確認用CGIに転送する。
- 確認用CGIは確認用URLを生成し、投稿者に対して「この確認用URLにアクセスしてください」という確認依頼メールを送信する。
- 指定された確認用URLにアクセスがあると、確認用CGIは、当該メールの配信を依頼するコマンドメールを Mailman に送信する。
確認用URLにアクセスした投稿者のメールアドレスは、データベースに登録されて、30日間は確認なしに配送されるようになります。
設定
必要なスクリプト類は、GitHub に公開しました。まずは、このリポジトリを clone してください。
$ git clone https://github.com/tsuchm/mmpostauth.git
重要なスクリプトは2つだけです。
-
action
: 確認用CGI -
mmpostauth-handler
: 司会依頼メールのアップロードスクリプト
司会用パスワードの設定
まず、Mailman にテスト用メーリングリストを作成し、司会者として自分自身を設定、テストメールをテスト用メーリングリストに送って、司会依頼メールのサンプルを入手してください。
次に、Mailman のウェブインターフェースを使って、テスト用メーリングリストの司会用パスワードを設定して下さい。設定した司会用パスワードは、確認用CGIと同じディレクトリにある設定ファイル mmpostauth.ini
に以下のように書き込んでおいて下さい。
[testlist@lists.example.net]
password = 司会用パスワード
確認用CGIのインストール
まず、mod_fcgid と必要な perl モジュールをインストールしておきます。
$ apt-get install libapache2-mod-fcgid libfcgi-perl libwww-perl libhtml-template-perl libcgi-session-perl libconfig-simple-perl libio-stringy-perl libmail-dkim-perl libdbix-class-perl libdbix-class-timestamp-perl libdbd-sqlite3-perl libdatetime-format-sqlite-perl
最初に、確認用CGIが mod_fcgid 経由で動作していることを確認します。例えば、確認用CGIのURLが、https://www.example.net/mmpostauth/action
であるとします。以後、このURLを基準URLと呼びます。基準URLの末尾に /status
を付け加えた https://www.example.net/mmpostauth/action/status
に、ウェブブラウザでアクセスしてみて、「OK」という出力が得られたならば、確認用CGIは正しく動作しているでしょう。
次に、確認用CGIに対して司会依頼メールをアップロードできるネットワークの範囲を制限しておきます。初期状態では、アップロード用URL(基準URL+/post
)に対するアクセス制限(抜粋)は、以下のようになっています。
SetEnvIf Request_URI "/action/post$" post_action
SetEnvIf Remote_Addr "^192\.168\." !post_action
<Files action>
SetHandler fcgid-script
Order Allow,Deny
Allow from all
Deny from env=post_action
</Files>
この設定は、以下のように振る舞います。
- アップロード用URL(基準URL+
/post
)に対するアクセスは基本的に拒否する。 - ただし、アクセス元のIPアドレスが正規表現(
^192\.168\.
)に一致する場合は、許可する。
司会依頼メールのアップロードスクリプトをインストールするサーバのIPアドレスと一致するように、適切に正規表現を書き換えて下さい。
また、設定ファイルには、秘密を要する情報(司会用パスワードなど)が記入されていますから、適切にアクセス制限しておく必要があります。初期状態は、以下のようになっています。
<FilesMatch "\.(ini|tmpl)$">
Order Deny,Allow
Deny from all
</FilesMatch>
司会依頼メールのアップロードスクリプトのインストール
その上で、司会依頼メールのアップロードスクリプトが正しく動作していることを、以下のコマンドで確認してください。
$ cat サンプル | perl mmpostauth-handler --debug --url https://www.example.net/mmpostauth/action/post
--url
オプションで、アップロード用URL(基準URL+/post
)を指定して下さい。このコマンドが正常に動作すると、以下のように確認依頼メールのヘッダと文面を含む結果が得られるはずです。
HTTP/1.1 200 OK
Connection: Keep-Alive
Date: Fri, 02 Mar 2018 01:25:15 GMT
Server: Apache/2.4.25 (Debian)
Vary: Accept-Encoding
Content-Type: text/plain; charset=UTF-8
From: testlist-owner@lists.example.net
To: alice@example.com
Subject: Your message requires the additional confirmation step
Date: Fri, 02 Mar 2018 01:25:15 GMT
X-Mailer: MIME-tools 5.508 (Entity 5.508)
The additional confirmation step is required, because your message
posted for testlist@lists.example.net is received from an untrusted mail server.
Access the following url:
https://www.example.net/mmpostauth/action/confirm/ランダム文字列
アップロードされた司会依頼メールは、確認用CGIと同じディレクトリの spool/
ディレクトリに貯まるようになっています。確認用CGIの動作権限に留意して、spool/
ディレクトリの所有者・パーミッションを適切に変更して下さい。
最後に、司会依頼メールのアップロードスクリプトが、MTA 経由で呼び出されるように、/etc/aliases
または ~/.forward
を適当に変更して下さい。本稿では、mmpostauth@mail.example.net
宛のメールがアップロードされるものとします。以下は設定例です。
mmpostauth: |"/somewhere/mmpostauth-handler --url https://www.example.net/mailman/action/post"
司会者の追加
- Mailman のウェブインターフェースを使って、テスト用メーリングリストの司会者に
mmpostauth@mail.example.net
を加えて下さい。 - テスト用メーリングリストに、非会員のアドレスからテストメールを投稿し、投稿者自身に確認依頼メールが戻ってくることを確認して下さい。
- 確認依頼メールに記載されている確認用URL(基準URL+
/confirm/
+ランダム文字列)にアクセスします。
アクセス制御
初期状態では、確認用URL(基準URL+/confirm/
+ランダム文字列)に対するアクセス制御は行わない(あらゆるアクセスを受け付ける)設定になっています。この状態でも、確認依頼メールから確認用URLを取り出し、更にアクセスするような高度な対応は spam ボットには到底不可能ですから、spam の投稿は十分に抑止されると期待されます。
ただ、対象のメーリングリストの性質によっては、より厳しいアクセス制御が必要になる場合も考えられます。その場合は、以下の設定を検討して下さい。
reCAPTCHA によるアクセス制御
確認用URL(基準URL+/confirm/
+ランダム文字列)にアクセスしているクライアントが人間であることを、reCAPTCHA によって確認します。この機能を利用する場合は、https://www.google.com/recaptcha/intro/android.html から reCAPTCHA 用のサイト鍵と秘密鍵を取得して、以下のように設定ファイルに記入して下さい。
[testlist@lists.example.net]
password = 司会用パスワード
sitekey = Googleから取得したサイト鍵
secretkey = Googleから取得した秘密鍵
なお、default セクションで設定すると、全てのメーリングリストに対して reCAPTCHA が有効になります。
ウェブサーバ上の認証・認可モジュールによるアクセス制御
Shibboleth 認証や LDAP 認証、Basic 認証など、上位ウェブサーバ上で動作する各種の認証・認可モジュールを用いてアクセス制御することが可能です。最も簡単な方法は、確認用CGI全体を、認証・認可モジュールの管理下に置くことです。
Satisfy Any
SetEnvIf Remote_Addr "^192\.168\." trusted_network
AuthType Shibboleth
ShibRequestSetting requireSession 1
Require valid-user
<Files action>
SetHandler fcgid-script
Order Deny,Allow
Deny from all
Allow from env=trusted_network
</Files>
この設定例は、以下のように振る舞います。
- 確認用CGIに対するアクセスは、基本的に拒否する。
- ただし、Shibboleth モジュールによって認証されている場合は、アクセスを許可する。
- または、信頼できるネットワークからは、アクセスを許可する。
しかし、アクセス制御の必要水準が異なるメーリングリストが混在している場合には、この設定例では対応できません。
アクセス制御の必要水準が異なるメーリングリストが混在している場合には、ログイン用URLのみを、認証・認可モジュールの管理下に置くという設定を行います。この機能を利用する場合は、まず以下のように設定ファイルで login
オプションを設定して下さい。
[testlist@lists.example.net]
password = 司会用パスワード
login = 1
このように設定すると、確認用URL(基準URL+/confirm/
+ランダム文字列)に対するアクセスは一旦、ログイン用URL(基準URL+/login/
+ランダム文字列)にリダイレクトされるようになります。
その上で、ログイン用URL(基準URL+/login/
+ランダム文字列)に対して、ウェブサーバ側で認証・認可を行うように設定します。
Satisfy Any
SetEnvIf Request_URI "/action/login/" login_action
AuthType Shibboleth
ShibRequestSetting requireSession 1
Require valid-user
SetEnvIf Request_URI "/action/post$" post_action
SetEnvIf Remote_Addr "^192\.168\." !post_action
<Files action>
SetHandler fcgid-script
Order Allow,Deny
Allow from all
Deny from env=login_action
Deny from env=post_action
</Files>
この設定例は、以下のように振る舞います。
- ログイン用URL(基準URL+
/login/
+ランダム文字列)に対するアクセスは、基本的に拒否する。 - ただし、Shibboleth モジュールによって認証されている場合は、アクセスを許可する。
- 司会依頼メッセージのアップロード用URL(基準URL+
/post
)に対するアクセスは、信頼できるネットワークのみ許可する。
ログイン用URL(基準URL+/login/
+ランダム文字列)にアクセスがあると、確認用CGIは、環境変数 REMOTE_USER
の値をチェックして、確認用URL(基準URL+/confirm/
+ランダム文字列)に更にリダイレクトします。環境変数 REMOTE_USER
が空の場合には、認証・認可されていないものと見なして、403 エラーページを返します。
文面の調整
実際の運用にあたっては、以下の文面を調整する必要があるでしょう。
-
notify.tmpl
: 確認依頼メール -
confirm.tmpl
: 確認用ウェブページ -
approve.tmpl
: 確認・送信承認後の結果表示ウェブページ -
revoke.tmpl
: 確認・送信取消後の結果表示ウェブページ
確認用URLにアクセスした投稿者のメールアドレスのキャッシュ
確認用URLにアクセスした投稿者のメールアドレスは、データベースに保存され、30日間は確認なしにメールが配送されるようになります。この挙動を変更したい場合は、以下のように設定ファイルで senderperiod
オプションを設定して下さい。値を0にすると「キャッシュせずに、毎回確認する」設定になります。
[default]
senderperiod = 0
古い司会依頼メールの破棄
30日以上経過した司会依頼メールについては、自動的に破棄する設定になっています。この挙動を変更したい場合は、以下のように設定ファイルで expire
オプションを設定して下さい。値を0にすると「破棄しない」設定になります。
[default]
expire = 0