SSL Server Test

日経NETWORK 2018年9月号より

米クリオスのSSLラボが提供するSSLチェックツール
https://www.ssllabs.com/

財テク.jpというサイトをチェックしたところレーティングはBでKey Exchangeに問題があるとの判定だった。

B判定の主な原因として、下記3点があった。
This server uses SSL 3, which is obsolete and insecure. Grade capped to B.
SSL3に対応している。

This server accepts RC4 cipher, but only with older protocols. Grade capped to B.
ChiperにRC4が利用できる。

This server does not support Forward Secrecy with the reference browsers. Grade capped to B.
Foward Securityに対応していない。。。。

一方で、HSTSに対応している点は評価された。
HTTP Strict Transport Security (HSTS) with long duration deployed on this server.

クライアント証明書

クライアント証明書は、ID+パスワードによる認証などと並んだ認証方法の一つで、SSL通信が必須である認証方式。(Basic認証はSSL通信は必須ではないが、パスフレーズが暗号化されない点では、盗聴リスクがあることに留意しなければならない。)
クライアント証明書を使用した認証の流れについては、Apacheでのクライアント認証の仕組みに記載されているが、通信相手を信頼する方法は、公開鍵暗号方式を利用する点では、サーバ証明書と同一だが、下記の点で異なる。

  1. 公開鍵暗号方式で共有した共通鍵によって通信の暗号化を保証する方法がクライアント証明書には含まれていない点
  2. と秘密鍵を保持している端末が異なる。(サーバ(サーバ証明書の場合)とクライアント(クライアント証明書の場合))が

クライアント証明書は、秘密鍵を保持しているだけに、サーバ証明書と同様に正しく管理さえされていればなりすましによる認証の可能性が限りなく減らせる。ID+パスワードによる認証とあわせて、多要素認証とすることが出来れば、さらになりすましの可能性を減らせる一方で、証明書の配布と管理が煩雑である面を持つ。

気軽にクライアント証明書を試すには、下記2ステップとなる。
1.自己証明局を作成する。
オレオレ認証局の作り方~SSL証明書を無料で作る方法 on CentOS 5
2.認証方法の変更とクライアント証明書の作成方法
オレオレ認証局でクライアント認証 ~ ウェブの Basic 認証をリプレース
なお、上記では、Webサーバの設定にSSLVerifyClientオプションが登場してくるが、この意味合いについては、ApacheでIP制限とクライアント認証をor条件で運用するに記載されている。

ApacheとTomcatを連携するにはどのモジュールを使うのがよいか???

案1)mod_proxy_http
案2)mod_proxy_ajp
案3)mod_jk?

Apache-Tomcat連携モジュールmod_jk/mod_proxy_ajp/mod_proxy_httpそれぞれについて接続が切れた時の挙動を比較・調査してみた

また、IISとTomcatを連携するには、ajp通信する場合には、ISAPIフィルタしかないみたい。

ところで、WebサーバとAPサーバを別マシンで稼働させる場合の優位性は何だろうか?

1.APサーバは重要なデータを保持しているため、APサーバとの通信は、Webサーバにのみ行うことで、セキュリティを担保しやすい。一方Webサーバはフロントエンドになるため、不特定多数と通信する。

2.Webサーバをロードバランサ(SSL通信を行う場合には、SSLオフロード)の役割を担わせることによって、APサーバをスケールアウトできる。

3.APサーバで処理する必要のない静的コンテンツをWebサーバ側で処理させることによって、オフロードできる。

DNSラウンドロビンについて考える

最近DNSラウンドロビンの構成になっているWebサーバを見かけなくなったのは、ロードバランサが仮想化されて稼働率が限りなく100%に近づいているからということなのか?
とあるサービスにて、複数のロードバランサをDNSラウンドロビンする方法があったのだが、ロードバランサに障害発生した場合に、DNSラウンドロビン登録しているAレコードのキャッシュ有効時間を限りなく短くしたうえで、ロードバランサ障害時にDNSレコードを動的に変更できる運用体制がないと可用性を下げる要因にしかならないのではないか?と思ってみた。
DNSラウンドロビンにおいては、
WindowsでDNSのラウンドロビン機能を利用するの文中にあるように
クライアント側の再試行機能(アクセスできない場合は、別のサーバIPアドレスへの接続を試行する)に”期待”したりする(アプリケーションによっては、アクセスできない場合は自動的に別IPアドレスへ再試行する機能を持っている)
とあり、アプリケーションの実装によっては、アクセスできない場合には別のAレコードの値を参照して再試行するというものがあるらしいが、アプリケーションが直接複数のレコードを使い分けるような実装をしているものがあるとは個人的には考えにくい。
参考)
Load Balancing
Azure Load Balancer の概要

CloudFrontを利用して静的コンテンツを高速化する

このブログサイトをCloudFrontを使って高速化してみた。
早くなったかどうかはちょっとわからないが、Googleからのリクエスト速度は間違いなく上がっているので、検索順位向上には寄与している(と思いたい)。
CloudFrontはCDN(Content Delivery Network)のサービスだが、EC2やLamdaなどのAWSのサービス以外にも利用できる。
昔はCDNなんてakamaiぐらいしかなく、個人が使えるようなサービスではなかった。
CloudFrontを独自ドメインで利用するメリットは下記が挙げられる。
1.全世界からのアクセスが高速化できる。(CDNの導入目的と同じ)
例えば、当ブログサイトのドメインに割り当てられているアドレスd2p33equgohrri.cloudfront.netを正引きすると以下のように8つのアドレスが返される。
このアドレスは、リクエスト元の地域(リージョン)によって変わることになる。
>nslookup -q=A d2p33equgohrri.cloudfront.net
権限のない回答:
名前: d2p33equgohrri.cloudfront.net
Addresses: 54.230.111.20
54.230.111.187
54.230.111.227
54.230.111.55
54.230.111.251
54.230.111.57
54.230.111.220
54.230.111.238
2.Webサーバへのリクエストを減らすことが出来る=負荷を軽減できる。(CDNの導入目的と同じ)
3.(D)Dos攻撃にも耐えられる。(CDNの導入目的と同じ/但し、リクエスト数が増大するので、課金がかさむことになる)
4.CloudFrontをSSL化しておいて、CloudFront→Webサーバ 間をHTTP通信にすることにより、昨今のセキュリティ要求に対応しながら、SSL通信にかかる負荷を軽減できる。
5.SSL証明書が無料で利用できる(CloudFront独自のメリット/但し、IPベースにすると毎月600ドル課金されるので注意)
6.AWSの無料枠の対象なので、気軽に試せる(CloudFront独自のメリット)
7.HTTP/2に対応できる。(CloudFront独自のメリット?/リクエストが高速になる)
SSL証明書はLet’s Encryptを利用する方法もあるが、CloudFrontを利用すると証明書の期限が13ヵ月になっているうえ、更新が自動で行われる点がビジネス目的に耐えられる点で優れている。
CloudFrontを利用するにはWebの画面だけで完結できるが、日本語化されておらず少しハードルは高い。
ただ、WordPressサイトをCloudFrontで配信するの説明が非常に分かりやすい。
上記サイトでは、WordPressサイトの登録を変更していたが、
Origin Domain Name には、CDN用のエイリアスFQDN(例:cdn.blog.development-network.net)
Alternate Domain Names には、オリジナルのFQDN(例:blog.development-network.net)
を登録し、cdn.blog.development-network.netのCNAMEに登録後に表示されるDomain Name(例:d2p33equgohrri.cloudfront.net)をDNSに登録すればよい。
SSL証明書の発行手順は、認証がメールベースになっているので、独自ドメインのメールエイリアスを登録できるようになっている必要がある。
SSL証明書を発行しても、すぐに独自ドメイン用のSSL証明書に切り替わらず、証明書エラーが表示されるので、少し焦るがCloudFrontのStatusがDeployedになるまでは待ってみることをお勧めする(焦った)

ban4ipインストールしてみました

オープンソースアンカンファレンス金沢2016でT.Kabuさんからのご紹介のあった
fail2banをやめてBan4ipにしませんか?
を早速試してみました。
ApacheやSSH、postfix、dovecot等のログを解析して失敗を検知した接続元のIPアドレスをiptablesに書き込みしてフィルタリングするデーモンになります。
IPv6にも対応しているところが優れています。
CentOS7の環境でしたが、問題なく導入でき1日間で17回BANできています。
インターネット環境にサーバを公開するということは常に攻撃にさらされていると感じました。
自動BANツールを導入していない方はぜひ導入してみるとよいと思います。
なお、サーバを再起動する際には、banした設定が消えてしまうので /sbin/service iptables save を忘れずに!

Let’s Encrypt でのSSL証明書作成

無償でSSL証明書を発行できるLet’s Encrypt。HTTPS対応は必須になりつつある。
ちなみにこのブログもLet’s Encrypt でSSL証明書を取得している。
Let’s Encrypt の使い方を基に実際にやってみた。
バージョンが変わると一部実装が変わるので、最新はコマンド一覧を参考にしてほしい。
証明書を取得したいFQDNをwww.hogehoge.com、そのDocumentRootが/var/www/www.hogehoge.com/ として記載する。
証明書を発行する場合
Webサーバを停止した状態で、証明書を取得する場合
$ sudo ./certbot-auto certonly –standalone -d www.hogehoge.com
Webサーバを停止せずに証明書を発行する場合
$ sudo ./certbot-auto certonly –webroot -w /var/www/www.hogehoge.com/ -d www.hogehoge.com
x Saving debug log to /var/log/letsencrypt/letsencrypt.log x
x Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org x
x Obtaining a new certificate x
x Performing the following challenges: x
x http-01 challenge for zaiteku.jp x
x Using the webroot path /var/www/www.hogehoge.com/ for x
x all unmatched domains. x
x Waiting for verification… x
x Cleaning up challenges x
x Generating key (2048 bits): x
x /etc/letsencrypt/keys/****_key-certbot.pem x
x Creating CSR: /etc/letsencrypt/csr/****_csr-certbot.pem
IMPORTANT NOTES:
– Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/www.hogehoge.com/fullchain.pem. Your cert will
expire on YYYY-MM-DD(有効期限). To obtain a new or tweaked version of this
certificate in the future, simply run certbot-auto again. To
non-interactively renew *all* of your certificates, run
“certbot-auto renew”
– If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
証明書を更新する場合
$ sudo ./certbot-auto renew
ただ、この場合にはWebサーバを停止させる必要がある。
Saving debug log to /var/log/letsencrypt/letsencrypt.log
——————————————————————————-
Processing /etc/letsencrypt/renewal/www.hogehoge.com.conf
——————————————————————————-
Cert is due for renewal, auto-renewing…
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for www.hogehoge.com
——————————————————————————-
Port 443 is already in use by another process. This will prevent us from binding
to that port. Please stop the process that is populating the port in question
and try again. For automated renewal, you may want to use a script that stops
and starts your webserver. You can find an example at
https://certbot.eff.org/docs/using.html#renewal . Alternatively you can use the
webroot plugin to renew without needing to stop and start your webserver.
——————————————————————————-
Cleaning up challenges
Attempting to renew cert from /etc/letsencrypt/renewal/www.hogehoge.com.conf produced an unexpected error: At least one of the required ports is already taken.. Skipping.
All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/www.hogehoge.com/fullchain.pem (failure)
1 renew failure(s), 0 parse failure(s)
無停止による証明書の更新方法について
下記コマンドで無停止で実行しようと思ったがうまくいかない
$ sudo ./certbot-auto renew –webroot -w /var/www/www.hogehoge.com/ -d www.hogehoge.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Currently, the renew verb is only capable of renewing all installed certificates that are due to be renewed; individual domains cannot be specified with this action. If you would like to renew specific certificates, use the certonly command. The renew verb may provide other options for selecting certificates to renew in the future.
下記コマンドでWebサーバの停止→証明書の更新→Webサーバの開始が可能(証明書の更新対象がない場合には再起動しない)
$ sudo ./certbot-auto renew –pre-hook “systemctl stop httpd” –post-hook “systemctl start httpd”
1.更新対象がある場合は次のようになる。
——————————————————————————-
Processing /etc/letsencrypt/renewal/www.hogehoge.com.conf
——————————————————————————-
Cert is due for renewal, auto-renewing…
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Running pre-hook command: systemctl stop httpd
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for www.hogehoge.com
Waiting for verification…
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0004_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0004_csr-certbot.pem
——————————————————————————-
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/www.hogehoge.com/fullchain.pem
——————————————————————————-
2.更新対象がない場合には下記のようになる。
Saving debug log to /var/log/letsencrypt/letsencrypt.log
——————————————————————————-
Processing /etc/letsencrypt/renewal/www.hogehoge.com.conf
——————————————————————————-
Cert not yet due for renewal
The following certs are not due for renewal yet:
/etc/letsencrypt/live/www.hogehoge.com/fullchain.pem (skipped)
No renewals were attempted.
No renewals attempted, so not running post-hook

レジューム機能

途中からダウンロードを再開できるレジューム機能は、HTTP/1.1に実装されている。
Range や Content-Rangeヘッダを含むGETメソッドにて指定バイトよりダウンロードが可能となり、その際のStatus Codeは206(Partial Content)となる。これらのヘッダを受け入れるかどうかはサーバーによって異なる。
詳しくはこちら

Segmentation faultが発生するとHTTPレスポンスが返らない

Linux+Apache2 + PHPの環境において、特定のページだけがブラウザでアクセスしても「ページが表示できません」となってしまう。
VirtualHostディレクティブで設定しているaccess_logをtail -f で監視していても対象のページだけリクエストがログに残らない。ページが表示されるときには正しくログに残っているので、書き出し先が間違っているわけでもない。
次にVirtualHostディレクティブで設定しているerror_logを見てみたが考えられるエラーは記録されていない。
その次にWiresharkでTCP通信を監視してみた。すると
(正常系)
クライアント –(SYN)–> サーバー
クライアント <--(SYN+ACK)-- サーバー クライアント --(ACK)--> サーバー
クライアント –(ACK)–> サーバー HTTPリクエスト
クライアント <--(ACK)--> サーバー HTTPレスポンス
と続くはずが
(異常系)
クライアント –(SYN)–> サーバー
クライアント <--(SYN+ACK)-- サーバー クライアント --(ACK)--> サーバー
クライアント –(ACK)–> サーバー HTTPリクエスト
クライアント <--(ACK)-- サーバー クライアント <--(ACK+FIN)-- サーバー クライアント --(ACK)--> サーバー
クライアント –(ACK+FIN)–> サーバー
で通信が終了してしまっている。
つまりHTTPレスポンスが返っていないのだ。
(このときはなぜにFINを返してしまうのだーと正直思った)
Linuxでもtcpdumpを使って解析したが、同じパケットがやり取りされており、通信経路上の問題点ではないことがわかった。
そうなるとTCPレイヤーが上位アプリケーション層にパケットを渡さなかったか、アプリケーション層が受け取ったパケットに対する応答をTCPレイヤーに返せないかのどちらかである。
/var/log/messageにはなにも記録されていなかったので、前者は考えにくく、またこの現象はPHPプログラムを入れ替えた機能から発生していることをみると、プログラムが怪しそうだったので、前のプログラムに入れ替えるとやはり問題が改善していた。
プログラムで問題のあると思われるところを修正した結果、正しくページが表示できるようになった。
これでめでたしめでたしだったが、ステータスコードが返せない理由がよくわからなかったので、ログをいろいろ調べてみた。
すると、httpd.confで設定しているerror_logをみると
[notice] child pid 21450 exit signal Segmentation fault (11)
という記録があった。
今回勉強になったのは次の3点
・Segmentation faultなどの致命的な問題が起動プログラム上で発生した場合、ApacheはログをVirtualHostディレクティブで指定したErrorLogのパスに書き出してくれない。(子プロセスが落っこちるから仕方ない?)
・また、Apacheの子プロセスが落っこちた場合にHTTPレスポンスを返せない。
・access_logを記録するのはHTTPレスポンスを返したあとなので、リクエストを受け取れたとしてもaccess_logには記録が残らない。
初めて遭遇した不可思議なこのトラブルには解決に2時間くらい要してしまった。。。

リバースプロキシを利用する上での問題点

Apache2.0から利用可能となったリバースプロキシ機能で ProxyPassとProxyPassReverseを利用することで本格的なロードバランサーを作成できる。
ただ、このロードバランサーを利用する上での問題点もあることは事実。その問題点について説明したい。
1.ProxyPassの指定先が異なるディレクトリだった場合においてコンテンツ内に絶対パスが含まれているとリンクがうまくいかない
たとえば、
ProxyPass / http://192.168.0.1/
などであれば問題はないが、
ProxyPass /share http://192.168.0.1/
などとしてしまうと絶対パスが指定されたリンクではうまく行かないことがある。また、baseタグが利用されているコンテンツがある場合にも注意が必要。
2.ProxyPassの指定先のホストが外部からは接続できないようなセグメントに配置されている場合において、HTTP_HOSTというサーバー環境変数をリンクに利用している場合にうまくいかない
たとえば、
ProxyPass / http://192.168.0.1/
と設定してリバースプロキシサーバーにグローバルIPアドレスが振られているケースでインターネットからリバースプロキシサーバーにアクセスするとはHTTP_HOSTの返答値が192.168.0.1になってしまい、それがリンクに使われているようなことがあると、そのリンク先をクリックすることでプライベートIPアドレスに接続に行ってしまうことになる。これはSERVER_NAMEという環境変数を使った上で、httpd.confのServerNameの設定値が外部DNSにおいて正引き可能なホスト名でかつ外部から到達可能なIPアドレスで解決できなければならない。なお、UseCanonicalNameがOffになっているとhttpd.confのServerNameは利用されないので、Onにする必要がある。(ApacheのデフォルトはOn)
もしリバースプロキシによるロードバランシングを検討されている場合には、上記のようなコンテンツ構成になっていないかを注意する必要があるだろう。