OpenSMTPD+Dovecotによるメールサーバ構築
概要
はじめに
本日、運用MXのホストを移転しました。 PostfixをOpenSMTPDにしてここ数日その移転調整をしてました(試運転は一月程前から。メールサーバはゆっくり設定しないとまわりに迷惑がかかるので、このペースがいいのです)。
OpenSMTPDは、OpenBSD標準で動いているので、 /etc/mail/smtpd.conf設定と個別ファイルをいくつか書けば動きます。
設定するまえに:予定MXホストのIPチェックをする
利用ホストは、ホスティングサービスで振り当てられたIPとなりますので、とりあえず
https://anonmails.de/dnsbl.php
とか,zen.spamhause.orgとか。
で、そのIPが、ip black listに登録(listed)されてないかどうかだけは確認してから使うようにしましょう。
digで、確かめてもいいです。
dig +short 予定MXのipアドレス.zen.spamhaus.org
これで返りがなければ未掲載。あれば、listedされてるということになります。
いくら、がんばっても、ブラックリスト入りしてればMXホストとして使うリスクが高いです。
下記のように,is not listed になっていると一安心。
SMTPD=OpenSMTPDの特長
設定ファイルのsmpt.confはSMTPの流れ沿って読めます。
暗号設定、受け口、追加処理の有無、誰を通すか最後に何をするか。
TLSや認証付き投稿はコードに取り込まれていて、それ以外の検査や署名、商業的な追加要件はfilterとして設定ファイル外に出というかたちになってますので、短い設定ファイルになります。 下の例も実質21行で済んでいます。
Postfixと比べると、設定に商用ポリシーや組織ルールを積み上げません。同じ目的でも設定が短く書け、役割の境界が崩れにくいという感じです。
smtpd.conf
まず、使っている設定ファイルを示します ( \ は見やすいように改行しているだけですので、行としては一行です)。
そのあとで、各パートにわけて説明を加えます。
smtpd.confのソース
#$OpenBSD: smtpd.conf,v 1.14 2019/11/26 20:14:38 gilles Exp $
# This is the smtpd server system-wide configuration file.
# See smtpd.conf(5) for more information.
#####名前づけマクロ#####
mxhost = "MXホスト名"
######table#######
table aliases file:/etc/mail/aliases
table vdomains file:/etc/mail/vdomains
table vusers file:/etc/mail/vusers
table credentials file:/etc/mail/credentials
table reject_myself file:/etc/mail/reject_myself
######TLS設定###########
pki $mxhost cert "/etc/letsencrypt/live/MXホスト名/fullchain.pem"
pki $mxhost key "/etc/letsencrypt/live/MXホスト名/privkey.pem"
######フィルター:dkimsignによるDKIM設定####
# 署名対象とするドメインを -d で必要な分だけすべて列挙
filter "dkimsign" proc-exec "/usr/local/libexec/smtpd/filter-dkimsign \
-d 利用ドメイン名...(以下列挙略) \
-s mail -k /etc/mail/dkim/private.rsa.key" user _dkimsign
###### listen部 ########
#dkimsignを使わないならこちらをコメントアウト
#listen on socket
#listen on lo0
#dkimsignを使うばあい
listen on socket filter "dkimsign"
listen on lo0 filter "dkimsign"
# 外部からの受信(25)
listen on egress tls pki $mxhost
# クライアント送信(587) 認証必須
listen on egress port 587 tls-require pki $mxhost auth <credentials> \
filter "dkimsign"
##### action部 #########
# 仮想ドメイン宛メールスプール: Maildir(Dovecotが読む場所でもある)
action "virtual_in" \
maildir "/var/spool/virtual/%{rcpt.domain}/%{rcpt.user}/Maildir" \
user vmail virtual <vusers>
# システムユーザー宛(root/cron など)→ 従来の mboxフォーマット
action "system_mbox" mbox alias <aliases>
# 外向き送信
action "outbound" relay
##### rules設定 ########
# 1) 未認証で自ドメインを名乗る差出人(バウンス型SPAM) → 拒否(Postfix reject_myself 相当)
match !auth from any mail-from <reject_myself> for any reject
# 2) 自ドメイン(仮想)宛は Maildir へ
match from any for domain <vdomains> action "virtual_in"
# 3) ホストローカル宛は mbox
match from local for local action "system_mbox"
# 4) 認証済みクライアントの外向き
match auth from any for any action "outbound"
# 5) ホスト自身からの外向き
match from local for any action "outbound"
名前づけマクロとtable
ホスト名変数
mxhost = “foo” などととすれば、$記述が可能。
ただし、 /foo/bar/$mxhost/some/ とはできない。 またダブルコーテーションで"$mxhost"と囲っても変数展開はないので、そのままの文字列となる。
各tableの機能
| table名 | 機能 | 書式 |
|---|---|---|
| aliases | ローカル別名 | root: www |
| vdomains | 仮想ドメイン名を一行ごと記入 | example.com |
| vusers | 仮想ユーザ名を一行ごとに記入 | [email protected][sapce(tab)]vmail(以下、カンマ,で区切れば、転送メールアドレスも記述可能) |
| credentials | ユーザ名[space(tab)]ハッシュ値 | [email protected][space(tab)]$2b$… |
| reject_myself | 外部からの自ドメイン騙りメール(バウンス型SPAM)拒否のため仮想ドメイン名を一行ごとに記入:@付きで記述すると紛れがない。 | @example.com |
*credentialsのユーザ用ハッシュ(パスワード)の作成設定はdovecotのUsersファイルと一緒に説明します
TLS設定
Let’s Encryptのliveリンクをそのままフルパス掲載すればOKです。
フィルター:dkimsignによるDKIM設定
フィルタは、DKIM処理をするために使ってます。 同処理は別ページで記述します。 試運転のときには、このフィルタ設定は後回しにして、単にsocketとlo0をlistenする、ということでいいかと思います。
listenとaction
1. listen on socket filter "dkimsign"
| 語 | 意味 |
|---|---|
listen on |
ここで待つ |
socket |
ローカルの Unix ソケット(/var/run/smtpd.sock 経由の投入) |
filter "dkimsign" |
通るメールに dkimsign フィルタを掛ける |
ローカル投入(mail コマンド等)にも DKIM 署名する、という指定。
2. listen on lo0 filter "dkimsign"
| 語 | 意味 |
|---|---|
lo0 |
ループバック(127.0.0.1 / ::1) |
filter "dkimsign" |
同様に DKIM 署名 |
localhost 経由の投稿にも署名する。
3. listen on egress tls pki $mxhost
| 語 | 意味 |
|---|---|
egress |
外向きインタフェース(グローバル側) |
tls |
STARTTLS を提供(必須ではない) |
pki $mxhost |
使う証明書・鍵のセット(上で定義した pki) |
外部からの受信用。25番ポート。
4. listen on egress port 587 tls-require pki $mxhost auth <credentials> filter "dkimsign"
| 語 | 意味 |
|---|---|
port 587 |
submission ポート |
tls-require |
STARTTLS 必須(平文のまま進めない) |
pki $mxhost |
証明書 |
auth <credentials> |
認証必須。表 credentials で検証 |
filter "dkimsign" |
認証後に出すメールへ DKIM 署名 |
送信:暗号化必須・認証必須・署名あり。
listen 全体の役割分担
| listen | 用途 |
|---|---|
socket + dkimsign |
ローカル投入に署名 |
lo0 + dkimsign |
localhost 投稿に署名 |
egress 25 + tls |
外部からの受信 |
egress 587 + tls-require + auth + dkimsign |
クライアント送信 |
なぜOpenBSDは、<外部からの受信点>なのに、それも<出>口egressと呼ぶのかについては、 インターネット史的に興味深い論点が伏在していると考えてますが、これは別記事に譲るとして、ともかく、ここでは、ingress/egressではなくて、egressで、その経路結節点を総称していると考えてください。
Maildirについて
action “virtual_in” でのmaildirパス指定
"/var/spool/virtual/%{rcpt.domain}/%{rcpt.user}/Maildir"
ですが、これは、お好みで。
当方は、前のホストと一緒にしたかったので、この記述にしてますが、
"/var/spool/vmail/%{rcpt.domain}/%{rcpt.user}"
などでももちろんいいです。
doas mkdir -p /var/spool/vmail
chown vmail:vmail /var/spool/vmail
しておけば、あとは自動につくってくれます。
DovecotのMaildirでもありますのでDovecot側でも同じにすることだけは忘れないように。
rules–match文
機能は、ソースのコメントのままです。
matchは上から評価されるので、下部のほうに既出条件の部分集合条件を入れても、無視されます。
smtpctl主要コマンド
smtpdの状態管理のためのコマンド。smtpctl allで使えるもの一覧が表示できる(インクリメントに確認できるので、ともかく困ったらallを使う)。
| 主要コマンド | 意味 |
|---|---|
encrypt |
認証用パスワードハッシュ生成:例: smtpctl encrypt ‘パスワード’でCRYPTハッシュ生成 |
update table <name> |
ファイル系テーブル再読込:例:smtpctl update table vdomainsで、vdomains設定を反映 |
show queue |
キュー確認 |
show status |
SMTP/MDA/MTA の状態 |
show stats |
統計 |
schedule all |
キューを即時配送 |
pause smtp / resume smtp |
着信の停止・再開 |
pause mta / resume mta |
外向きの停止・再開 |
pause mda / resume mda |
ローカル配送の停止・再開 |
remove <id|all> |
キュー削除 |
trace expand |
alias/virtual/forward 展開の追跡(maillog反映) :切る場合はuntrace expand |
trace rules |
match 判定の追跡(同上):切る場合はuntrace rules |
log verbose / log brief |
詳細ログの入切 |
show hoststats |
直近4時間のドメイン別最終配送状況 |
spf walk |
対象ドメインのspfレコード確認:例:smtpctl spf walk < ドメイン一覧.txt |
パスワード:OpenSMTPDとDovecotの共通性を生かす
パスワードファイル名
本ページでは、OpenSMTPDは、credentials、dovecotはusersとしています。
パスワードハッシュ値bcryptの「思想」
OpenSMTPDの smtpctl encrypt は、OpenBSD標準のbcrypt形式(OpenBSD自設計)を出力します。 Linuxでよく見るSHA系ハッシュを「将来のハードウェアに対応できない」として採用してません。ハッシュの強さだけを競うより、システム全体の設計で守る、という思想です。
OpenSMTPDとDovecotは独立した認証ファイルを持ちますが、中身は、実質一緒です。 両者ともCRYPTを採用してますので(Dovecotは複数方式対応)。
この複数ファイルの存在を「二重化」=複雑化と見るむきもありますが、 両者の認証情報を同一に保つのはスクリプトで同期すれば足ります。
Postfixの場合Dovecot認証(Usersファイル)に委ねる実装がしばしばされますけど、 疎結合にしておくと、障害の影響範囲を限定できるし、問題切り分けが容易になります。
bcryptのプレフィックスについて($2b$ と $2y$)
OpenBSDは $2b$ で、これは「最初から正しかった」側の印です。 Dovecotの $2y$ は、PHP側で見つかったバグを後修正した「互換用の印」です。 当環境では $2b$ に統一してます。
継続資産=Dovecotの$2y$ を使う場合は、プレフィックスだけを書き換えてOpenSMTPDに流用してOK。アルゴリズム自体は同一です。
パスワード発行と相互共有のしかた
OpenSMTPDなら、smtpctl encrypt ‘パスワード’ で、
$2b$10$(以下略)
Dovecotなら、doveadm pw -p ‘パスワード’で、
{CRYPT}$2y$05$(以下略)
が生成されます。
Dovecotのほうが$05$になっていて、<弱い>のはわかりますが、これは、ホスト全体の防御で守られていることを考えれば、気にしなくていいという判断が妥当だと考えています(むしろFW,pf,ログチェックによるIP denyなどに注力したほうがいい)(「部分最適化」よりも「全体のノイズバジェット」重視指向)。
それぞれの「パスワードファイル」の書式は
credentials(OpenSMTPD)ファイル内
[email protected]スペースあるいはタブ区切り
users(Dovecot)ファイル内
[email protected]:(コロン){CRYPT}ハッシュ値
dovecot userファイル内フィールドについて
Dovecotの正式フォーマットでは、次の表にあるように[email protected]:(コロン){CRYPT}ハッシュ値のあとに以下コロンで区切られたフィールドが5つあります。
不要なものもありますし必要なものはDovecotの10-mail.confで設定できますので、users内での記載は不要です。が、、、コロンだけは入れておいて保守の役に立てたいので、当方は、
[email protected]:{CRYPT}ハッシュ値:::::
と、してます。
| フィールド | 使うか | 備考 |
|---|---|---|
| user | 必須 | メールアドレス |
| password | 必須 | {CRYPT}$2b$... |
| uid | 通常不要 | mail_uid でグローバル指定するなら空でOK |
| gid | 通常不要 | 同上(mail_gid) |
| gecos | 不要 | コメント。空でOK |
| home | 通常不要 | mail_location で指定するなら空でOK |
| shell | 不要 | ログインしないので空でOK |
vmailユーザについての補足(記憶違い訂正)
えっとですね(涙)、vmail名ユーザで動作させることを前提にここまで書いてましたが、 考えてみればOpenBSDがデフォルトでそのユーザを持っているわけもなく、このユーザ(グループ)を、わたくしは作成していたはずです。
なので、addser/groupaddコマンドで作成しておいてください。uid,gidはお好みですが、1000にすることが多いようです(なお、uid,gid番号はDovecot設定で必須となります)。作ったら、一応、id vmail で、uid/gidを確認しませう。
IMAPD=Dovecotの設定
Dovecotは、インストールが必要です。
pkg_add dovecot
dovecotユーザ(_dovecot)も自動で作ってくれます。
/etc/dovecotができてますので、とりあえずその中のものは、 mkdir -p /etc/dovecot/original として cp -a /etc/dovecot/* originalで、保存します。
/usr/local/share/examples/dovecot/example-config/ 一式を/etc/dovecotに複製し、次のファイルを編集します。
- dovecot.conf
- conf.d/10-auth.conf
- conf.d/10-mail.conf
- conf.d/10-ssl.conf
dovecot.conf
protocols = imap
!include conf.d/*conf
の二行だけでいいです。
conf.d/10-auth.conf
disable_plaintext_auth = yes
auth_mechanisms = plain login
の二行だけでいいです。
conf.d/10-mail.conf
mail_location = maildir:ここにsmtpdと同じものを書く
inbox = yes
mail_uid = id番号
mail_gid = id番号
mmap_disable = yes
first_valid_uid = id番号
last_valid_uid = id番号
mail_plugin_dir = /usr/local/lib/dovecot
mbox_write_locks = fcntl
uid gidは、id vmailで表示されるもの。
conf.d/10-ssl.conf
ssl = required
ssl_cert = ここにsmtpdと同じものを書く
ssl_key = ここにsmtpdと同じものを書く
以上で、終わります。あっさりです。
あとは、パスワードファイルとして/etc/dovecot/usersを設定/保存してください。
試運転:慎重な運用を
本番予定のホスト名は、使わないでおきましょう。 地味に段階的にすすめるのがいいかと。
- VPSのデフォルトホスト名で証明書取得(さくらVPSだと、ik1-..略など)
- pfを固める(必要なポートだけ開ける)
- 試験用ユーザーを作り、できることはローカルで設定確認(構文・テーブル・Maildir・doveadm auth test)
- ホストを一時的にMXレコードとしてDNS設定
- 外部から認証付きでSMTP(587)とIMAPが通るか確認
- 外部アドレスから試験ユーザー宛てメールを送り受信確認
- 送信はまだしない
pf設定
MX稼働に最小限必要なポート設定。メール系のTCP+DNS/NTPのUDP (外向けインターフェースvio0とする)。
*必要に応じて、80(証明書取得)とかそういうのはありますが、全部省いてます。
pass out on vio0 proto tcp from any to any port = 25 flags S/SA
pass out on vio0 proto udp from any to any port = 53
pass out on vio0 proto udp from any to any port = 123
pass in on vio0 proto tcp from any to any port = 25 flags S/SA
pass in on vio0 proto tcp from any to any port = 587 flags S/SA
pass in on vio0 proto tcp from any to any port = 993 flags S/SA
証明書取得
VPSの標準ホスト名のものをcertbotで取得します。 standaloneで取得するのが楽です。
certbot certonly –standalone -d ホスト名
この時だけはpfで、80ポートを開ける必要があります。
メール利用ドメインのDNSのMXレコードにテストホスト名を記入する
これ忘れるとまず、どうもならない。
dig MX ドメイン名 |grep テストMXホスト名
で確認
DNS逆引き設定を忘れずに
MXホストのDNSの逆引きレコード(PTR)が設定されてないと、メール配送側でチェックが入ることが多いです。かならず設定しておきましょう。
逆引きは、そのIP管理権をもっているホスティング会社のDNS経由で有効になりますので、 そちらのDNSで使うホストの逆引き設定をすませます。(他のDNSを使っていても、PTR設定はIP管理しているホスティング会社のDNS設定で行う必要があります)。
当方が使っているsakura Internetの場合は、逆引きは、一つのIPしか許されてませんので、 テスト時点では、テスト用につかうデフォルトのik1-(以下略)のホスト名で、本番になるばあいは本番のホスト名を設定します。
ローカルテスト
試験用ユーザをつくります。これで、ホスト名とユーザ名があれば、 vdomainsやvusersなどの関連ファイルを作りますし、証明書もとってあるので、 smtpd/imapに必要なファイルがそろいます。
やれることは限られていますが、ローカルでまずテスト。
| 項目 | 内容 | 備考 |
|---|---|---|
| 設定ファイルの文法 | smtpd -n / doveconf -n |
構文エラーがないか |
| テーブルの読み込み | smtpctl update table ... |
vdomains / vusers / credentials が読めるか |
| ローカル投入 | echo test | mail -s test localuser や sendmail |
socket /lo0経由の配送:これはOpenBSDの初期設定を壊してないかのテストです。 |
| Dovecotの認証 | doveadm auth test user@domain |
ハッシュが正しいか |
| IMAPログイン(localhost) | openssl s_client や mail client で 127.0.0.1 |
TLSなし/あり両方ですが、これは、次の |
| キューの動作 | smtpctl show queue / schedule |
滞留・再送の挙動 |
受信テスト
まずは受信テストをします。
外部のアドレスから、テストメールを送信しておいてください。
smtpが動作し、dovecot経由でIMAPアクセスで受信メールが見えるか。
(なんちゃって)送信テスト
まずは、送信テストは、しない、というのが大切です。
やるとすれば、自アドレスあてに、とか、もう一つユーザをつくって、そのユーザあてとかがいいです。
spf,dkim,dmarc設定をしていないドメインで、メジャーどころ(gmailとかicloudとか)あてのメールをテストするのは<危険>です(spamとしてはねられ、IP/ドメイン名の評判判定が落ちることはほぼ必至ですので)。
外部への(本気の)送信テスト
dkim設定もふくめて別ページにて記述します。
OpenSMTPDのdkimフィルタ構築とsfp/dkim/dmarcレコードの設定、をご覧ください。
各種バージョン情報
- OpenBSD 7.8 GENERIC.MP#54 amd64 amd64
- smtpd -h version: OpenSMTPD 7.7.0
- dovecot –version 2.3.21.1 (d492236fa0)