あきネット

フリーソフトウェアやオープンソースをはじめ、コンピューターやインターネットに関するTIPSや話題を扱います

北のまちから南のまちへと素敵な何かを届けます。
それは、六花かもしれないし、ナナカマドの実かもしれないし、雪の下キャベツかもしれません。

Postfix で Let's Encrypt の証明書をちゃんと使う

とても御無沙汰になってしまいました。


普段は使わない Windows 10 のアップデートを久々にしていたら1日かかったりしていました。(パソコンは1台しかないマルチブートなので、アップデートで「数回再起動します」とかいって使えない時間がかなりあったり。)


Postfix のポート番号増設 ( master.cf )


先日書いたように、(【日記】スタードメイン無料サーバーはメール非対応)
Google Compute Engine (GCE) の「インスタンス」(サーバ)からメール送信をするのに、OP25B (Outbound Port 25 Block) がかかっているため、SendGrid のポート2525で送信するようにしました。
しかし、よその業者を無駄に経由するのも厭だし、送信数制限が月ごとにあるため、 SendGrid を使いたくないと思い、
別の VPS で動かしているメールサーバ (Postfix) で25番以外の特殊なポート番号を追加して、そちらを使うように変更しました。


ポート番号の増設はそれほど難しいものではありません。
Postfix の設定ファイルは、はじめはとっつきにくいですが、マニュアルを読めば master.cf のシンタックスは単純なことがわかります。


また、ここにも書いてあります。


16. How can I get Postfix to listen on a port other than 25?


Set the port in an smtpd entry in the master.cf file. You can change the existing smtpd entry or add an additional one depending on what you need.


An entry like the following causes Postfix to listen on port 10025:


10025 inet n - n - - smtpd


行を追加すればよく、行の最初の項目は(smtps や submission のようなサービス名でなく)ポート番号でも指定可能です。


今回は、GCE のインスタンスの外部IPアドレスを $mynetworks に追加したので、
-o smtpd_recipient_restrictions=permit_mynetworks,reject
のオプションだけで、SMTP認証なしの簡単な設定にしました。
送信元を制限してしまえば、パスワード認証がなくたって、よそには使われません。
シンプルです。


Postfix で Let's Encrypt の証明書を使って TLS 接続する


手間取ったのが、 Let's Encrypt の証明書を使って、確実にTLS (旧称 SSL) 通信をする設定です。
今回は、GCE は Debian で、最終的にメールを受け取る VPS は CentOS です。


証明書の取得方法 (Certbot とか)については略。(ACME クライアントによって操作も異なりますし)


GCE (Debian) の /etc/postfix/main.cf の該当箇所


# 送信
# 今回は $relayhost にしか送らないので常に TLS
smtp_tls_security_level = encrypt
smtp_tls_CApath = /etc/ssl/certs
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# 確認のためログをとる
smtp_tls_loglevel = 1

# 受信
# 可能ならば TLS 通信
smtpd_tls_security_level = may
smtpd_tls_CApath = /etc/ssl/certs
# Let's Encrypt の 公開鍵はフルチェイン(中間CA証明書付き)で
smtpd_tls_cert_file = /etc/letsencrypt/live/gce.example.com/fullchain.pem
# サーバの秘密鍵
smtpd_tls_key_file = /etc/letsencrypt/live/gce.example.com/privkey.pem
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
# 証明書の検証を求める
smtpd_tls_ask_ccert = yes
# 確認のためログをとる
smtpd_tls_loglevel = 1


解説

1.(注:混同したり見間違えたりして片方を忘れがち)
Postfix では、送信と受信で、smtp_* と smtpd_* というように設定項目が別々にわかれます。
送信でも受信でも TLS 通信させたいのであれば、それぞれに設定が必要です。
typo でもなんでもなく、別々の設定項目です。
忘れないように整頓して main.cf を書くようにしたほうがよいです。

2.
今回は、送信先になるのは VPS の Postfix 1台だけで、そちらは TLS 通信には対応しているので、送信は常に encrypt します。
こういう用途ではなく送信相手が限定されない一般的なメールサーバならば may が適切です(世の中にはTLS通信しないメールサーバもあるので、TLS通信に失敗するとメールが送れなくて困る)。

3.(注:これを忘れると、下記4に書いた証明書の検証がうまく機能しない)
smtp_tls_CApath と smtpd_tls_CApath に指定しているのは、一般に信頼する認証局の証明書が置いてあるディレクトリです。
Debian では、 /etc/ssl/certs に置いてあります。

