Postfixで受け取ったメールをそのまま正規表現+パイプで外部コマンドに渡した場合にエンベロープFrom/Toを取得

Postfixで、受け取ったメールを自作した外部コマンドに処理させたい。もちろん、設定を書けばそういう風にできる。メールの内容(ヘッダやボディ)が標準入力で入ってくるようになる。

でもエンベロープFromやエンベロープToはその標準入力には含まれない。環境変数で渡されてくる。

そんな中で今回は、普通にメールアカウントに対して送られてきたメールだけを処理するのではなく、どんなアドレス宛のメールを受け取ってもすべて外部コマンドに処理させるようにしたい。そういうときは正規表現で受け取るアカウントを設定できるんだけど、それをやると環境変数で渡されるエンベロープFromやToが、自サーバ内で生成したアドレスに書き換わっちゃうことがあるっぽい。

なので、そういうのを回避するやり方を調べたのでメモ。

つまり、

  • あらゆるアドレス宛に送られてきたメールを受け取る
  • 受け取ったメールを外部コマンドに渡して処理させる
  • その外部コマンドでエンベロープFromとエンベロープToを取得する

というのをやる方法ってこと。

/etc/postfix/main.cf
mydestination = $myhostname, localhost.$mydomain, localhost, regexp:/etc/postfix/mydestination.regexp
virtual_alias_maps = regexp:/etc/aliases.regexp
alias_maps = hash:/etc/aliases

これから使う3つのファイルを読み込む設定。

/etc/postfix/mydestination.regexp
/^.*$/ ACCEPT

正規表現で、すべてのドメイン宛てのメールを受け取るようにする。

/etc/aliases.regexp
/^.*$/ all

ここも正規表現で、すべてのアドレス宛に来たメールを「all」アカウントで受け取るようにする。

/etc/aliases

他を全部消して

all: "| /usr/local/bin/mailreceiver >/dev/null 2>&1"

これだけにする。

ここでは、「all」アカウントで受け取ることになったメールをパイプで /usr/local/bin/mailreceiver という外部コマンドで処理させるようにしている。

/usr/local/bin/mailreceiver
#! /usr/bin/ruby

File.open("/var/mails/#{Time.now.to_i}_#{$$}.txt", 'w') do |f|
  f.puts ENV['SENDER']
  f.puts ENV['ORIGINAL_RECIPIENT']
  while str = gets
    f.print str
  end
end

ここで実際に受け取ったメールを処理させる。好きなように書いていいけど、上記ではとりあえず、エポック秒とプロセスIDをファイル名として、そこにメールの内容を書き出すようなRubyスクリプトにしてみた。で、先頭の2行が、エンベロープFromとエンベロープToになるようにしている。

こんな感じで、環境変数のSENDERとORIGINAL_RECIPIENTから取り出せる。他にも環境変数はいろいろあるので、気になる人は全部出力してみるといい。例えばメールを送ってきた接続元のIPとかも取れる。

どうやら一般的にはSENDERとRECIPIENTという環境変数エンベロープFrom/Toが入るらしいけど、自分の環境ではそうはならなかった。冒頭にも書いたように、たぶんmydestinationやaliasに正規表現を使っていると、内部的に転送みたいな扱いになって情報が書き換わってる気がする。例えばSENDERが「owner-(Toのローカル部)@localhost.localdomain」になったり、RECIPIENTが「all@localhost.localdomain」になったりする。

それで、上記のように設定したところ、SENDERとORIGINAL_RECIPIENTでそれぞれ取得できるようになった。

よくわかってない

いろいろ試したらこのやり方でできたっていうだけで、それぞれの設定の意味はよくわかってない。特にvirtual_alias_mapsとalias_mapsの関係がよくわからん。なので、なんかダメなことをやっちゃってるかも知れない。

なんかあったら教えてください。