主頁 | 自己紹介 | 日記 | 農業 | 台所 | 電算機 | | 本棚 | | Git

メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot

はじめに

少し前にさくらのVPSで独自ドメインのメールサーバーを構築した。当時はドキュメントを追うのに必死で構築手順を整理できていなかった。しかしスマホを使わない僕の数少い連絡手段なので、受信できなくなると困る。そこであらためてサーバーを構築し、その手順を確認することにした。あいかわらずDNSの知識は自信がない。セキュリティは自己責任で。

使うもの

メールの仕組み

メールの送受信にはSMTPというプロトコルが使われ、この通信を担うのがSMTPサーバーと呼ばれるソフトウェアである。このサーバーはメールの転送を担うので、MTA(Mail Transfer Agent)とも呼ばれる。今回はMTAとしてOpenSMTPDというのを使う。手元のパソコン等で書いたメールは、SMTPの通信で送信側のMTAに送られ、そこから受信側のMTAに転送される。受信側のMTAでは届いたメールをユーザーごとにふりわけ、所定の場所に保存する。

受信側のユーザーは、手元のパソコン等から受信側のサーバーに繋ぎ、自分宛てのメールを確認する。この時に使われるのがIMAPというプロトコルである(POPというのもあるが今回は割愛)。IMAPによるこの通信を担うのがIMAPサーバーで、メールを配達する役割を担うことから、MDA(Mail Delivery Agent)とも呼ばれる。今回MDAとしてDovecotを使用する。

以上をまとめると以下のようになる:

+------------+        +----------------+
|MTA(mtkn.jp)|--SMTP->|      MTA       |
+------------+        +--example.com---+
      ^               |      MDA       |
      |               +----------------+
      |                       |
     SMTP                    IMAP
      |                       |
      |                       v
+------------+        +----------------+
|user@mtkn.jp|        |user@example.com|
+------------+        +----------------+

  1. メールを書く。
  2. 送信側のMTAに送る。
  3. 受信側のMTAに転送する。
  4. 受信者が受信側のMDAに新着メールの有無を確認する。

SMTPサーバーどうしが通信する際、スパムメールをフィルタリングする為、いくつかの方法で送信元の本人確認を行う。その際第三者としてDNS及び、サーバーのIPアドレスを管理している人(今回の場合はさくらインターネット)が登場する。DNSにはドメインをIPアドレスにひもづける情報が登録できる。またIPアドレスの管理人には、自分が借りているIPアドレスを自分のドメインにひもづける情報を登録してもらう。これらによって、メールの送信者がドメイン、IPアドレス双方から確認できるので、なりすましを防止できる仕組みである。

最近はgmail等の大手がスパム対策を強化する一環として、以下に述べるような本人確認をすべて通らなければ受信しないようにしているらしい。

MTAどうしのやりとり

以下、MTAどうしの通信内容をざっくり説明する。

Aレコード、MXレコードによる送信先の特定

MTAがメールを送信する際、まずは宛先のサーバーを特定する必要がある。この際に使用するのがDNSに登録されているMXレコードである。送信元のMTAはまずDNSに宛先のドメインのMXレコードを問い合わせる。するとメールの送信先のドメイン名が分かるので、続いてこの送信先のIPアドレスを問い合わせる:

MTA (mtkn.jp sss.sss.sss.sss)                 DNS

    example.com宛のメールって
    誰に送ったらええん?
    ---------------------------------------->

                 mai.example.comやで(MXレコード)
    <----------------------------------------

    mail.example.comってどこや?
    ---------------------------------------->

               ddd.ddd.ddd.dddやで(Aレコード)
    <----------------------------------------

次にこの宛先のMTAとの通信を開始する。しかし受信側のMTA(example.com)は、mtkn.jpを名乗るサーバーが本当にmtkn.jpかどうか確認する必要がある:

MTA (mtkn.jp sss.sss.sss.sss)                 MTA (example.com ddd.ddd.ddd.ddd)
    こんちは。mtkn.jpです。
    ---------------------------------------->

                  なりすましちゃうか確認したろ

SPFレコードによる送信元の本人確認

