yamlで管理するメーリングリストサーバ

yamlを編集するだけで設定できて、
自由にメールの内容を変更できるメーリングリストサーバが
欲しくなったので作ってみました。
作ったといっても30行もないrubyスクリプトです。

https://github.com/yaasita/ymst

目次

これは何?

こんな感じのyamlを書けばメーリングリストを作れるってやつです

    mltest1@ml.example.com:
      - member1@example.com
      - member2@example.com
      - member3@example.com
    mltest2@ml.example.com:
      - member4@example.com
      - member5@example.com
      - member6@example.com

mltest1@ml.example.comに送れば
member1@example.com
member2@example.com
member3@example.com
に届くという意味です

これだけならaliasesでもできるんですが、yamlの方が個人的にすきなのと、
今後、あて先ごとに名前等の付加情報もつける可能性があるので、yamlにしました。

仕組み

flow

1. メールをpostfixが受けてvirtual_alias_mapsによりymstユーザに集約します。これによりどんなアドレスでもymst@localhostに届くようになります。
2. ymst.shがプログラムを起動します。直接send.rbに送ってもいいのですが、rbenv,rvmとか使いたい人やPATHを変えたい、シェルコマンドを使いたい、テストしやすくしたい等の需要がありそうなので、一個シェルスクリプトを挟みました。
3. send.rbでメールを加工して、yamlに基づいて会員に送信しています。sendmailコマンドに標準出力経由で渡しています。
4. sendmailで渡されたメールをpostfixが処理し、会員のアドレスに配送します。

使い方

サーバ設定はansibleに書いておきました。
設定方法は
READMEに書いたとおりですが
簡単に説明しておきます

以下のファイルを編集してください

* ansible/roles/ymst/files/authorized_keys: 自分の公開鍵にする
* ansible/ansible_hosts: 対象のサーバのアドレス、ユーザ名、パスワード
* ansible/group_vars/all: メーリングリストのドメインを設定する

ansibleについて分る人は適宜環境に合わせて編集してください

実行します

cd ansible
ansible-playbook site.yml

これでサーバは準備できました。

以降はサーバにあるmail.ymlファイルを変更すればOKです。
サーバのリポジトリはgit hookによってworktreeもリセットされるようになっています。
なのでそのままpushすればOKです

git clone ymst@yourserver.eample.com:ymst
cd ymst
vi mail.yml
git add mail.yml && git commit -m "mod ml" && git push

※メーリングリストの中にメーリングリストを入れてもループはしませんが、エラーになります

カスタマイズ

設定ファイルは一切無いので直接rubyのコードを編集して下さいw
とだけ言うのもあれなのでメールからコマンドに渡したときの挙動やら注意点やらを書いておきます

メールをコマンドへ渡す

今回は以下のファイルによって

/home/ymst/.forward

内容は以下の通り

"|/home/ymst/ymst/bin/ymst.sh"

コマンドへ渡す方法はpostfixのmapファイルや/etc/aliasesでも可能です。
このコマンドはメールを同時に受信すれば平行して走る可能性もあります。
なので、一時ファイルを作る場合メールファイルを作る場合はファイル名のバッティングに注意して下さい
ローカル配送をする場合は maildrop等を利用すると便利です

入力の受け取り方

メールの内容は標準入力によって渡されます
例えば以下のようにすると
/tmp/mail.pidでメールが保存されます

#!/bin/bash
cat - > /tmp/mail.$$

作成されるファイルのデフォルトのパーミッションは
600なので他のユーザが見る場合は注意します

ls -l
合計 4
-rw------- 1 ymst ymst 379  8月  1 13:31 mail.5744

環境変数について

以下の環境変数が設定されます
よく使うのはenvelope-fromをあらわすSENDER
envlope-toをあらわすORIGINAL_RECIPIENT
接続してきたクライアントのIPをあらわすCLIENT_ADDRESSくらいでしょうか

USER=ymst
HOME=/home/ymst
DOMAIN=localhost
LOGNAME=ymst
CLIENT_PROTOCOL=SMTP
ORIGINAL_RECIPIENT=mltest2@ml.example.com
LOCAL=ymst
PATH=/usr/bin:/bin
SENDER=from@hoge.example.com
LANG=C
CLIENT_ADDRESS=192.168.33.1
SHELL=/bin/bash
MAIL_CONFIG=/etc/postfix
PWD=/var/spool/postfix
RECIPIENT=ymst@localhost
CLIENT_HOSTNAME=unknown
CLIENT_HELO=sender.bpsinc.jp

終了コードについて

プログラムの終了コードについて
よく使うものだけ紹介します

exit 0

終了コードがゼロなら問題なく配送したとして、処理を終了させます
以下の例だと問題なく受け取るけどそのまま捨てます

送信者はエラーになったことは分りません

exit 69

メールをバウンスさせます。
バウンスなのでエラーだったことを送信元に知らせます。(bounceメール)
後方散乱メールに利用されないように注意してください
(ちなみにexit 1とかexit 2でもこの挙動になります)

無効なアドレスに来たメールをバウンスさせる例

送信者に届くbounceメール

This is the mail system at host ml.example.com.

I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.

For further assistance, please send mail to postmaster.

If you do so, please include this problem report. You can
delete your own text from the attached returned message.

                   The mail system

<ymst@localhost> (expanded from <mltest1@ml.example.com>): service unavailable.
    Command output: address not found

exit 75

defferさせます。
defferは一時的に配送を中止して、キューにためておき、しばらくしたら再送してくれることです。
以下の例はmail.ymlが無い場合に一時的に配送を中止する例

mailqコマンドでみるとキューに溜まっているのが確認できます

-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
3E661140446      240 Fri Aug  1 15:24:22  from@hoge.example.com
                                                           (temporary failure)
                                         ymst@localhost

-- 0 Kbytes in 1 Request.

カスタマイズ例

とりあえず必要になった変更例だけ箇条書きしておきます

enbelove from変更する

sendmail の -f オプションで出来ます

Gmail等の外部送信メールサーバを使用したい

postfixの設定でいけます
詳しくはその他のサイトに解説があると思います

送信するアドレス別にトラッキングコードつける

こんな感じに送信先に応じてURLを変える

http://www.example.com/?q=5ee5c2eecf82f58b0798183da83d6997de5cf363

特定のIP、特定のFromからじゃなければ受け付けない

postfixでもできますが、今回はbashで

smtp auth等はpostfix側でします

特定のヘッダを削除したい

receivedヘッダ等は消しておく

以上です

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

yamasita

東京電機大学工学部→3年間某SIerにて銀行システムの開発→bpsに入社

yamasitaの書いた記事

インフラ
現場で使うansible

2014年12月25日

インフラ
検証環境の作り方

2014年08月14日

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