/etc/ssl/certs に、Let's Encrypt のCA証明書も保存しておきます

  • ISRG Root X1 (self-signed)
  • Let’s Encrypt Authority X3 (IdenTrust cross-signed)
  • Let’s Encrypt Authority X3 (Signed by ISRG Root X1)
の3種類が現在使われているCA証明書ですが、
このうち最低でも "Let’s Encrypt Authority X3 (IdenTrust cross-signed)" は必要です。この証明書に署名している "IdenTrust DST Root CA X3" は一般にインストール済なので。
とはいえ、私は自信がないので、3種類とも /etc/ssl/certs に保存しました 😁

どういうことかというと、
Let's Encrypt 自身の証明書は、本来は、 Internet Security Research Group が署名しているものです。ところが、この ISRG の証明書は、ウェブブラウザやOSなどによっては、インストールされていないこともあるのです。
そこで、IdenTrust という既存の認証局が署名した証明書も用意されているわけです。



4.(注:この情報が見つかりにくかった)
smtpd_tls_ask_ccert = yes にしないと、通信相手(クライアント)に対して自分の証明書の検証を求めないため、暗号化はされるものの、(TLS証明書の意義のひとつである)なりすまし検知の機能がなくなります

ちなみに、クライアント側のログには
検証されていないときは: Untrusted TLS connection established to ...
検証されたときは: Trusted TLS connection established to ...
記録されます。
なりすましがされていないことが検証されたら、trusted (信頼された)ということになるわけです。
いずれでも、暗号化はされています

5.(注:デフォルトでは、TLSのログだけが載らなくなってしまう
今回は、TLS 通信に成功しているか確認するため、ログもとります。
(loglevel を設定しないと、ログ (/var/log/mail.log) に載りません。)

受信先 (CentOS)の /etc/postfix/main.cf の該当箇所


smtp_tls_security_level = may
smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_tls_loglevel = 1

smtpd_tls_security_level = may
smtpd_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_ask_ccert = yes
smtpd_tls_loglevel = 1

解説

同じ Postfix なので、Debian とは概ね共通ではあります。

1.
よそにもメールを送信するため、smtp_tls_security_level = may です。
ちなみに、smtp_tls_security_level = may は、旧バージョン(2.2)では smtp_use_tls = yes でした
2.3以降でも、 smtp_use_tls = yes と書けば smtp_tls_security_level = may と同じ効果はありますが、smtp_tls_security_level = may に書き換えてしまうべき

smtpd_use_tls と smtpd_tls_security_level の関係も同じ



2.(注:この情報を載せているウェブサイトが特に少なかった)
RHEL/CentOS/Fedora 系は、Debian とは CA 証明書の扱いが異なります
CA証明書は、 Debian系と異なるディレクトリにあります。
しかも、後記の通り独特の設定方法があるので、ディレクトリでなくファイルを指定します。

smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
smtpd_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt


このファイルに対して、例の Let't Encrypt のCA証明書を追記すればよいのですが、
正しい追記のしかたは、

  1. /etc/pki/ca-trust/source/anchors に証明書ファイルを置く
  2. # update-ca-trust の実行 (root 権限)

です。


QUICK HELP 1: To add a certificate in the simple PEM or DER file formats to the list of CAs trusted on the system:


add it as a new file to directory /etc/pki/ca-trust/source/anchors/

run update-ca-trust extract


update-ca-trust で、/etc/pki/tls/certs/ca-bundle.crt が再生成されます。



メールクライアント(メーラ)からの通信は、証明書を検証不能


メールクライアントはメールサーバに、メール送信を依頼するわけですが、
メールクライアントは、認証局が署名した証明書なんて持っていないのが普通です。


これは、ウェブブラウザとおんなじですね。
ウェブサーバは TLS 証明書を認証局から発行してもらいますが、
ウェブブラウザ(クライアント ; user-agent )を使っているときの我々って、証明書を発行してもらったおぼえなんてないですよね😂


つまり、ウェブサーバから見て訪問してくるウェブブラウザも匿名なのと同じで、
メールサーバから見て、メールクライアントは匿名(anonymous)です。


よって、メールサーバのログには、
Anonymous TLS connection established from...
というように載ります。
メールクライアントからの通信では、これが正常です。


相手が誰かわからないわけですが、通信自体は暗号化されています