TLSの設定

LDAP連携とは関係ないのだが、せっかく検証したので TLS (Transport Layer Security) でメール通信を保護する方法についてもまとめておく。SSLTLS との違いや TLS の概要について知りたければ、Dovecot の下記のドキュメントが簡潔にまとまっている。

Dovecot のTLS設定

なるべくリアルな検証とするため、自前の CA (認証局) を作り、サーバ証明書はその CA によって署名することにした。証明書関係の作業では、一部 OpenSSL も使うが、多少なりとも作業を楽にするため主として GnuTLS ユーティリティ (※) を使用する。CentOS/RHEL では gnutls-utils という名前で標準パッケージがあり、yum 等でインストールできる。certtool コマンドのオプションとテンプレートファイルのフォーマットについては GnuTLS オフィシャルマニュアルの "Invoking certtool" を参照。

証明書関係のファイル配置を下図に示す。サーバ秘密鍵/公開鍵は、DovecotPostfix で共用する。

/etc/pki/
        \_ CA/
             \_ cacert.pem              <- 自前CAの自己署名公開鍵 root:root 444か644
             \_ private/
                     \_ ca.info         <- 自前CA自己署名用テンプレート root:root 600
                        cakey.pem       <- 自前CAの秘密鍵 root:root 400
        \_ tls/
             \_ private/
                     \_ mailcert.info   <- サーバ公開鍵用テンプレート root:root 600
                        mailkey.pem     <- サーバ秘密鍵 root:root 400
             \_ certs/
                     \_ mailcert.pem    <- CA署名済みサーバ公開鍵 root:root 444か644
                     \_ cacert.pem      <- 自前CAの自己署名公開鍵のコピー root:root 444か644

※ GnuTLS は、GNUライセンスでないため他のGNUソフトウェアと一緒には配布しにくいOpenSSLに代わって、新たに書かれGPL及びLGPL下で配布されているSSLライブラリ。GnuTLSユーティリティはOpenSSLを使いやすくするラッパーかと思っていたのだがそうではなかった。

プライベートCAの作成

自前の認証局を作る。もちろん、本物の CA (Verisign等) に署名してもらう場合や既に自前の CA が整備してある場合は不要。

1. CAのプライベートキーの作成

この生成には openssl を使う。現状の certtool では RSAキーの生成に異常に時間がかかるからだ (乱数生成デバイスファイルの扱いに問題があるらしい)。

root# cd /etc/pki/CA/private
root# openssl genrsa -out cakey.pem 1024
root# chmod 400 cakey.pem 

内容を確認したければ、

root# certtool -k --infile cakey.pem
2. 自己署名済みCA証明書の作成

まず、CA証明書(公開鍵)生成のためのテンプレートファイル /etc/pki/CA/private/ca.info を作成する。内容は下記の要領。`serial=xxxx' の指定をしないと証明書のシリアルナンバーが `00' になり、Thunderbird で使う時に文句を言われた。有効期限は極端に長くしておけばいいだろう (例では10年)。

cn="Hoge Net CA"
organization="Hoge Net"
unit="Hoge Net Certificate Authority"
country=JP
expiration_days=3650
serial=1001
ca
cert_signing_key
crl_signing_key

自己署名済みCA証明書を作る。

root# cd /etc/pki/CA/private
root# certtool -s \
      --load-privkey cakey.pem \
      --template ca.info \
      --outfile ../cacert.pem

内容を確認したければ、

root# certtool -i --infile ../cacert.pem

問題がなければ、使いやすいよう cacert.pem/etc/pki/tls/certs/ にコピーしておく。このファイルは公開鍵なので誰でも読めるようにして構わない。

メールサーバ用サーバ証明書の作成

ここで作成する秘密鍵と公開鍵(証明書)は Postfix でも使う。

1. メールサーバ用プライベートキーの作成

Dovecot のソースに付属する mkcert.sh スクリプト(※) を利用する手もあるが、サーバ証明書を CA で署名する場合にはかえって面倒。Dovecot インストール時に生成されたプライベートキー /etc/pki/dovecot/private/dovecot.pem をコピーして利用するか、openssl で単純に生成したほうがいい。生成するなら、

