Dovecot + Postfix catch-all メール受信サーバ構築手順(実ユーザ方式・AlmaLinux 10)

ドメイン example.com 宛のすべてのメールを、OS の実ユーザ test のホームディレクトリ(~/Maildir)で受信し、POP3 / IMAP で取得する構成です。認証は OS のシステムユーザ認証(PAM)を使用します。

本構成では、宛先アドレスごとにメールボックスを用意せず、example.com 宛のすべてのメールを単一のメールボックスで受け取ります。テスト環境では任意のアドレスへの到達確認を手軽に行いたく、アドレスごとに箱を作る手間を省ける catch-all 構成が適しているためです。なお catch-all は宛先を区別せず不特定多数のメールを受信するため、本番運用には向きません。あくまでテスト用途を前提としています。

主な設計判断は次のとおりです。

  • メールアカウントは OS の実ユーザ test とし、保存先はそのホームディレクトリ配下の Maildir とします
  • 認証は Dovecot の passwd-file ではなく、OS のシステムユーザ認証(PAM)を使用します
  • ファイアウォールは firewalld を正とします(AlmaLinux 10 のデフォルトです。iptables は末尾に併記しています)
  • 検証段階では平文認証を許可します。本番公開時は STARTTLS / POP3S の必須化を推奨します

0. 前提パッケージ

dnf install -y dovecot postfix policycoreutils-python-utils

policycoreutils-python-utilssemanage 用です(SELinux の設定で使用します)。


1. メール受信用ユーザを作成します

実ユーザ方式と仮想ユーザ方式の違いを整理します。

