OpenSMTPD+Dovecotによるメールサーバ構築

#PCとプログラム覚え

概要

はじめに

本日、運用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 になっていると一安心。

dnsbl check

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を設定/保存してください。

試運転:慎重な運用を

本番予定のホスト名は、使わないでおきましょう。 地味に段階的にすすめるのがいいかと。

  1. VPSのデフォルトホスト名で証明書取得(さくらVPSだと、ik1-..略など)
  2. pfを固める(必要なポートだけ開ける)
  3. 試験用ユーザーを作り、できることはローカルで設定確認(構文・テーブル・Maildir・doveadm auth test)
  4. ホストを一時的にMXレコードとしてDNS設定
  5. 外部から認証付きでSMTP(587)とIMAPが通るか確認
  6. 外部アドレスから試験ユーザー宛てメールを送り受信確認
  7. 送信はまだしない

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)
Popup