root# cd /etc/pki/tls/private
root# openssl genrsa -out mailkey.pem 1024
root# chmod 400 mailkey.pem

※ CentOS/RHEL 5.x では /usr/share/doc/dovecot-*.*/examples/ にインストールされる。

2. CA署名済みメールサーバ証明書の作成

Dovecot のインストールの際に /etc/pki/dovecot/certs/dovecot.pem が生成されるが、それは自己署名証明書なので使わない。

まず、テンプレートファイル /etc/pki/tls/private/mailcert.info を作成する。証明書タイプ (下記最後の3行) には tls_www_server, encryption_key が必要。signing_key は不明。

cn=mail.hoge.cxm
organization="Hoge Net"
unit="IT Development"
state="Aichi"
locality="Nagoya"
country=JP
expiration_days=730
serial=1001
tls_www_server
encryption_key
signing_key

CA署名済みのサーバ証明書 (公開鍵) を作成する。本物の CA に署名してもらう場合は下記参照(※)。

root# cd /etc/pki/tls/private
root# certtool -c \
      --load-privkey mailkey.pem \
      --load-ca-certificate /etc/pki/CA/cacert.pem \
      --load-ca-privkey /etc/pki/CA/private/cakey.pem \
      --template mailcert.info \
      --outfile ../certs/mailcert.pem

内容を確認するには、

root# certtool -i --infile ../certs/mailcert.pem

※ 本物の CA に署名を依頼する場合は、上記の代わりに CSR を作る。

root# certtool -q \
      --load-privkey mailkey.pem \
      --template mailcert.info \
      --outfile mailcert.csr

内容を確認するには、

root# openssl req -noout -text -in mailcert.csr 

Dovecot の設定変更

下記のパラメータを調整する。Dovecot Wiki の "Dovecot SSL configuration" に解説あり。

#disable_plaintext_auth = yes     <- [補足参照]
 
ssl_disable = no                                    <- SSL/TLS機能を有効にする
ssl_cert_file = /etc/pki/tls/certs/mailcert.pem     <- 公開鍵(証明書)のパス
ssl_key_file = /etc/pki/tls/private/mailkey.pem     <- 秘密鍵のパス
ssl_parameters_regenerate = 0     <- Diffie Hellmanパラメータの定期的な再生成を行わない[補足参照]
ssl_cipher_list = ALL:!LOW:!SSLv2                   <- 暗号強度の規制 [補足参照]

[補足]

disable_plaintext_auth
SSLTLS で通信が保護されていない限り平文認証を許可しない。ただし、クライアントの IPアドレスがサーバのリッスンしている IP と同じだった時、つまりメールサーバ自体の上からアクセスした場合には、平文認証も許可される。規制を掛けたいならアンコメントしていただきたい。
ssl_parameters_regenerate
値は更新周期(H)。機能を無効にするには 0 にする。Diffie Hellman パラメータの再計算には、少々遅いコンピュータだと 30分以上かかることもあるらしい。マニュアルには「再生成したからといって、そう大してセキュリティの上積みになるわけではない」とある。今どきのコンピュータなら、計算には数秒から数十秒しかかからないので、デフォルトの 168 時間 (1週間) でいいだろう。 DHパラメータの保存先は /var/lib/dovecot/ssl-parameters.dat (または ~.ssl)。
ssl_cipher_list
Dovecot 1.0.7 のデフォルトはヌルで、全ての暗号強度を受け入れるようだ。上の例では 1.1以降のデフォルトに合わせている。

設定したら、反映させるために Dovecot デーモンを再起動する。

テスト

準備として、メールサーバの公開ホスト名 (サーバ公開鍵を作る時に cn に指定したもの) が名前解決できるよう、クライアント上の hosts ファイルまたは DNSサーバにレコードを登録しておかなければならない。

