Postfixの証明書認証アレコレ

この記事は、[Postfix Advent Calendar 2014](http://qiita.com/advent-calendar/2014/postfix)　12日目の記事です。
Postfixの証明書設定、認証について自分が調べた事を書きます。

***
### 目次
主な項目としては、
1. SMTPSのサーバ証明書設定と認証設定
2. SMTPSのクライアント認証設定
3. その他アレコレ

### 環境
どうせなら新しいほうがいいだろうと検証環境は、
`OS: Centos7`
`Postfix: postfix-2.10.1-6.el7.x86_64`
`SELINUX: Enforcing(石川さんありがとう)`
`Firewalld: tcp25,tcp587,tcp465`
※事前に以下のコマンドを発行して開放
> `# firewall-cmd --add-port=25/tcp --permanent`
> `# firewall-cmd --add-port=587/tcp --permanent`
> `# firewall-cmd --add-port=465/tcp --permanent`
> `# systemctl restart firewalld`

※裏側にimaps(Dovecot on CentOS7)サーバあるけど割愛、こっちは石川さんごめんなさい
※さらに第四種オレオレ証明書をCA込みで構築したけど割愛

\#2014/12/24に追記
※ニセDNSサーバ(MXレコード付)を構築、こちらも割愛

***
### 1. SMTPSのサーバ証明書と認証設定

#### メーラ(MUA)とPostfixサーバのSMTPS
SMTPSといえばHTTPSでいうWebブラウザとWebサーバの関係の様に、メールクライアントとメールサーバの間で暗号化された通信経路を構築してメールを送信するものでしょう。という事で早速PostfixのSMTPS設定を行います。

**証明書の設定**
/etc/postfix/main.cf　に以下を追記
> `smtpd_tls_cert_file=<証明書のパス>`
> `smtpd_tls_key_file=<秘密鍵のパス>`

※秘密鍵のパスワードは無しにして`chmod 400`しておいたほうが良いでしょう

**SMTPSポート(TCP465)の設定**
/etc/postfix/master.cf
26行目の
> `#smtps     inet  n       -       n       -       -       smtpd`

と28行目の
> `#  -o smtpd_tls_wrappermode=yes`

のコメントをはずしてPostfixサービスを再起動しましょう。これはずさないと、TCP465であってもSTARTTLS(後述)での通信になります。
> `# systemctl restart postfix`

これでHTTPSと同じようにいきなりTLSセッションから始まるSMTPS通信が使えます。

接続確認します
>`# openssl s_client -connect smtp.example.com:465`
>`CONNECTED(00000003)`
>`～中略～`
>#####`Verify return code: 19 (self signed certificate in certificate chain)`
> `---`
> `220 smtp.example.com ESMTP Postfix unknown`
> `→コマンド入力待ち`

という事でオレオレ証明書で怒られますが、接続はできました。
尚、
>`# openssl s_client -connect smtp.example.com:465 -CAfile <オレオレ認証局証明書>`

で認証局の証明書を指定した場合は
>`Verify return code: 0 (ok)`

を返してくれます。商用CAの証明書ならまず大丈夫でしょう

**Submissionポート(TCP587)の設定**
OP25Bの回避策として使われるSubmissionではSTARTTLSでの設定になります。STARTTLSってのは、EHLOした後でセッションの途中からTLSに切り替える通信です。
/etc/postfix/master.cf
16行目の
> `submission inet n       -       n       -       -       smtpd`

と18行目の
>   `-o smtpd_tls_security_level=encrypt`

のコメントをはずしましょう。
この18行目の`smtpd_tls_security_level`ですが、設定値を
> `encrypt`

にしてしまうと、クライアントから接続時に
> `530 5.7.0 Must issue a STARTTLS command first`

と必ずSTARTTLSをしてから送れ、と言われてしまうので、要件にもよりますが、暗号化を必須としない
>   `-o smtpd_tls_security_level=may`

にしておいたほうが良いかもしれません。

**SMTPポート(TCP25)の設定**
通常のSMTPでSTARTTLSの設定を行うにはmaster.cfではなくmain.cfを弄ります。
/etc/postfix/main.cf
に以下の値を追記します
> `smtpd_tls_security_level=may`

平文でのやりとりがデフォルトのTCP25では、暗号化できたらするけどできなくてもいいや的な`smtpd_tls_security_level=may`なのは当然ですね。この設定値を暗号化を必須とする`smtpd_tls_security_level=encrypt`にしようものなら、対応状況にもよりますが、どうなる事やら。。。。(構成にもよりますが、社内のメーラだけでなく、社外から送信されるサーバからの通信も対象になるので)

以上PostfixサーバのSMTPS対応でした。

#### Postfixサーバと送信先メールサーバのSMTPS
　Postfix側から先方のメールサーバにメールを中継する時、PostfixサーバはSMTPクライアントとして動作します。
　Postfixの送信設定をSMTPS対応にして相手のサーバがSMTPS(STARTTLS)に対応していれば暗号化された通信経路でメールが送られます。
　インターネット上のメールサーバ全部が全部SMTPSに対応している訳ではない為、「暗号化できたらする」といった設定がデフォルトになります。

**Postfixからメール送信する時のSTMPS設定**
それでは早速クライアントとしてのSMTPS対応を行います。
/etc/postfix/main.cf
に以下の値を追記します
> `smtp_tls_security_level=may`

先ほどサーバとして動作する時の設定値は`smtpd_～`となっていましたが、クライアントの場合は`smtp_～`となります。
これで、Postfixがメールを送信する時のSMTPS(STARTTLS)設定完了です。

**送信先ドメイン毎のSTMPS設定**
送信先ドメインによっては、必ず暗号化したいといった場合があります。
そういう時は`smtp_tls_policy_maps`を使って個別設定ができます。

/etc/postfix/main.cf
に以下の値を追記します
> `smtp_tls_policy_maps = hash:/etc/postfix/tls_policy`

新規で/etc/postfix/tls_policy ファイルを作成し送信ドメイン毎に暗号化強度を設定します。最近POODLEの脆弱性があった事ですし、`protocols=TLSv1` でSSLv3を使わないようにしましょう。
そしてRC4ではなくAESを使う様に`ciphers=high`を設定しておきましょう。
記述は
<送信先ドメイン> <レベル> <オプション>の順になります。
最後の行に「上記以外のドメインはmay」とします
※mayの行は<オプション>が設定できません。

> `hoge.com secure protocols=TLSv1 ciphers=high`
> `foo.com verify protocols=TLSv1 ciphers=high`
> `fuga.com encrypt protocols=TLSv1 ciphers=high`
> `* may`

ハッシュ化を忘れずにしましょう
> `# postmap /etc/postfix/tls_policy`

Postfixを再起動して設定完了です。

この設定値ですが、`may`<`encrypt`<`verify`<`secure`の順で強くなります。それぞれの条件は以下の通りです。
`may`:暗号化できたらするけど、できなくてもメール送信OK
`encrypt`:暗号化必須だけど、証明書は規格に則っていれば何でもOK
`verify`:暗号化必須で、さらに証明書のCAが信用できるかも検証する
`secure`:`verify`に加えて、証明書の`SubjectAlternativeName`か`CommonName`が接続先サーバ名と一致している事が必須

`encrypt`以上はSSL通信が確立しないとメールが送られません。下手にレベルを高くすると、ひたすら送信に失敗した挙句、5日後にGiveupメールが送信者に届くことがあります。

`verify`以上の場合は不明な認証局の証明書(unknownCA)だったり、証明書の有効期限切(certificate is not yet valid / certificate has expired)だったりで送れないという事があります。(迫真)

また、`verify`以上の場合は必然的に検証の為に信頼すべきCAも増えます。オレオレ認証局を追加する等、信頼すべきCAが複数にまたがる場合は、バンドルされているCAファイルを直接指定する
　> `smtp_tls_CAfile=<CAの証明書>`
で、追加したいCA証明書を追記するか
　> `smtp_tls_CApath=<CAの証明書置き場>`
　　としてディレクトリを指定し、
　その中にはハッシュリンクでCAの証明書を管理する方法があります。

ハッシュリンクの作成方法はこちらが詳しいです。
  [システム管理者の心得？ ～ OpenSSLコマンドで証明書をチェック（３） ](http://dsas.blog.klab.org/archives/50484787.html)

`secure`以上を指定するのは、かなりリスキーです。オレオレ証明書ではなく、商用CAの証明書を使っていても、CNの値が一致していないという事がままあります。HTTPSのあの厳格さがSMTPSには無いのが残念。


###2. SMTPSのクライアント認証
クライアント認証というのは、サーバ側が、アクセスしてきたクライアント側を証明書で認証する技術です。
HTTPの世界では、Webサーバソフトの大御所であるApacheやIIS、最近ではnginxでもクライアント認証は出来るものの、これがPostfixではどうなってるか調べてみました。

#### メーラ(MUA)とPostfixサーバのクライアント認証
Postfixでクライアント証明書認証を行う場合は以下の設定値を使います。

/etc/postfix/main.cf
に以下の値を追記します
>`smtpd_tls_ask_ccert = yes`
>`smtpd_tls_security_level = may`

これでクライアント認証にも対応しました。

強制(絶対クライアント証明書を提出させる)する場合は以下の通りにします。
メール受信時に必ずクライアント証明書を要求するので、送信専用サーバでない限りは、設定しないほうがいいでしょう。外部からのメールが受信できなくなるかと思います。
/etc/postfix/main.cf
>`smtpd_tls_req_ccert = yes`
>`smtpd_tls_security_level = encrypt`

Postfixサーバがクライアント認証する必要があるのは、リレー中継許可かどうかですので、使いドコロとしては、`smtpd_relay_rectrictions`になります。
/etc/postfix/main.cf
`smtpd_relay_rectrictions=permit_tls_all_clientcerts,reject_unauth_destination`

で、クライアント認証が完了した者のみリレー可能となります。

中継の動作確認をします。
クライアント証明書を提示する場合
`openssl s_client -connect smtp.example.com:465 -cert <クライアント証明書> -key <クライアント秘密鍵>`

クライアント証明書を提示しない場合
`openssl s_client -connect smtp.example.com:465`
で接続しますが、クライアント認証強制モードの場合は
`421 4.7.1 smtp.example.com Error: No client certificate presented`
となって、セッションが終了します。


ここまで調査した時点で、PostfixはPKI(公開鍵基盤)のCRLやOCSPレスポンダに対応するオプションがない事を知りました。(公式ドキュメントを見た限りでは)

　PKIの認証局では、発行済の証明書(秘密鍵)ついては何処にあるか、いくつコピーされたかなんて追えません。
　従業員が退職した後や、危殆化(秘密鍵が盗難に遭う等)が判明しても、有効期限までその証明書は有効であると保証しちゃっている為、何らかの対策が必要になってきます。

CRL(失効した証明書の一覧ファイル)の配布を行ったり、OCSPレスポンダ(シリアル番号を尋ねると、有効か失効済かを回答する)サーバを用意する事で、対策をとっているのですが、それを利用する仕組みはアプリ側が実装する必要があります。
WebサーバでもFreeRADIUS等でもCRLをチェックするレベルのオプションがありますが、Postfixにはそのオプションが無い様です。
→ちょっと調べてみても、CRLチェック関係の議論が海外ではあるようですが今後実装される事はあるのかな。詳しい人教えて下さい。

その代わりといってはなんですが、Poftixでは、フィンガープリントを使いなさいと公式ドキュメントにあります。
先ほど設定した中継設定では
/etc/postfix/main.cf
`smtpd_relay_rectrictions=permit_tls_all_clientcerts,reject_unauth_destination`
となっていますが、
`permit_tls_all_clientcerts`を設定すると、証明書が正しければ全て通過するようです。つまり制限無し。秘密鍵が1個でも漏れたら終わり。
これとは別に
`permit_tls_clientcerts`を使えば、

/etc/postfix/main.cf:
>`relay_clientcerts = hash:/etc/postfix/relay_clientcerts`

という設定と
/etc/postfix/relay_clientcerts:
>`D7:04:2F:A7:0B:8C:A5:21:FA:31:77:E1:41:8A:EE:80 lutzpc.at.home`

(公式ドキュメント丸パクリ)
という<フィンガープリント> <ユーザ名>というファイルで、
使えるクライアント証明書をリストアップできそうです。あぁめんどくさそう。

という訳で、クライアント認証は敷居が高い感じですね。

###3. その他アレコレ
　今回の記事の調査で知った蛇足情報2点です。

**SMTPSセッションでのRコマンド**

openssl s_clientを使ったクライアント認証中のSMTPSセッションで
`RCPT TO:`
を発行すると、最初の1文字目の`R`で
`RENEGOTIATING`
となって、宛先が設定できません。
大文字のRから始まる命令は`RENEGOTIATING`しかないからだそうで、
宛先指定は`rcpt to:`を使う必要があるとの事です。

**PostfixサーバとDovecotサーバが異なる時のSMTP-AUTH設定**
今回の為にCentOS7でDovecotによるIMAPSサーバを構築してSMTP-AUTH用の設定を施したのですが、Postfixサーバとは異なるサーバに認証先を設定出来る事を初めて知りました。認証先がDovecotの場合、
/etc/postfix/main.cf
　`smtpd_sasl_auth = enable`
　`smtpd_sasl_type = dovecot`
　`smtpd_sasl_path = inet:<Dovecotサーバアドレス>:12345`
で実現できます。

Dovecot側は
/etc/dovecot/conf.d/10-master.confにて
`service auth {`
`  inet_listener {` ←追記
`    port = 12345` ←追記
`  }` ←追記
`}`
で実現できました。


### まとめ
・SMTPSを導入してメールの通信経路を暗号化しましょう
・暗号化の必要性に応じてポリシー導入しましょう
・クライアント認証はまだまだこれから？

### 最後に
PostfixのAdvent Calendarがあると聞いて、あれやこれや呟いた所、周りからの嬉しい後押しを頂いたので、、今回思い切って枠を頂いてみました。

私は特にブログも持たず、IT系の記事を書くのは初めての人間なので、記事の体裁が整っていない、間違っている箇所があるかもしれません。ご指摘頂ければ感謝です。

(なにしろmarkdownすら初めてで、慣れるまでに時間がかかった。。。)

とまぁこんな感じで、とりとめもなく役に立つかどうか微妙な情報ではありますが、検証しながら進める上で自分にとっては勉強になり、俺得なまとめになりました。

丁度この記事を書いている最中にOCNの定期パスワード定期変更強制のハナシがありました。iPadで表示された「SSLではない事を警告するダイアログ」で「はい」を選択させる、というのを見るにつけて、E-mailのSSL(SMTPSやIMAPS)がもっと広まってくれればいいなぁと思います。

そういう意味では、[Let’s Encryptプロジェクト](https://www.eff.org/deeplinks/2014/11/certificate-authority-encrypt-entire-web)が来年以降広まれば、もっと容易にSSL環境が構築出来るのかなーと期待

ありがとうございました。