これは Postfix Advent Calendar 2014 の15日めの記事です。
ルックアップテーブル
Postfix のルックアップテーブルは Linux だと通常は hash 形式のファイルですが、ファイルの代わりに MySQL, PostgreSQL, LDAP 等を参照することができます。
どの形式が使えるかは postconf -m
コマンドで使用できる形式の一覧を見ることができます。Ubuntu だと次のようになってます。
% postconf -m btree cidr environ fail hash internal memcache nis proxy regexp sdbm socketmap sqlite static tcp texthash unix
Ubuntu では deb で対応形式を追加できるようになっています。
% sudo apt-get install postfix-cdb postfix-ldap postfix-mysql postfix-pcre postfix-pgsql ... % postconf -m btree cdb cidr environ fail hash internal ldap memcache mysql nis pcre pgsql proxy regexp sdbm socketmap sqlite static tcp texthash unix
あとから追加できるのは Ubuntu(Debian?)の独自拡張で、通常はコンパイル時に指定したものしか使えません。 CentOS の場合は mysql はありますが、pgsql(PostgreSQL) はありません。Postfix をコンパイルし直さない限り、あとで追加することもできません。
% postconf -m btree cidr environ fail hash internal ldap memcache mysql nis pcre proxy regexp socketmap static tcp texthash unix
MySQL
ルックアップテーブルは、ある値(キー)を与えるとそれに対応する値を返すテーブルで、*_maps
という名前のパラメータや、smtpd_*_restrictions
に指定するアクセス制御で使用されます。
たとえば alias_maps
パラメータで MySQL を参照したい場合は次のように指定します。
[main.cf]
alias_maps = mysql:/etc/postfix/alias.mysql
/etc/postfix/alias.mysql
ファイルには MySQL 接続用の情報とクエリを記述します。
[alias.mysql]
hosts = mysql_server user = mysql_user password = user_password dbname = some_db query = select result from alias_table where name='%s'
この例では、検索キーが alias_table の name カラムに一致したら result カラムの値を返します。
%s
内の '
や "
はちゃんとエスケープされるので、SQL インジェクションについては気にしなくても大丈夫です。
クエリが複数カラムや複数レコードを返す場合は、,
で結合されて返ります。
my.cnf
Postfix 2.11 から my.cnf や他のファイルを読むことができるようになりました。
option_group = group option_file = /etc/postfix/mysql.cnf
option_file
は my.cnf ファイルの代わりに読み込むファイル名を指定します。
option_group
は cnf ファイル中のグループを指定します。
option_file
も option_group
も指定されない場合は、my.cnf も読み込まれません。
これを使用することで、default-character-set
や init-command
等の MySQL の色んなオプションが指定できるようになります。
charset
Postfix から MySQL への接続の charset は Ubuntu ではデフォルトは latin1 になっています。
試してみます。
[/tmp/hoge.mysql]
hosts = localhost user = test password = abcdefg dbname = test query = show variables like 'character_set_connection'
% postmap -q hoge mysql:/tmp/hoge.mysql character_set_connection,latin1
上述したように Postfix 2.11 では my.cnf を読ませることができるので、my.cnf で default-character-set
を設定すればそれに従います。
[/tmp/hoge.mysql]
hosts = localhost user = test password = abcdefg dbname = test query = show variables like 'character_set_connection' option_group = hoge
[/etc/mysql/my.cnf]
[hoge] default-character-set = utf8mb4
% postmap -q hoge mysql:/tmp/hoge.mysql character_set_connection,utf8mb4
Postfix 2.11 未満では my.cnf を読むことができないので、charset を指定することができません。
接続用の charset がカラムの charset と異なる場合、クエリがエラーになることがあります。
たとえば、ascii のカラムにメールアドレスが格納されている場合、接続が latin1 だと 8bit 文字をクエリに渡すとエラーになります。
[/tmp/virtual_alias.mysql] *1
hosts = 127.0.0.1 user = test password = abcdefg dbname = test query = select address from alias where alias='%s'
[main.cf]
virtual_alias_maps = mysql:/tmp/virtual_alias.mysql
[SMTP]
MAIL FROM:<hoge> 250 2.1.0 Ok RCPT TO:<ほげ> rcpt to:<ほげ@example.com> 451 4.3.0 < @example.com>: Temporary lookup failure
[mail.log]
Dec 15 02:51:38 x220 postfix/smtpd[9860]: connect from localhost[127.0.0.1] Dec 15 02:51:48 x220 postfix/smtpd[9860]: warning: mysql query failed: Illegal mix of collations (ascii_general_ci,IMPLICIT) and (latin1_swedish_ci,COERCIBLE) for operation '=' Dec 15 02:51:48 x220 postfix/smtpd[9860]: warning: mysql:/tmp/virtual_alias.mysql lookup error for "??????@example.com" Dec 15 02:51:48 x220 postfix/smtpd[9860]: NOQUEUE: reject: RCPT from localhost[127.0.0.1]: 451 4.3.0 < @example.com>: Temporary lookup failure; from=<hoge> to=<??????@example.com> proto=SMTP Dec 15 02:51:52 x220 postfix/smtpd[9860]: disconnect from localhost[127.0.0.1]
Postfix はメールアドレスのローカルパートに 8bit 文字があってもエラーにはせず、そのまま処理を行おうとします*2。そのため ascii カラムと latin1 リテラルを比較しようとして MySQL エラーになってしまいます。
クエリを次のように変更すると、リテラルを強制的に ascii とみなすようになるため、MySQL エラーにはなりません。
[/tmp/virtual_alias.mysql]
query = select address from alias where alias=_ascii'%s'
[SMTP]
RCPT TO:<ほげ@example.com> 550 5.1.1 < @example.com>: Recipient address rejected: example.com
[mail.log]
Dec 15 02:53:13 x220 postfix/smtpd[10080]: connect from localhost[127.0.0.1] Dec 15 02:53:21 x220 postfix/smtpd[10080]: NOQUEUE: reject: RCPT from localhost[127.0.0.1]: 550 5.1.1 < @example.com>: Recipient address rejected: example.com; from=<hoge> to=<??????@example.com> proto=SMTP Dec 15 02:53:23 x220 postfix/smtpd[10080]: disconnect from localhost[127.0.0.1]
一旦 Temporary lookup failure のエラーになると、しばらくエラーの状態が記憶されてしまうため、その後のクエリもエラーになってしまいます。
MySQL を使用する場合には注意しましょう。