Telnetによるテスト
hoshu$ telnet mail.hoge.cxm 110
Escape character is '^]'.
+OK ready
capa
+OK
CAPA
TOP
UIDL
RESP-CODES
PIPELINING
STLS      <- リストの中にこれが出ればOK
USER
SASL PLAIN
.
stls
+OK Begin TLS negotiation now.  <- こういったメッセージが返ってくればOK
quit
quit
OpenSSL s_clientによるテスト

/etc/pki/CA/cacert.pem を一般ユーザで読める場所 (例えば /tmp) へコピーしておいてから、

hoshu$ openssl s_client -starttls pop3 -crlf -CAfile /tmp/cacert.pem \
 -connect mail.hoge.cxm:110
CONNECTED(00000003)
depth=1 /C=JP/O=Hoge Net/OU=Hoge Net Certificate Authority/CN=Hoge Net CA
verify return:1
depth=0 /C=JP/O=Hoge Net/OU=IT Development/L=Nagoya/ST=Aichi/CN=mail.hoge.cxm
verify return:1
---
Certificate chain
 0 s:/C=JP/O=Hoge Net/OU=IT Development/L=Nagoya/ST=Aichi/CN=mail.hoge.cxm
   i:/C=JP/O=Hoge Net/OU=Hoge Net Certificate Authority/CN=Hoge Net CA
---
<- 中略 ->
---
SSL handshake has read 1523 bytes and written 354 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: **********************
    Session-ID-ctx: 
    Master-Key: *******************************
    Key-Arg   : None
    Krb5 Principal: None
    Start Time: 1303996441
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
+OK ready
user ******
+OK
pass ******
+OK Logged in.
quit
+OK Logging out.
read:errno=0

あとは、実際のメールクライアントで試験。メールサーバ証明書を自前CA でサインした場合は、自前CAの証明書をクライアントメーラーでインポートしてからテストする。

Postfix のTLS設定

サーバ証明書は Dovecot の TLS設定で作成したものを共用する。Postfix だけ設定したい人は、そちらを見て作成しておいていただきたい。

Postfix の設定変更

main.cf に以下のパラメータを加える。設定のガイドラインは Postfix.org の "Postfix TLS Support" を参照するといい。

