Dovecotのインストール (Courier-IMAPから移行)

IMAP4(&POP3)サーバ用のプログラムとしてここ18年近くCourier-IMAPを使っている。大きなトラブルもなく機能豊富でとにかくよく動いた。ただ、非常に複雑なので、Courier-authlib + saslauthdを使ってカンニング無しにゼロから仕上げろって言われたら構築できる自信が全くない。そんくらい難しい。(難しくなり続けている)
そして、メモリを食って動作が重い。
そこで軽いと言われるDovecotに移りたいなと思いはじめて既に10年を過ぎている。qmailからPostfixに変えたときがDovecot移行の最大のチャンスだったのに手が回らないこともあってその機会を逃してしまったのが悔やまれる。

で、今回、突然に(Raidの玉替えのついでに?)やる気スイッチが入ったのでDovecotを入れてみた。

インストール

以下、インストールはFreeBSDのportsとする。

# cd /usr/ports/mail/dovecot
# make install

Dovecot Portsコンフィグ
コンフィグで選択するオプションはKQUEUEとMYSQLとICUとGSSAPI_NONE。
MYSQLはメールアカウントがバーチャルドメインユーザーでPostfixAdminなどで管理している場合。UNIXアカウントだけなら不要。
ドキュメントや設定ファイルのサンプルも欲しければ入れる。

Dovecotの設定

以下のDovecotの設定はportsで入るサンプルの構成を基にした。

