Dovecotの設定をしゃぶりつくす理由2020

この記事は、GMOペパボエンジニア [Advent Calendar 2020](https://adventar.org/calendars/5420)の10日目の記事です。
9日目はmochikoさんの[エンジニア未経験・異業種からの転職で早くも1年経っていた](https://note.com/maki_m/n/n4c58bf6f9d19)でした。

---

さて、本題です。
私は2020年にDovecotをガツガツ触っていました。

その際に公式のドキュメントを見つつ要件に合う設定を導入していました。
この時、事例をネットで検索したのですがなかなか出てこなくて困っていました。
そこで、今回POP3/IMAP4サーバの[Dovecot](https://www.dovecot.org/)の設定あるあるをできるだけまとめてみることにしました。

## まとめ
- [Dovecot](https://www.dovecot.org/)を最近使っているので情報をまとてみた
- 意図：ネット検索に引っかかる情報は古い情報が多めと感じたので2020年からみた情報を残したい。
 - 設定。動作確認。DB接続。postfixのSASL認証利用。暗号化などなど。

### 役割
- POP3, IMAP4対応のミドルウェア
- 今も更新し続けられているソフトウェアなのでセキュアな要件に見合う機能がある。
- 一昔前だと、この役割に[Courier-IMAP](https://www.courier-mta.org/imap/)も候補に上がっていたが、更新は止まっておりセキュア対応に近年は懸念がある。

### メジャーバージョン差異の注意点
1.xと2.xとに別れるが、今は2.xを使うのが一般的。
ドキュメントの場所が、[1.x](https://wiki1.dovecot.org/)と[2.x](https://doc.dovecot.org/)と違うので注意。Google検索でたどると古い情報にたどり着きやすいので公式の[2.x系のドキュメント]((https://doc.dovecot.org/))を必ず参照すること。

### 2.2 to 2.3アップデート時の警告
2.x系も今は2.4（今は2.3がstable）まであり、[2.2から2.3の差異](https://dovecot.org/download)よりアップデート情報が多いのが注意。
latestを使わない限りは、大体のOSでの標準は2.2系を扱う。
特にSSL周りの警告が増える。

> ssl_protocols setting was replaced by ssl_min_protocol. Now you only specify the minimum ssl protocol version Dovecot accepts, defaulting to TLSv1.

> ssl_parameters was replaced with ssl_dh. See Diffie-Hellman Parameters for SSL.

> SSLv2 is no longer supported as SSL protocol.

公式引用より。このあたりの扱いが変わる。
`ssl_min_protocol`の指定も変わる。まだ警告レベルのエラー。

```
dovecot: doveconf: Warning: Obsolete setting in /etc/dovecot/conf.d/10-ssl.conf:7: ssl_protocols has been replaced by ssl_min_protocol
```

[Diffie-Hellman（DH）](https://doc.dovecot.org/installation_guide/upgrading/from-2.2-to-2.3/#diffie-hellman-parameters-for-ssl)の対応を何も設定していないと以下のエラーが出る。

```
dovecot: doveconf: Warning: NOTE: You can get a new clean config file with: doveconf -Pn > dovecot-new.conf
```

なお、のちに紹介する`doveadm`コマンドで動作確認をしても以下のように警告が促される。

```
dd if=/var/lib/dovecot/ssl-parameters.dat bs=1 skip=88 | openssl dhparam -inform der > /etc/dovecot/dh.pem
```

### 対応OS
[ソースファイル](https://www.dovecot.org/download)はあるが[パッケージのダウンロード](https://repo.dovecot.org/)もある。対応OSはCentOSやUbuntuと広範囲。ここではCentOS7/8より。

```yaml:/etc/yum.repos.d/dovecot.repo
[dovecot-2.3-latest]
name=Dovecot 2.3 CentOS $releasever - $basearch
baseurl=http://repo.dovecot.org/ce-2.3-latest/centos/$releasever/RPMS/$basearch
gpgkey=https://repo.dovecot.org/DOVECOT-REPO-GPG
gpgcheck=1
enabled=1
```
yumrepoからダウンロードしてインストールすることができる。

#### 対象ファイル
対象ファイルは、`dovecot` がメイン。
ユーザ・パスワードをMySQLやPostgreSQLを使う場合は `dovecot-mysql`, `dovecot-pgsql` を使う。
ここではMysqlにユーザ・パスワードが保存されているとして、のちに接続方法を紹介する。

### パスワードのセキュリティ強化の要件に答えることができる
このとき、DBのパスワード暗号化に相当する要件が欲しいとなるので、dovecotのセキュアな対応仕様が活躍する。
[Password Schemes — Dovecot documentation](https://doc.dovecot.org/configuration_manual/authentication/password_schemes/?highlight=default_pass_scheme)
公式にあるように、SHA512-CRYPTなどセキュアなハッシュアルゴリズムを選択可能。

※ Courier-IMAPだと機能はあるが選択できるハッシュアルゴリズムがMD5ぐらいと新しいものに追随できていない。

### imaptest
[dovecot-imaptest](https://github.com/dovecot/imaptest) なるパッケージはimapのパフォーマンステストが可能。
なお、簡易接続テストにはtelnetコマンドもよいが、 `doveadm` コマンドが機能が豊富なので使えるようになると便利。[使用例](https://doc.dovecot.org/admin_manual/sample_tests/)。

## 実設定
pop,imapのサーバを構築することを想定する。
加えてユーザ・パスワードの保存はDB（MySQL）にあるものとする。
その際の特殊設定としてSQLを用意してアクセスするので、その設定を紹介する。

### ファイル群
設定箇所のデフォルトは以下。
主に、 `/etc/dovecot/` にある、dovecot.confがiclude元となり、 `/etc/dovecot/conf.d/`を読み込む設定のON/OFFをする。
ここに二種類の設定がある。主にconfがコメントアウトされているので必要に応じて使い、`ext` はそのconfが必要に応じて読み込む設定となる。


```yaml:dovecotのファイルの一部
/etc/dovecot
/etc/dovecot/conf.d
/etc/dovecot/conf.d/10-auth.conf
/etc/dovecot/conf.d/10-director.conf
/etc/dovecot/conf.d/10-logging.conf
/etc/dovecot/conf.d/10-mail.conf
/etc/dovecot/conf.d/10-master.conf
/etc/dovecot/conf.d/10-ssl.conf
/etc/dovecot/conf.d/15-lda.conf
/etc/dovecot/conf.d/15-mailboxes.conf
/etc/dovecot/conf.d/20-imap.conf
/etc/dovecot/conf.d/20-lmtp.conf
/etc/dovecot/conf.d/20-pop3.conf
/etc/dovecot/conf.d/20-submission.conf
/etc/dovecot/conf.d/90-acl.conf
/etc/dovecot/conf.d/90-plugin.conf
/etc/dovecot/conf.d/90-quota.conf
/etc/dovecot/conf.d/auth-checkpassword.conf.ext
/etc/dovecot/conf.d/auth-deny.conf.ext
/etc/dovecot/conf.d/auth-dict.conf.ext
/etc/dovecot/conf.d/auth-ldap.conf.ext
/etc/dovecot/conf.d/auth-master.conf.ext
/etc/dovecot/conf.d/auth-passwdfile.conf.ext
/etc/dovecot/conf.d/auth-sql.conf.ext
/etc/dovecot/conf.d/auth-static.conf.ext
/etc/dovecot/conf.d/auth-system.conf.ext
/etc/dovecot/conf.d/auth-vpopmail.conf.ext
/etc/dovecot/dovecot.conf
```

### POP3, IMAP4
`/etc/dovecot/conf.d/20-pop3.conf`
`/etc/dovecot/conf.d/20-imap.conf`
この二つのファイルに相当する設定がある。

このとき、 `/etc/dovecot/dovecot.conf` にて使うプロコトルを指定する(デフォルトで起動するが明示的に）。

```yaml:/etc/dovecot/dovecot.conf
protocols = imap pop3
!include conf.d/20-pop3.conf
!include conf.d/20-imap.conf
```

他にもSSLや認証要件にあわせて追加可能。

#### POP3の具体例
コメントアウトを意識せずに必要なものだけを抜き出すとこのような感じ。
dirverはsqlを使用を想定とする。

```yaml:/etc/dovecot/conf.d/20-pop3.conf
protocol pop3 {
  passdb {
    args = /etc/dovecot/conf.d/auth-sql.conf.ext
    driver = sql
    result_failure = return-fail
    result_internalfail = return-fail
    result_success = return-ok
  }
}
```

パスワードの保存をpassdbを使っての保存。argsにある箇所がsqlを指定したextファイルを指定している。
[公式情報のMySQLのクエリ例](https://doc.dovecot.org/configuration_manual/authentication/sql/?highlight=default_pass_scheme#mysql)から一部抜粋だと以下。

```yaml:dovecot-sql.conf
driver = mysql
connect = host=/var/run/mysqld/mysqld.sock dbname=mails user=admin password=pass
#connect = host=localhost dbname=mails user=admin password=pass # port=3306

password_query = SELECT userid AS username, domain, password \
FROM users WHERE userid = '%n' AND domain = '%d'
user_query = SELECT home, uid, gid FROM users WHERE userid = '%n' AND domain = '%d'
```

password_query, user_queryがメインのクエリとなる。
パスワードを参照するクエリを用意。ASを使っているようにDBの名前にあわせてdovecotが必要とする名前に変更する必要がある。
あと、username, domain, passwordとあるが全部で持って認証するのかは要件次第。

なお、デフォルトは平文想定の `default_pass_scheme = PLAIN`。
DBに生パスワードを保存していることが想定だが今時は使わないほうがよい。
そこで、あらかじめ対応したハッシュ強度（MD5, SHA2など）にあわせて設定する。

例えばテーブル側でハッシュ化処理をした場合は以下の様な接続条件を追加する。

```yaml:/etc/dovecot/conf.d/auth-sql.conf.ext
driver = mysql
default_pass_scheme = SHA512-CRYPT
```

今回は `SHA512-CRYPT` としてのは、MySQL側の該当のパスワードをハッシュ保存しているためこの値。
参考：[MySQL :: MySQL 5\.6 リファレンスマニュアル :: 12\.13 暗号化関数と圧縮関数](https://dev.mysql.com/doc/refman/5.6/ja/encryption-functions.html)
`SHA2(str, hash_length)`にあわせて設定する必要がある。

POP3同様にIMAPも似た設定をすることでDBでの認証前提アクセスが用意可能。

#### PROXYで透過的に疎通する方法
公式情報：[Dovecot Proxy — Dovecot documentation](https://doc.dovecot.org/admin_manual/dovecot_proxy/)
このとき、フロントとなるdovecotをproxyとして介し、実際の認証をバックエンドに任せる方法もある。
今回紹介した構成の場合は、直接アクセスするのではなく、クエリ発行時にproxy設定をすればよい。

```extファイルのqueryの構文に足すだけ
password_query = SELECT 'Y' as proxy, 
```
このおまじないのような設定を加えてあげると透過してクエリを発行することができる。
この方法はdovecot1.xのドキュメントのほうが具体例が参考になる。
参考：[HowTo/ImapProxy \- Dovecot Wiki](https://wiki1.dovecot.org/HowTo/ImapProxy)

#### ロードバランサを前段に用意したときのIPアドレスの表示
ロードバランサ配下にdovecotを搭載した場合、ロードバランサあるあるでログに残る送信元IPアドレスがロードバランサのIP固定になることがある。
これを変更して表示したいときは、dovecot側の設定として以下を行う。

```shell
haproxy = yes
haproxy_trusted_networks = xxx.xxx.xxx.xxx/yy
service imap-login {
  inet_listener imap {
    haproxy = yes
  }
  inet_listener imaps {
    haproxy = yes
  }
}
service pop3-login {
  inet_listener pop3 {
    haproxy = yes
  }
  inet_listener pop3s {
    haproxy = yes
  }
}
```

haproxyじゃなくても設定の意味として想定したものとして設定を用意する。
ポイントは、それぞれのserviceごとに設定が必要。
ssl対応なのかどうか。ssl未対応もサポートするのかで設定値が変わる。

なお、このserviceの値はポート番号の指定や変更などにも用いる箇所。

たまに紹介時にあるように、documentが1.xのほうが詳しい情報が残ったままということもある。
[HAProxy \- Dovecot Wiki](https://wiki2.dovecot.org/HAProxy)
今回だとこちらの公式ドキュメントのほうが欲しい情報が書いている。

### SSL
公式情報はこちら：[SSL Configuration — Dovecot documentation](https://doc.dovecot.org/configuration_manual/ssl_configuration/)

```
ssl_cert = </etc/dovecot/dovecot.crt
ssl_key = </etc/dovecot/dovecot.key
```

とあるように、 `<xxx` と書くところがポイント。
なお、SSLv3を使わせたくない。[ciphers](https://www.openssl.org/docs/manmaster/man1/ciphers.html)を絞りたいといった場合は以下。
公式情報参考：[Dovecot SSL configuration — Dovecot documentation](https://doc.dovecot.org/configuration_manual/dovecot_ssl_configuration/)

```
ssl = yes
ssl_cipher_list=ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH
ssl_prefer_server_ciphers = yes
ssl_min_protocol = !SSLv3 !SSLv2
```

[ssl_chiper_list](https://doc.dovecot.org/settings/core/?highlight=ssl_prefer_server_ciphers#ssl-cipher-list)は要件にあわせて。今時のメーラーを意識して対応するとよさそう。
これらの設定を優先さえるために、 [ssl_prefer_server_ciphers](https://doc.dovecot.org/settings/core/?highlight=ssl_prefer_server_ciphers#ssl-prefer-server-ciphers) = `yes` は必須。
`ssl_min_protocol`は2.3のアップデート情報を紹介したとおりで、 `ssl_protocols` で情報を探すと参考事例はみつかる。

[ssl = yes](https://doc.dovecot.org/settings/core/#ssl) はdefaultの値だが or `no` or `required` と特殊。
これはメーラーのSSL対応を想定して設定する必要がある。

### 全体設定
```
default_client_limit = 1000
default_process_limit = 500
log_path = syslog
mail_location = maildir:~/Maildir
```

[default_process_limit](https://doc.dovecot.org/settings/core/?highlight=ssl_prefer_server_ciphers#default-process-limit) が接続数上限となる。
この値を超える接続がある場合は、以下のログがはかれる。


```
dovecot: master: Warning: service(imap-login): process_limit (100) reached, client connections are being dropped
```
なお、デフォルトは100。
また、1024を超える場合は以下のログを気にすること。ulimit周りの設定が必要。

```
dovecot: Warning: fd limit (ulimit -n) is lower than required under max. load (1024 < 2000), because of default_client_limit
```

[mail_location](https://doc.dovecot.org/settings/core/?highlight=ssl_prefer_server_ciphers#mail-location)の指定はMailbox形式かMaildir形式を洗濯したいときに用いる。
dovecotのデフォルトは、mboxである。これは用途による。
リプレースするといった要件の場合は過去踏襲で気にする箇所。

## 稼動確認
実設定後に設定確認や接続確認する方法をまとめる。
telnetもよいが便利なコマンドもあるのでご紹介。

### 設定確認：doveconf
必要な設定はpostfixにおける`postconf` のように `doveconf` を使うこと。
実際に用意した設定が反映されているかはこの結果次第なので比較する。
例えば、先ほどの設定は以下の様に一覧される。

なお、のちに紹介するdoveadmの `doveadm config` でも代替え可能。

```yaml:doveconf一部抜粋
protocol pop3 {
  passdb {
    args = /etc/dovecot/conf.d/auth-sql.conf.ext
    driver = sql
    name =
    result_failure = return-fail
    result_internalfail = return-fail
    result_success = return-ok
  }
}
```

なお、今回はpop3, imap4とpassdbの用意をプロトコルごとに用意したが、
デフォルトで使用するpassdbをグローバルに設定しないと動作しない点が注意。
doveconfより、以下の様にpop, imapを指定しない設定が必要

```yaml:doveconfの一部
passdb {
  args = /etc/dovecot/conf.d/auth-default.ext
  auth_verbose = default
  default_fields =
  deny = no
  driver = sql
  master = no
  mechanisms =
  name =
  override_fields =
  pass = no
  result_failure = return-fail
  result_internalfail = return-fail
  result_success = return-ok
  skip = never
  username_filter =
}
```

```yaml:default-auth-conf
### pop, imapのservice指定ではなくpassdbを直接記載すること。
passdb {
  args = /etc/dovecot/conf.d/auth-default.ext
  driver = sql
  result_failure = return-fail
  result_internalfail = return-fail
  result_success = return-ok
}
```

configでの定義は何もないpassdbを定義することになるが、その設定が必要となる。
プロトコルごとに定義を分ける場合は注意が必要（はまる）。
このように切り分けは設定したconf+extファイルを見るだけでなく、実反映を見極めるとよいです。

### debug
パスワードの中身のレベルで確認したいときや、より詳細に情報を知りたいときは以下の設定をとりあえず書いておけば出したい情報は全て足すことができる。
本番での設定の扱いは注意。あくまでintegration環境を想定。

```
auth_debug = yes
auth_debug_passwords = yes
auth_verbose = yes
mail_debug = yes
verbose_ssl=yes
```

設定したものがうまく動かないときはここのエラーを捕まえるとクエリの結果レベルまで見ることができるので切り分けに便利。

### 動作確認：doveadm
`doveadm`が便利。
いろいろな組み合わせがあるので以下紹介する。

#### doveadm log
[Dovecot Logging — Dovecot documentation](https://doc.dovecot.org/admin_manual/logging/)

```
doveadm log find
doveadm log errors
```

このあたりが便利。

#### doveadm auth login
例えば使用するメールアドレスとパスワードを使って、認証をすることができるのか？といった確認ができる。

```yaml
# doveadm auth login hogehoge@hogehoge.com
Password:
passdb: hogehoge@hogehoge.com auth succeeded
extra fields:
  user=hogehoge@hogehoge.com
userdb extra fields:
  hogehoge@hogehoge.com

  auth_mech=PLAIN
```

失敗すると以下。

```yaml
# doveadm auth login hogehoge@hogehoge.com
Password:
passdb: hogehoge@hogehoge.com auth failed
extra fields:
  user=hogehoge@hogehoge.com
```

これで、存在するメールアドレスがあるのか。パスワードは正しいのか。
また、両方が正しくても疎通ができないのか、アカウントロック処理をアプリケーションで行なっているのかと行った監視対象としても使うことが可能。

#### doveadm service status
`doveadm` コマンドより状態確認が可能。接続数確認が可能。

```yaml:imap-login
$ sudo doveadm service status imap-login
name: imap-login
process_count: 244
process_avail: 0
process_limit: 500
client_limit: 1
throttle_secs: 0
exit_failure_last: 0
exit_failures_in_sec: 0
last_drop_warning: 0
listen_pending: n
listening: y
doveadm_stop: n
process_total: 827
```

```yaml:pop3-login
$ sudo doveadm service status pop3-login
name: pop3-login
process_count: 44
process_avail: 0
process_limit: 500
client_limit: 1
throttle_secs: 0
exit_failure_last: 0
exit_failures_in_sec: 0
last_drop_warning: 0
listen_pending: n
listening: y
doveadm_stop: n
process_total: 1491
```

このように接続数を取得することが可能なのと、あらかじめ設定した　`processs_limit` を確認することができる。
この上限に達するかどうかをスペックとあわせて調整することができる。

また、topコマンドの様な `doveadm stats top` というコマンドもある（バージョンによってコマンドも異なるので実施にコマンドを打ち込んで確認するとよい）。

#### 接続数監視（doveadm応用）
[Mackerel](https://mackerel.io/ja/)などで簡易に上記の値を取得すれば採取可能。
カスタムメトリックをワンライナーで用意できる。
公式マニュアル参考：[ホストのカスタムメトリックを投稿する \- Mackerel ヘルプ](https://mackerel.io/ja/docs/entry/advanced/custom-metrics)

例えば、以下であればlimit数と接続数をグラフ化することができる。

```yaml:/etc/mackerel-agent/mackerel-agent.conf
[plugin.metrics.doveadm_pop3_login_count]
command = "echo -e \"dovecot.doveadm.pop3-login-count\t$(doveadm service status pop3-login | grep process_count | awk '{print $2}')\t$(date +%s)\""

[plugin.metrics.doveadm_pop3_login_limit]
command = "echo -e \"dovecot.doveadm.pop3-login-limit\t$(doveadm service status pop3-login | grep process_limit | awk '{print $2}')\t$(date +%s)\""

[plugin.metrics.doveadm_imap_login_count]
command = "echo -e \"dovecot.doveadm.imap-login-count\t$(doveadm service status imap-login | grep process_count | awk '{print $2}')\t$(date +%s)\""

[plugin.metrics.doveadm_imap_login_limit]
command = "echo -e \"dovecot.doveadm.imap-login-limit\t$(doveadm service status imap-login | grep process_limit | awk '{print $2}')\t$(date +%s)\""
```

---

### postfixのSASL認証にdovecotを使う方法
公式情報：[Postfix SASL How](http://www.postfix-jp.info/trans-2.3/jhtml/SASL_README.html)
postfixのsasl認証の定番は昔は[Cyrus SASL](https://www.cyrusimap.org/sasl/)だったが、今時ならdovecot認証を利用する。
これも、postfixの公式情報の通りで設定例は用意されているのでpostfix側は以下の設定。

```yaml:/etc/postfix/main.cf
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_authenticated_header = yes
smtpd_sasl_type = dovecot

### 以下はdovecotをunix socketに持つか別サーバに接続するかでわかれる
smtpd_sasl_path = private/auth
smtpd_sasl_path = inet:xxx.xxx.xxx:12345
```

`smtpd_sasl_auth_enable = yes` が基本。sasl認証を使う。
加えて、`smtpd_sasl_type = dovecot` と 指定。なお、cyrus-saslを使う場合はここを特に指定しない。

ここで、dovecotでsaslとして接続するにはpostfixが導入されたホストにdovecotをインストールしてunix socketk経由で使うか、dovecotがすでに入っているimap/popサーバに認証を間借りするかで設定が変わる。

#### postfix+dovecot(認証専用）を同居させる場合
この場合はpostfixの設定は

```/etc/postfix/main.cf
smtpd_sasl_path = private/auth
```

あとはdovecotをインストールしたときの設定として認証情報の用意が必要

```yaml:/etc/dovecot/conf.d/10-smtp-auth.conf
service auth {
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    mode = 0666
    user = postfix
  }
}
```

dovecotは起動しつつunix socketを使う設定を用意。
注意点は、デフォルトだとpop/imapサーバを使わないのに起動してしまう点。
デメリットとしてdovecotを管理する必要が出てきます。ただ、障害時は単一の範囲に収まります。

そこで、無理やりlmtp（ポート番号起動がない）を立てるだけ立てる方法もあります。

```:/etc/dovecot/dovecot.conf
protocols = lmtp
```

#### あらかじめ用意したdovecotサーバにsasl認証として接続する
この場合はpostfixの設定は接続先のIPアドレスを指定する必要があります。

```/etc/postfix/main.cf
smtpd_sasl_path = inet:xxx.xxx.xxx:12345
```

このときポート番号はdovecot側で用意する必要があります（例の12345は適当なポートを指定しています）。
この場合は、dovecot側は

```yaml:/etc/dovecot/conf.d/10-smtp-auth.conf
service auth {
  inet_listener {
    port = 12345
  }
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    mode = 0666
    user = postfix
  }
}
```

これで、指定したポート（12345)に対してアクセスするポートの待ち受けサービスが起動する。

話を省略したが、このsmtpのauth認証の場合、
imap/popサーバ構築で用意した様なユーザ・パスワード認証が同様に必要になる。
その際はextファイルを指定すればよい。

```
passdb {
  args = /etc/dovecot/conf.d/smtp-auth.conf.ext
  driver = sql
  result_failure = return-fail
  result_internalfail = return-fail
  result_success = return-ok
}
```

バージョンは2.2系だがpostfixのdovecotを意識したSASL認証はこちらの情報が参考になる。
[postfixadmin/Postfix\-Dovecot\-Postgresql\-Example\.md at master · postfixadmin/postfixadmin](https://github.com/postfixadmin/postfixadmin/blob/master/DOCUMENTS/Postfix-Dovecot-Postgresql-Example.md#dovecot)

---

#### こういう情報が欲しかった！！！
という感想になるといいなの思いです。