受信側のMTAは、自分に連絡してきたサーバーが本人かどうかをDNSに問い合わせる。このときに使うのがSPFレコードである。このレコードにはドメインの所有者が、自分のドメインからメールを送信してもいいIPアドレスを指定する。受信側のMTAは送信元のIPアドレスと、このレコードに記載されているIPアドレスを比べることで、送信側のMTAがドメインの所有者に認められているMTAかどうか判断できる。

DNS                                          MTA (example.com ddd.ddd.ddd.ddd)
                 mtkn.jpを名乗るやつから連絡
                 来てるんやけどこいつ本物?
    <----------------------------------------

    IPアドレスがsss.sss.sss.sss
    やったら多分本物やわ。(SPFレコード)
    ---------------------------------------->

                                     よっしゃ

PTRレコード

次は逆にIPアドレスからドメインを辿って本人確認する。これに使われるのがPTRレコードである。このレコードはIPアドレスの管理人(ここではさくらインターネット)でないと登録できない。しかし、IPアドレスを借りるにあたって、自分の名前やら住所やらを提供しており、PTRレコードをきちんと設定した上でスパムメールを送ると、自分の身元がバレるので、スパムの抑止に繋がる。

DNS                                          MTA (example.com ddd.ddd.ddd.ddd)
	    sss.sss.sss.sssってIPアドレスで
            mtkn.jpってドメイン登録されてる?
    <----------------------------------------

    されてるで。(PTRレコード)
    ---------------------------------------->

                                     よっしゃ

DKIM

送信側MTAが本人であることを証明するため、受信側に送るメールに署名する。署名は公開鍵方式で行うが、この鍵をドメインキーというようである。この署名が付いたメールをDKIM(DomainKey Identified Mail)という。送信側はあらかじめDNSにドメインキーの公開鍵を登録しておき、メール送信時に秘密鍵で署名する:

MTA (mtkn.jp sss.sss.sss.sss)                 MTA (example.com ddd.ddd.ddd.ddd)

                        本人確認できたわ
                        おまたせ。
    <----------------------------------------

    userさんにメール送るで。
    署名も付けるで。
    ---------------------------------------->

受信側はDNSから送信側のドメイン鍵の公開鍵をダウンロードし、署名を確認する:

DNS                                          MTA (example.com ddd.ddd.ddd.ddd)
                 mtkn.jpが署名付きでメール
                 くれたんやけどこの署名本物?
    <----------------------------------------

    これで確認してみて(DKIM公開鍵)
    ---------------------------------------->

                                     よっしゃ

DMARCレコード(上記の本人確認で詐欺だと発覚した場合)

この他DNSにはDMARCレコードというものを登録することができる。これは、以上の本人確認により送信側がにせものであることが確認できた際、受信側にどのような行動を取ってほしいかを記述するものである。これにより例えば、自分を名乗る詐欺師がどこかにメールを送信した場合、受信したサーバーから自分宛てに通報するように頼める(この頼みが聞き入れられるかどうかは多分受信側MTAによる)。

DNS                                          MTA (example.com ddd.ddd.ddd.ddd)
               mtkn.jpを名乗る詐欺師から
               メールきたんやけどどないしょ?
    <----------------------------------------

    ここ(postmaster@mtkn.jp)に通報や。
    (DMARCレコード)
    ---------------------------------------->

                                     よっしゃ

サーバーの設定

VPSの契約、ドメインの取得

VPSとドメインを契約する。今回はVPSをさくらインターネットで、ドメインをムームードメインでそれぞれ契約した。さくらのVPSは好きなISOからOSをインストールでき、管理画面も分かりやすい(他のVPSを知らないので比較はできないが)。ムームードメインは安い。管理画面も悪くはない。以前お名前.comでも借りたことがあるが、こちらは広告のメールがやたら届いてうっとうしかった記憶がある。

OpenBSDのインストール

さくらのVPSにOpenBSDをインストールする。さくらインターネットが用意したISOがあるので、OS再インストールの画面からそれを選べばすぐにインストールきる。でもなんとなく気持悪いのでopenbsd.orgから本家をダウンロードした。

インストールのこまかい手順は割愛するが、ひとつだけひっかかった点があるので書いておく。インストール先のディスクをセットアップし、OSに必要なファイルをインストールメディアからディスクにコピーすると以下のエラーがでてカーネルパニックになった:

wdc_atapi_start: not ready, st = 50
fatal protection fault in supervisor mode
trap type 4 code 0 rip ffffffff810081a9 cs 8 rflags 10282 cr 2 23a896000 cpl 6 rsp ffff80000e9df410
gsbase 0xffffffff81908ff0 kgsbase 0x0
panic: trap type 4, code=0, pc=ffffffff810081a9
syncing disks...18 18 18 18 18 18 18 18

原因はあまり調べていないが、どうもインストールメディアの読み込みに失敗しているようで、インストールに必要なファイルをウェブ上からダウンロードする方法を選択すると問題なくインストールできた。

ファイアーフォールの設定

メールの送受信等に必要なポートを開ける:

以上の設定を/etc/pf.confに設定する:

set skip on lo

block return    # block stateless traffic
pass in log proto tcp from any to any port 22 #ssh

pass out proto udp from any to DNSのIPアドレス port 53 #DNS
pass out proto udp from any to any port 123 #ntp

pass proto icmp #ping
pass proto tcp from any to any port { 80, 443 } #www
pass proto tcp from any to any port { 25, 587, 993 } #mail

pf(4)の設定を反映:

# pfctl -f /etc/pf.conf

DNSの設定

A, MX, SPF, DMARC

DNSのレコードを設定する。ムームーDNSの場合、コントロールパネルにログインし、サイドバーからドメイン管理>ドメイン操作>ムームーDNSと進み、設定するドメインの変更ボタンをクリックすると設定画面がでるので、以下のように設定する:

サブドメイン 種別 内容 優先度
A sss.sss.sss.sss
mail A sss.sss.sss.sss
MX mail.mtkn.jp 10
TXT v=spf1 mx -all
_dmarc TXT v=DMARC1;p=reject;rua=mailto:postmaster@mtkn.jp

ひとつめのAレコードは、ドメイン名(mtkn.jp)にIPアドレス(sss.sss.sss.sss)を紐付けるものである。ふたつめはmail.mtkn.jpのIPアドレスを登録するものである。みっつめのMXレコードは、このドメイン宛てのメール(user@mtkn.jp等)をどこのサーバーに送信するかを決めるものである。上記の設定では、mail.mtkn.jpに送信するようにしている。以上の設定で、user@mtkn.jp宛てのメールは、mail.mtkn.jpというサーバーに送信すればよいことが分かり、さらにこのサーバーのIPアドレスはsss.sss.sss.sssであることも分かる。

次のTXTレコードはSPFレコードである。v=spf1でSPFのバージョンを示し、mxでドメインのMXレコードに登録されたIPアドレスからの送信を許可し、-allでそれ以外のIPアドレスからの送信を禁止している。

最後のものはDMARCレコードである。v=DMARC1でバージョンを示し、p=rejectで、本人確認をクリアしなかったメールについて、その受信者が受信を拒否するように指定している。rua=mailto:postmaster@mtkn.jpでは、本人確認をクリアしなかったメールを受信した場合の通報先を指定している。

PTR

PTRレコードを設定する。さくらのVPSの場合、VPS管理画面にログインし、ネットワーク>ホスト名逆引き登録と進み、カスタムホスト名に自分のドメイン名(mtkn.jp)を入力する。

OpenSMTPDの設定

ユーザーの作成

メールの送受信を担当するユーザーを作成する:

# useradd -m -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail

サーバー証明書の取得

httpd(8)の設定ファイルを作成する。/etc/examplesからサンプルの設定ファイルをコピーして編集する。変更箇所はドメイン名だけでいい。編集後、httpdを起動する。

# cp /etc/examples/httpd.conf /etc/
# vi /etc/httpd.conf
# echo httpd_flags= >> /etc/rc.conf.local
# rcctl start httpd
# rcctl enable httpd