/usr/local/etc/dovecot.conf
1
2
3
4
protocols = imap pop3 
listen = *, ::
!include conf.d/*.conf
!include_try local.conf
/usr/local/etc/dovecot/conf.d/10-auth.conf
1
2
3
disable_plaintext_auth = no
auth_mechanisms = plain login
!include auth-sql.conf.ext
/usr/local/etc/dovecot/conf.d/10-director.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
service director {
  unix_listener login/director {
    #mode = 0666
  }
  fifo_listener login/proxy-notify {
    #mode = 0666
  }
  unix_listener director-userdb {
    #mode = 0600
  }
  inet_listener {
    #port = 
  }
}
service imap-login {
  #executable = imap-login director
}
service pop3-login {
  #executable = pop3-login director
}
/usr/local/etc/dovecot/conf.d/10-logging.conf
1
2
3
log_path = /var/log/dovecot.log
log_timestamp = "%Y-%m-%d %H:%M:%S "
login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c
/usr/local/etc/dovecot/conf.d/10-mail.conf
1
2
3
4
5
6
7
8
mail_location = maildir:/mail/%d/%u
namespace inbox {
  separator = .
  prefix = INBOX.
  inbox = yes
}
mail_uid = 5000
mail_gid = 5000

Courier-IMAPから移行で迷うのがここ。

既存のMaildirの構成を以下とする。
/mail/example.com/foobar@example.com
.DRAFT  #サブディレクトリ下にcur,newなどがある
.Drafts #サブディレクトリ下にcur,newなどがある
.Keep   #サブディレクトリ下にcur,newなどがある
.Sent   #サブディレクトリ下にcur,newなどがある
.Trash  #サブディレクトリ下にcur,newなどがある
cur #メールボックスの既読用ディレクトリ
new #メールボックスの未読用ディレクトリ
tmp

mail_location = maildir:/mail/%d/%uを指定するとメールの保管場所は/mail/example.com/foobar@example.comのようになる。(%uはユーザー名、%dはドメイン名)
prefixは普通と逆の働きをするっぽい。 「prefix = INBOX.」とせずに「prefix = 」(つまり未指定)とすると例えばメールボックス内にtestという名前でサブフォルダを作成すると.testではなくINBOX.testという名前でサブディレクトリが作成される。逆に.testフォルダを見たくてもINBOX.testが無いので見れないことになる筈。
prefix = INBOX.にするとフォルダ名の先頭にINBOXが無い状態で処理してくれる。
既存のディレクトリ構成が違うならそれに合わせる。
mail_uidとmail_gidはバーチャルドメインユーザーのMaildirの読み書きを行うためにID 5000でvmailというシステムユーザーとID 5000でvmailというグループを作成しているとする。

/usr/local/etc/dovecot/conf.d/10-master.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

service pop3-login {
  inet_listener pop3 {
    port = 110
  }
  inet_listener pop3s {
    port = 995
    ssl = yes
  }
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
  }
}

service dict {
  unix_listener dict {
    mode = 0600
    user = vmail
    group = vmail
  }
}

暗号通信無しのPOP3,IMAP4は利用不可にしたい場合はinet_listener pop3, inet_listener imapの中をそれぞれ port = 0にする。

/usr/local/etc/dovecot/conf.d/10-ssl.conf
1
2
3
4
5
6
7
ssl = yes
ssl_cert = </usr/local/etc/dovecot/ssl/server.crt
ssl_key = </usr/local/etc/dovecot/ssl/server.key
ssl_ca = </usr/local/etc/dovecot/ssl/intermediate.crt
ssl_dh_parameters_length = 2048
ssl_protocols = !SSLv3
ssl_cipher_list = HIGH:AES128:!DHE:!3DES:!aNULL

証明書のPathの直前に<が付く独特な書き方なので注意。
上の例では暗号スイートの指定(ssl_cipher_list)はだいぶ緩めなので利用者が意識高い系の人ばかりならもっと厳選するのもありだが、やりすぎない。
Diffie-Hellman鍵長は2048を指定しているが好みで。でも今時は2048が妥当かと。

/usr/local/etc/dovecot/conf.d/15-mailboxes.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace inbox {
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Trash {
    special_use = \Trash
  }
  mailbox Sent {
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
}
/usr/local/etc/dovecot/conf.d/20-imap.conf
1
2
3
protocol imap {
  mail_max_userip_connections = 15
}
/usr/local/etc/dovecot/conf.d/20-pop3.conf
1
2
3
protocol pop3 {
  mail_max_userip_connections = 5
}
/usr/local/etc/dovecot/conf.d/auth-sql.conf.ext
1
2
3
4
5
6
7
passdb {
  driver = sql
  args = /usr/local/etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = prefetch
}
既存のmailbox(ユーザー情報)テーブル
| username           | password     | maildir                         | active |
| foobar@example.com | $1$000000000 | example.com/foobar@example.com/ |      1 |
/usr/local/etc/dovecot/dovecot-sql.conf.ext
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
driver = mysql 
connect = host=localhost dbname=postfix user=postfix password=postfixpasswd
default_pass_scheme = MD5
password_query = \
  SELECT username AS user, password, \
  concat('/mail/', maildir) AS userdb_home, \
  5000 AS userdb_uid, 5000 AS userdb_gid \
  FROM mailbox WHERE username = '%u' AND active = 1

userdb_warning_disable=yes

作成するのはパスワードクエリーのみ。1つ前のauth-sql.conf.extでuserdb = prefetchにしているのでユーザークエリーは不要。作ってもパスワードクエリと大差無い(同じテーブル参照)のに2回DBにアクセスすることになるので無駄。

PostfixAdmin用に作ったデータベースに適合するようにフィールド名をa AS bで書いた。ユーザー名用のフィールド名は既存がusernameでdovecot標準で求められるのがuserなのでusername AS userとなる。メールの保存場所は既存が/mail/DOMAIN名/ユーザー名となっているがDBに保存されているmaildirフィールドにはドメイン名/ユーザー名として記録されているのでconcat('/mail/', maildir)とすることで正しいPathが取得できる。また、それがDovecot標準で求められるフィールド名userdb_homeの値となる。
FROM mailboxはユーザー情報の格納されているテーブル。WHERE username = '%u'はusernameフィールドから%u(検索するユーザー名)で検索するということ。
AND active = 1は直前のユーザー名かつactive = 1を検索する。activeフィールドはユーザーが有効か無効かを示すフィールドとして用意しているもの。存在しなければAND active = 1の部分は不要。

Postfixの設定変更

/usr/local/etc/postfix/main.cf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
cyrus_sasl_config_path = /usr/local/lib/sasl2   #削除

smtpd_sasl_type = dovecot                       #追加
smtpd_sasl_path = private/auth                  #追加
smtpd_sasl_auth_enable = yes                    #既にある筈,残す
#使用中の他のsmtpd_sasl_hogeも残す

smtpd_recipient_restrictions =                  #残す
    permit_mynetworks,                          #sample
    permit_auth_destination,                    #sample
    permit_sasl_authenticated,             #残す
    reject_unauth_pipelining,                   #sample
    reject_unauth_destination,                  #sample
    reject_unknown_recipient_domain             #sample
    reject_unverified_recipient,                #sample
    reject_multi_recipient_bounce,              #sample
    permit                                      #sample
/usr/local/etc/postfix/master.cf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
submission inet n       -       n       -       -       smtpd
    #中略
    -o smtpd_sasl_auth_enable=yes  #既にある筈,残す
    -o smtpd_client_restrictions=permit_sasl_authenticated,reject
    #中略  
smtps     inet  n       -       n       -       -       smtpd
    #中略
    -o smtpd_sasl_auth_enable=yes  #既にある筈,残す
    -o smtpd_client_restrictions=permit_sasl_authenticated,reject #既にある筈,残す
    #中略

Dovecotを動かす

これまで動いていたCourier-IMAPのpop3, pop3s, imap, imap-ssl, Courier-authlib, saslauthdを停止する。

# service courier-imap-imapd stop
# service courier-imap-imapd-ssl stop
# service courier-imap-pop3d stop
# service courier-imap-pop3d-ssl stop
# service courier-authdaemond stop
# service saslauthd stop
Postfixを再起動する。

# service postfix restart
/etc/rc.conf
1
2
3
4
5
6
7
8
9
saslauthd_enable="YES"               #削除
saslauthd_flags=${saslauthd_flags:-"-a rimap -r -O 127.0.0.1"}   #削除
courier_authdaemond_enable="YES"     #削除
courier_imap_imapd_enable="YES"      #削除
courier_imap_imapd_ssl_enable="YES"  #削除
courier_imap_pop3d_enable="YES"      #削除
courier_imap_pop3d_ssl_enable="YES"  #削除

dovecot_enable="YES"                 #追加
Dovecotを起動する。
# service dovecot start

メールボックスの情報の移行

以上でDovecotが動く筈なのでPostfixのログやDovecotのログ(上の設定例では/var/log/dovecot.log)を見てエラーになってないことやおかしな動作をしていないことを確認。

これだけだと利用者には既存のメールやサブフォルダが消えて真っさらなメールボックスに見えてしまう(IMAP4の場合)、または溜めてた既読メールが全て新しいメールとしてダウンロードされる(POP3の場合)。
ユーザーのメールボックス内にあるCourier-IMAPのメール情報をDovecot用に書き換る必要がある。
http://www.dovecot.org/tools/courier-dovecot-migrate.plをダウンロードして利用する。

# perl ./courier-dovecot-migrate.pl --to-dovecot --convert --recursive ユーザーのMaildir

これでサブフォルダ内も自動的に変換してくれる筈。
上手く行かなかった場合は--recursiveを外して(サブフォルダは変換しないで)やり直すとか --overwriteを付けて上書きモードで再変換するとか。
Courier-IMAP用のメール情報ファイルはそのまま残るのでDovecotへの移行が全て問題無く完了したら手動で消す。

移行後の感想

Courier-IMAP(+Courier-authlib+saslauthd)が恐ろしく複雑だったのでDovecotへの移行も大変かと恐れてなかなか重い腰が上がらなかったのだけど、意外と簡単でサクッと動いた。
取り敢えずDovecot(殆ど初期値)のメモリの使用量がCourier-IMAP(調整済み)利用時の半分になっている。細かい調整は様子を見ながら今後のんびり行う予定。
期待していた動作の軽さはそれほど実感できていない。調整すると軽くなるのかしら。

関連記事: