Postfix で受信したメールをフックして Slack に通知する

目次

はじめに

先日買ったルーター (ヤマハ RTX810) には、不正アクセスを検知したときにメール通知を行う機能が用意されているのですが、この型番では SMTPS や STARTTLS を使ってメールを送れません。

メールの認証情報を平文でインターネットに流すわけにいかないので、家の中にメールのリレーサーバを立てて送信しようかと思いましたが、どうせならメール通知を Slack 通知に変換するメールサーバを作ることにしました。


Postfix 設定

OS

Ubuntu 18.04 LTS に Postfix をインストールして環境を作りました。
Postfix のインストール時、どのような構成で設定ファイルを作成するかを尋ねられましたが、設定ファイルを作成しない選択肢 (No configuration) を選びました。

main.cf の設定

この Postfix はメールを受け取ってフックスクリプトを実行するだけの機能にするため、main.cf の内容は次の3行だけにしました。

/etc/postfix/main.cf ファイル

1mydestination = localhost.localdomain
2mynetworks = 127.0.0.0/8, [::1]/128, 192.168.1.0/24, [家の IPv6 プレフィックス::]/64
3inet_protocols = all

宛先メールアドレスのドメイン部が "localhost.localdomain" のものだけ受け取るようにしています。

フックスクリプト (単純なサンプル)

Python 3 でフックスクリプト単純なサンプルを作成しました。
メールの MIME がフックスクリプトの標準入力で渡されるので、それを読み取ってパースするだけのサンプルです。

 1import sys
 2import email.parser
 3
 4mime_str = sys.stdin.read()
 5message = email.parser.Parser().parsestr(mime_str)
 6
 7## メールヘッダの取得
 8for key in message.keys():
 9    value = message.get(key)
10
11## メールの本文の取得
12payload = message.get_payload()

フックスクリプト (Slack 通知を行うサンプル)

Slack Incoming Webhooks を用いて、メールのタイトルと本文を Slack に通知するようなサンプルも作成しました。

 1import sys
 2import json
 3import urllib.request
 4import urllib.parse
 5import email.parser
 6
 7mime_str = sys.stdin.read()
 8message = email.parser.Parser().parsestr(mime_str)
 9
10## Slack Incoming Webhooks の URL
11url = "https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXX"
12
13payload = {}
14payload["text"] = message.get("Subject") + "\n" + message.get_payload()
15
16data = json.dumps(payload).encode("utf-8")
17
18request = urllib.request.Request(url, data)
19urllib.request.urlopen(request)
20
21sys.exit(0)

エイリアスの設定

メールを受信したときにスクリプトを実行するよう、エイリアスファイルに次のような行を追加しました。

/etc/aliases

1hook:   |"LC_CTYPE='C.UTF-8' /usr/bin/python3 /etc/postfix/script/hook.py || true"

これで、宛先メールアドレスが "[email protected]" のメールを受け取ると、フックスクリプト "/etc/postfix/script/hook.py" が実行されます。

メールに日本語が含まれていても扱えるようにするため、環境変数 "LC_CTYPE='C.UTF-8'" を付けてスクリプトを実行するようにしています。

また、スクリプトでエラーが発生してもバウンスメールを返さないようにするために、"|| true" を付けてエラーを握りつぶすようにしています。


まとめ

スクリプトの中身やエイリアスなどは少し変えて、実際に家のサーバにも配置し、ルーターのメール通知機能も設定しました。

ただ、ルーターのメール通知機能をテストする良い方法が考えつかずにテストできていないため、実際に不正アクセスを検知したときにちゃんと動いてくれることを祈っています。