続いてacme-client(1)を使ってLet's Encryptでサーバー証明書を発行する。まずはこちらもサンプルの設定ファイルをコピーしてドメイン名を変更する。その後、acme-client(1)を実行し、さらにcron(8)に登録することで、証明書が自動で更新されるようにする。

# cp /etc/examples/acme-client.conf /etc/
# vi /etc/acme-client.conf
# acme-client -v mail.mtkn.jp
# crontab -e
#minute hour    mday    month   wday    [flags] command
~ 2 * * * acme-client mail.mtkn.jp && rcctl restart smtpd

ドメインキーの作成とDNSへの登録

OpenSMTPD用のDKIMフィルターをインストールする。

# pkg_add opensmtpd-filter-dkimsign

ドメインキーとしてRSAの秘密鍵と公開鍵を作成する。DNSのTXTレコードの制約により、鍵の長さは1024ビットにする。

# openssl genrsa -out /etc/mail/dkim/private.key 1024
# openssl rsa -in /etc/mail/dkim/private.key -pubout -out /etc/mail/dkim/public.key
# chown -r _dkimsign:_dkimsign /etc/mail/dkim
# chmod 0400 /etc/mail/dkim/private.key

作成したドメインキーの公開鍵をDNSに登録する:

サブドメイン 種別 内容 優先度
selector1._domainkey.mail TXT v=DKIM1;k=rsa;p=公開鍵

サブドメインのselector1は以下のsmtpd.confでfilter-dkimsignの-sオプションに 指定してものと同じものであれば他のものでもいい。

smtpd.confの設定

/etc/mail/smtpd.confを以下の通り変更:

# 認証に使用するサーバー証明書と秘密鍵を"mtkn.jp"という名前で登録。
# 証明書のファイルは先程acme-client(1)で取得したもの。
pki mtkn.jp cert "/etc/ssl/mail.mtkn.jp.fullchain.pem"
pki mtkn.jp key "/etc/ssl/private/mail.mtkn.jp.key"

# テーブルの登録
# ローカルに届いたメールの転送を設定するテーブル
table aliases file:/etc/mail/aliases
# ユーザーの名前と暗号化されたパスワードのテーブル
table passwd file:/etc/mail/passwd
# メールの転送を設定するテーブル
table virtuals file:/etc/mail/virtuals

# フィルターの登録
# ドメインキーによる署名を付加するフィルター
filter dkim_sign proc-exec\
	"filter-dkimsign -d mail.mtkn.jp -s selector1\
	-k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign
# DNSの逆引き(PTRレコード)ができなかった場合に受信を拒否するフィルター
filter check_rdns phase connect match !rdns disconnect "550 no rDNS."
# 正引き(Aレコード)と逆引き(PTRレコード)でDNSのレコードが一致しなかった
# 場合に受信を拒否するフィルター
filter check_fcrdns phase connect match !fcrdns disconnect "550 no FCrDNS."

# 各ネットワークインターフェース毎の処理を設定。
listen on socket
listen on lo0
# vio0というインターフェースの25番ポートに届いたものに関する設定。
# 外部からmtkn.jp宛に届いたものの処理である。
# 最初に登録した証明書と秘密鍵を使ってMTAの本人確認をし、
# 上で設定したDNSに関するフィルターを適応。
listen on vio0 port 25 tls pki mtkn.jp hostname "mail.mtkn.jp"\
	filter { check_rdns, check_fcrdns }
# vio0というインターフェースのsubmission(587番)ポートに関する設定。
# mtkn.jpのユーザー(e.g. user@mtkn.jp)から任意の人宛に送信される
# メールに関する設定。
# 上と同様に"mtkn.jp”という名前で登録されたpkiを使ってMTAの本人確認をし、
# "passwd"という名前で登録したパスワードで認証し、
# さらにドメインキーでメールに署名する。
listen on vio0 mask-src port submission smtps pki mtkn.jp\
	hostname "mail.mtkn.jp" auth <passwd> filter dkim_sign

# 各種メールの処理を定義する。
# 各処理が実行される条件はこの後定義する。
# ローカルメールはデフォルトのまま
action "local_mail" mbox alias <aliases>
# 自分のドメイン宛のメールは/var/vmail/以下に振り分けてmaildir形式で保存。
action "domain" maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}"\
	virtual <virtuals>