smtpd_tls_security_level = may
#smtpd_tls_auth_only = no
smtpd_tls_cert_file = /etc/pki/tls/certs/mailcert.pem
smtpd_tls_key_file = /etc/pki/tls/private/mailkey.pem
smtpd_tls_session_cache_database = btree:/var/run/postfix/smtpd_tls_session
#smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_dh1024_param_file = /etc/postfix/dh_1024.pem
smtpd_tls_dh512_param_file = /etc/postfix/dh_512.pem
#tls_random_source = dev:/dev/urandom
#tls_random_bytes = 32
#tls_random_reseed_period = 3600s
#smtpd_tls_loglevel = 1
smtpd_tls_security_level
TLSサポートを有効にし、その使用をクライアントに強制するかしないかなどを決めるパラメータ。may だと、クライアントは TLS を使わなくてもメール送信手続きができる。encrypt にすると、セッションの初めに STARTTLS をしない限り他のコマンドは一切受け付けない。
smtpd_tls_auth_only
前記が may の時、smtpd_tls_auth_only=yes にすると、SMTP AUTH だけは非暗号化セッション上では認めない。ただしその他のコマンド (`MAIL FROM' や `RCPT TO' など) は規制されない。このオプションの直接の意図は生のユーザ名とパスワードをネットワークに流させないことだが、結果的に「TLS を使わないとメールを外部ドメインに送れない」という動きになる。SMTP AUTH によるリレー制御との組み合わせで設定を考慮すべきだ。
smtpd_tls_cert_file
サーバ証明書 (公開鍵) を指定。
smtpd_tls_key_file
サーバ秘密鍵を指定。
smtpd_tls_session_cache_database
デフォルトでは、Postfixsmtpd と クライアントとの間で交わされたセッション情報は当の smtpd プロセスが消えると同時に消滅する。一方、このパラメータで情報をデータベースファイルに蓄えるようにしておけば、キャッシュとして働き、CPUパワーとネットワーク帯域の節約になる。セッション情報ひとつは数キロバイトあるため、比較的大きなデータも扱うことのできる btree (Balanced Tree) テーブルが向いているらしい。例として書いたディレクトリはデフォルトでは存在しないので、設定を反映させる前に作っておく (root:root 755) 必要がある。ただしキャッシュファイルそのものは、存在しなければ自動的に作られる。
smtpd_tls_session_cache_timeout
前記セッションキャッシュの有効期限を調整できる。例でコメントアウトしてある 3600秒 (=1時間) は Postfix のデフォルト。TLS プロトコルの RFC [RFC2246] は、24時間を上限とすることを推奨しているので、もっと長くしてもいいのかもしれない。
smtpd_tls_dh1024_param_file
smtpd_tls_dh512_param_file
Postfix のガイドを読むと、ブルートフォース攻撃を受けるリスクを減らすため、Diffie Hellman パラメータファイルは自前で生成したほうがいいと書かれている。DovecotDHファイルを定期的に再生成する機能があるくらいなので、Postfix 用のものも時たま作り直したほうがいいのかもしれない。そのため、DHファイルを生成する小さなシェルスクリプト postdh (root:root 750) を作って、postmap コマンドなどと同じ /usr/sbin/ に置いておくことにした。パラメータファイルがきちんとできたか確認するには、下記のようにコマンドする;
root# openssl dhparam -noout -text -in dh_1024.pem
smtpd_tls_loglevel
これをアンコメントすると TLS 関連の動きがログに出力される。設定後の試験やトラブルシューティングに役立つ。マニュアルには「絶対 4 にはするな」と書いてある。2 でも相当出る。

設定したら、反映のため Postfix をリロードまたは再起動する。

テスト

前提として、メールサーバの公開ホスト名 (サーバ公開鍵を作る時に cn に指定したもの) が名前解決できなければならない。まだやってなければ、クライアント上の hosts ファイルまたは DNSサーバにレコードを登録しておこう。

Telnetによる基本テスト
hoshu$ telnet mail.hoge.cxm 25
Trying 192.168.0.1...
Connected to mail.hoge.cxm (192.168.0.1).
Escape character is '^]'.
220 mail.hoge.cxm ESMTP Postfix
ehlo localhost
250-mail.hoge.cxm
250-PIPELINING
250-SIZE 512000000
250-VRFY
250-ETRN
250-STARTTLS         <- STARTTLSが出ていればOK
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
starttls
220 2.0.0 Ready to start TLS  <- こういったメッセージが返ってくればOK
quit
quit
OpenSSL s_clientによるテスト

/etc/pki/CA/cacert.pem を一般ユーザで読める場所 (例えば /tmp) へコピーしておいてから、

hoshu$ openssl s_client -starttls smtp -crlf -CAfile /tmp/cacert.pem \
 -connect mail.hoge.cxm:25
CONNECTED(00000003)
depth=1 /C=JP/O=Hoge Net/OU=Hoge Net Certificate Authority/CN=Hoge Net CA
verify return:1
depth=0 /C=JP/O=Hoge Net/OU=IT Development/L=Nagoya/ST=Aichi/CN=mail.hoge.cxm
verify return:1
---
Certificate chain
 0 s:/C=JP/O=Hoge Net/OU=IT Development/L=Nagoya/ST=Aichi/CN=mail.hoge.cxm
   i:/C=JP/O=Hoge Net/OU=Hoge Net Certificate Authority/CN=Hoge Net CA
---
<- 中略 ->
---
250 DSN
auth plain **************************
235 2.0.0 Authentication successful  <- こういったメッセージが返ってくればOK
quit
221 2.0.0 Bye
read:errno=0

あとは、実際のメールクライアントで試験。メールサーバ証明書を自前CA でサインした場合は、自前CAの証明書をクライアントメーラーでインポートしてからテストする。