方式置き場所メールアカウントSELinux
実ユーザ方式(本構成)ホームディレクトリ配下(/home/test/MaildirOS のユーザと 1 対 1ラベル付与が必要(ホーム配下は標準のメール置き場ではないため)
仮想ユーザ方式/var/vmail など専用Dovecot で管理し OS とは無関係ラベル付与が必要
参考:実ユーザ + 標準スプール/var/spool/mail(mbox形式)OS のユーザと 1 対 1付与不要(標準の置き場所)

/var/spool/mail は最初から SELinux のメール用ラベル(mail_spool_t)が付いている標準の置き場所ですが、mbox 形式(全メールを 1 ファイルに連結する形式)になります。今回は Maildir 形式で受けたいため、ホームディレクトリ配下に Maildir を作成する方式を採ります。この場合、ホーム配下は標準のメール置き場ではないため、SELinux のラベル付与が必要になります(手順 8 で対応します)。

それでは、メール受信用のユーザを作成します。

useradd -m -s /sbin/nologin test

-m でホームディレクトリ(/home/test)を作成し、-s /sbin/nologin でシェルログインを禁止します。POP3 / IMAP の認証には Linux のシェルログインは不要なため、nologin で問題ありません。


2. パスワードを設定します

passwd test

新しいパスワードを 2 回入力します。この操作で /etc/shadow にハッシュ化されたパスワードが保存されます。デフォルトでは SHA-512 でハッシュ化され、/etc/shadow の該当行は $6$ から始まる文字列になります。

先頭の $6$ は、ハッシュがどのアルゴリズムで作られたかを示す識別子です。crypt 形式のハッシュは $ID$ソルト$ハッシュ値 という構造になっており、先頭の番号でアルゴリズムを表します。

識別子アルゴリズム
$1$MD5
$2a$ / $2b$ / $2y$bcrypt
$5$SHA-256
$6$SHA-512

この方式では、手順で別途パスワードファイルを作成する必要はありません。認証は次の手順で OS のシステムユーザ認証(PAM)に委ねます。


3. システムユーザ認証を有効にします

cp -arp /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.org

/etc/dovecot/conf.d/10-auth.conf を編集します。

# 検証用に平文認証を許可します
#disable_plaintext_auth = yes
disable_plaintext_auth = no

# OS のシステムユーザ認証(PAM)を使用します
!include auth-system.conf.ext
#!include auth-passwdfile.conf.ext

auth-system.conf.ext は、Linux のユーザ名・パスワード(/etc/passwd/etc/shadow)を使って認証する設定です。手順 1・2 で作成した test ユーザとそのパスワードが、そのまま POP3 / IMAP のログイン情報になります。


4. SSL 設定を確認します

cp -arp /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl.conf.org

/etc/dovecot/conf.d/10-ssl.conf を編集します。

# SSL を使用しますが必須にはしません(平文 POP3 も許可します)
#ssl = required
ssl = yes

ssl = yes の場合、証明書ファイルが実在しないと起動に失敗します。確認します。

grep -E '^ssl_cert|^ssl_key' /etc/dovecot/conf.d/10-ssl.conf
ls -l /etc/pki/dovecot/certs/dovecot.pem /etc/pki/dovecot/private/dovecot.pem

存在しない場合は、付属スクリプトで自己署名証明書を生成します(パッケージにより有無があります)。

/usr/libexec/dovecot/mkcert.sh 2>/dev/null || \
  echo "証明書を別途用意してください(Let's Encrypt 等)"

このコマンドは、自己署名証明書を生成するスクリプトがあれば実行し、無ければメッセージを表示するという書き方です。mkcert.sh は Dovecot 付属の自己署名証明書生成スクリプトで、実行すると /etc/pki/dovecot/ 配下に証明書と秘密鍵が作られます。2>/dev/null はエラー出力を捨てる指定で、スクリプトが存在しない環境で出るエラーメッセージを画面に表示させないためのものです。|| は「左のコマンドが失敗したら右を実行する」という意味の演算子で、ここではスクリプトが無くて失敗した場合に echo の案内メッセージが表示されます。行末の \ は、長いコマンドを次の行に折り返して書くための継続記号で、実質的には 1 行のコマンドです。

なお、自己署名証明書はメールクライアントなどで警告が表示される検証用の簡易的なものです。テスト環境では自己署名で十分ですが、本番では Let’s Encrypt などの正式な証明書を使用してください。


平文認証と TLS の組み合わせについて

ssl = yesdisable_plaintext_auth = no を組み合わせると、TLS なしの平文接続でも、TLS ありの接続でも、どちらでも認証が通ります。テスト環境で nc localhost 110 を使って手軽に確認したい本構成では、この「どちらでも通る」設定が都合がよいため採用しています。

設定の組み合わせによる認証可否は次のとおりです。

設定の組み合わせ平文接続TLS 接続
ssl = yes + disable_plaintext_auth = no(本構成)通ります通ります
ssl = yes + disable_plaintext_auth = yes弾かれます通ります
ssl = required非 TLS 接続を拒否します通ります

本番運用では、平文接続を塞ぐ disable_plaintext_auth = yes、または非 TLS 接続自体を拒否する ssl = required を選び、パスワードを必ず TLS で保護することを推奨します。


5. メール保存場所を設定します

cp -arp /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf.org

/etc/dovecot/conf.d/10-mail.conf を編集します。

mail_location = maildir:~/Maildir

~ はログインしたユーザ(この場合は test)のホームディレクトリに展開されます。つまり実際の保存先は /home/test/Maildir になります。


6. プロトコルを有効にします

cp -arp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.org

/etc/dovecot/dovecot.conf を編集します。

protocols = imap pop3

POP3 のみで良ければ protocols = pop3 とします。


7. Maildir を作成します

mkdir -p /home/test/Maildir/{new,cur,tmp}
chown -R test:test /home/test/Maildir
chmod -R 700 /home/test/Maildir

8. SELinux を設定します(AlmaLinux 10 で最重要)

ホームディレクトリ配下は、SELinux の標準ではメール配送用の場所として想定されていません。/home/test/Maildir を Postfix・Dovecot が読み書きできるよう、メールスプール用のコンテキストを付与します。

semanage fcontext -a -t mail_spool_t "/home/test/Maildir(/.*)?"
restorecon -Rv /home/test/Maildir

確認します。

ls -Zd /home/test/Maildir
# ... system_u:object_r:mail_spool_t:s0 ... になっていれば問題ありません

トラブルの切り分け時は、一時的に setenforce 0 で配送が通るかを確認すると、原因を特定しやすくなります。 恒久的に enforcing のまま運用するため、上記の fcontext 付与を正とします。


9. Dovecot の設定を確認し、起動します

doveconf -n          # エラーが無いことを確認します
systemctl enable --now dovecot

10. Postfix 側を設定します

ここでは Postfix に「example.com 宛のメールを受け取り、実ユーザ test のホームディレクトリに保存する」よう設定します。Postfix がメールを受け取ってから保存するまでの流れは、おおまかに次のようになります。

外部から 何でも@example.com 宛にメールが届くと、まず virtual(エイリアス表)が宛先を実ユーザ test に書き換えます。実ユーザ宛になったメールは、Postfix の通常のローカル配送機能によって、そのユーザのホームディレクトリ配下の Maildir/ に保存されます。仮想メールボックス方式(vmailboxvirtual_mailbox_maps)は使用しません。実ユーザの /etc/passwd の情報(ホームディレクトリの場所など)がそのまま使われるためです。

catch-all エイリアス(virtual)

virtual は、宛先アドレスを別のアドレスに書き換える(転送する)表です。catch-all を実現する中心がここです。/etc/postfix/virtual に追記します。

@example.com    test

左辺の @example.com はユーザ名を持たないドメイン全体の指定で、「example.com 宛のうち、他の具体的な宛先にマッチしなかったすべて」を意味します。これを右辺の実ユーザ test に書き換えます。これにより、sales@info@taro@ など、どんなユーザ名宛のメールでも、最終的に実ユーザ test の 1 つの箱へ集約されます。これが「任意のアドレスで受信する」catch-all の仕組みです。

編集後、Postfix が高速に検索できるよう、データベース形式に変換します。まず、使用している Postfix がどのデータベース形式に対応しているか確認します。

postconf -m

hash は AlmaLinux 10 の Postfix では標準で対応しておらず、postfix-hash パッケージも提供されていないため使用できません。代わりに、標準で対応している lmdb を使用します(一覧に lmdb が無い場合は、表示された別の形式に読み替えてください)。

lmdbhash(Berkeley DB)の後継として設計されたデータベース形式で、読み込みが高速で、複数プロセスから同時に読んでも安全という特徴があります。Berkeley DB はライセンス変更などの事情もあり、ディストリビューション側で同梱をやめる動きが進んでいます。AlmaLinux 10 で hash が使えないのもこの流れの一つで、新しい環境では lmdb を使うのが標準的な選択です。

postmap lmdb:/etc/postfix/virtual

postmap はテキストの表をデータベースファイル(この場合 virtual.lmdb)に変換するコマンドです。表を書き換えたら毎回実行する必要があります。

main.cf

main.cf は Postfix 全体の設定ファイルです。まずバックアップを取ります。

cp -arp /etc/postfix/main.cf /etc/postfix/main.cf.org

/etc/postfix/main.cf を編集します。

inet_interfaces = all

home_mailbox = Maildir/
virtual_alias_domains = example.com
virtual_alias_maps = lmdb:/etc/postfix/virtual

各設定の意味は次のとおりです。

設定項目意味
inet_interfaces = allPostfix が待ち受けるネットワークインターフェースを全インターフェースにします。デフォルトは localhost のみで、外部からの SMTP 接続を受けられません。外部のメールを受信するため all にします。
home_mailbox = Maildir/ローカル配送(実ユーザ宛の配送)の保存先を、各ユーザのホームディレクトリ配下の Maildir/ にします。これにより test 宛のメールは /home/test/Maildir/ に届きます。
virtual_alias_domains自分のサーバが受け取るべきドメインを宣言します。virtual_alias_maps はあくまで宛先の書き換え表であり、「そのドメイン宛を受け取ってよいか」の判断はこちらの設定で行われます。これが無いと、外部から他ドメイン宛の中継を頼まれたと誤認識され、Relay access denied として拒否されます。
virtual_alias_mapsどのエイリアス表(virtual)を使うかを指定します。lmdb: はデータベース形式の指定で、postmap で作成した virtual.lmdb を読み込みます。catch-all の宛先書き換えがここで有効になります。

virtual_alias_maps を既に他で使用している場合は、上書きにご注意ください。 反映前に postconf virtual_alias_maps で現行値を確認しておくことをおすすめします。

編集したら、設定にミスが無いか確認し、Postfix を再起動して反映します。

postfix check
systemctl restart postfix

postfix check は設定ファイルの文法エラーや、参照先ファイルの不足などを検査するコマンドです。何も表示されなければ問題ありません。


11. ファイアウォールを設定します(firewalld:AlmaLinux 10 デフォルト)

firewall-cmd --permanent --add-service=smtp    # 25
firewall-cmd --permanent --add-port=110/tcp    # POP3
firewall-cmd --permanent --add-port=143/tcp    # IMAP(使用する場合)
firewall-cmd --reload
firewall-cmd --list-all

本番でインターネットに公開する場合は、平文の 110/143 ではなく、 POP3S(995) / IMAPS(993) / Submission(587, STARTTLS) の利用を推奨します。

<details> <summary>iptables-services を使用している場合(firewalld を停止している環境のみ)</summary>

/etc/sysconfig/iptables で、REJECT 行より前に挿入してください。

-A INPUT -p tcp -m state --state NEW -m tcp --dport 25 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 110 -j ACCEPT
# ↑これらを下記の REJECT より「上」に記述します
-A INPUT -j REJECT --reject-with icmp-host-prohibited

-A(末尾追加)のまま REJECT の後ろに置くと ACCEPT が効かないため、ご注意ください。

systemctl restart iptables

</details>


12. 動作を確認します

POP3(ローカル)

nc localhost 110
USER test
PASS 手順2で設定したパスワード
STAT
LIST
QUIT

認証の単体テスト

doveadm auth test test 手順2で設定したパスワード
# passdb: ... auth succeeded と表示されれば問題ありません

外部からテストメールを送信した後

ls -l /home/test/Maildir/new/
# メールファイルが入っていれば配送成功です
tail -f /var/log/maillog   # 配送ログを確認します

トラブル時の確認順序

メールが届かない場合は、まず tail -f /var/log/maillog を確認します。(maildir delivery failed ... Permission denied) のようなログであれば SELinux(手順8)が原因です。setenforce 0 で届く場合は、fcontext の付与漏れが考えられます。

外部から接続できない場合は、ファイアウォール(手順11)と inet_interfaces = all を確認します。

認証に失敗する場合は、doveadm auth test の結果と、passwd test でパスワードが正しく設定されているかを確認します。

Dovecot が起動しない場合は、doveconf -n の出力と、SSL 証明書の実在(手順4)を確認します。

メール送信元に 451 4.3.0 Temporary lookup failure が返る場合は、/var/log/maillogunsupported dictionary type: hash が出ていないか確認します。出ている場合は、main.cfvirtual の参照が hash: のままになっています。手順10のとおり lmdb: に統一し、postmap lmdb:/etc/postfix/virtual を実行してから systemctl restart postfix してください。

メール送信元に 454 4.7.1 Relay access denied が返る場合は、main.cfvirtual_alias_domains が設定されているか確認します。virtual_alias_maps だけでは「受け取るドメインの宣言」にならないため、これが無いと外部からの中継依頼と誤認識されて拒否されます。

コメント