# 他のドメイン宛ては宛先へ転送。
action "outbound" relay

# どういう条件でどういう処理をするかを定義する。
# 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。
# ここでは/var/vmail/以下に振り分けて保存する。
match from any for domain mtkn.jp action "domain"
# このサーバー内でのメールはlocal_mailとして処理。
match from local for local action "local_mail"
# このサーバー内から他所へのメールは宛先に転送
match from local for any action "outbound"
# 外部から来た他所宛てのメールは認証されているものに限り転送。
# authを忘れると多分このサーバー経由で迷惑メールが送られる危険がある。
match auth from any for any action "outbound"

各テーブルの作成

上記の設定ファイルのtableで指定したテーブルを作成する。/etc/mail/aliasesは特に変更しなくてもいい。/etc/mail/passwdにはメールアカウントと暗号化されたパスワードを保存する。パスワードの暗号化にはsmtpctl(8)を使う:

# echo user@mtkn.jp:$(smtpctl encrypt) >> /etc/mail/passwd
パスワードを入力し、エンター、Ctrl-D

/etc/mail/virtualsにはメールの転送を設定できる。以下のようにする:

abuse@mtkn.jp         user@mtkn.jp
postmaster@mtkn.jp    user@mtkn.jp
webmaster@mtkn.jp     user@mtkn.jp
user@mtkn.jp          vmail

この設定で、例えばabuse@mtkn.jp宛てのメールがuser@mtkn.jpに転送される。abuse, postmaster, webmasterはなんか一応作っとけみたいに書いてたけど必要なんかな?

Dovecotの設定

MDAとしてdovecot(1)をインストールする。

# pkg_add dovecot

dhparamを作成する(時間がかかる):

# openssl dhparam -out /etc/ssl/dh.pem 4096

わかりにくい設定ファイルをどけてシンプルなものに書き直す:

# mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
# vi /etc/dovecot/dovecot.conf

ドキュメントは公式ウェブサイトで確認できる。分量が多すぎて読む気にならない。サンプルの設定ファイルにも各項目の説明と既定値が書いてあるがこれも分かりにくい。パスワードが漏洩するのはとりあえず避けたいので、ssl = requiredと暗号化されていないIMAPをport = 0として無効にする。他はとりあえず以下の設定で動く:

# IPv4は全て監視
listen = *

# SSL接続のみ許可
ssl = required

# SSLに使用するファイル
ssl_cert = </etc/ssl/mail.mtkn.jp.fullchain.pem
ssl_key = </etc/ssl/private/mail.mtkn.jp.key
ssl_dh = </etc/ssl/dh.pem

# メールの保存場所は各ユーザーのホームディレクトリ。
# ホームディレクトリの場所は以下のuserdbで設定。
mail_location = maildir:~

# パスワードのファイル。
# OpenSMTPDと共有。
passdb {
    args = scheme=BLF-CRYPT /etc/mail/passwd
    driver = passwd-file
}

# ユーザーの設定。
# ホームディレクトリは/var/vmail/ドメイン/ユーザー名
userdb {
    args = uid=vmail gid=vmail home=/var/vmail/%d/%n
    driver = static
}

# 使用するプロトコル。
protocols = imap lmtp

# SSLを使ったIMAPSのみを許可。
service imap-login {
  inet_listener imaps {
    port = 993
    ssl = yes
  }
  # Disable imap
  inet_listener imap {
    port = 0
  }
}
# rcctl start dovecot
# rcctl enable dovecot

サーバーの証明書が更新される際にdovecot(1)も再読み込みされるようにcron(8)に追加しておく:

crontab -e
#minute hour    mday    month   wday    [flags] command
~ 2 * * * acme-client mail.mtkn.jp && rcctl restart smtpd && rcctl reload dovecot

テスト

手元のメールクライアントから送受信のテストをする。特にgmail等に送った際に迷惑メールに分類されないかどうか確認する。以上の設定で、gmailへの送受信は問題なかった。また、neomutt(1)thunderbird(1)からログイン及び送受信できることも確認した。

参考文献