SSLアクセラレータ配下のapacheで、アクセスがhttpかhttpsかを判別する方法
少し前に試行錯誤して現在はひとまず解決したのですが、同じようにはてなで悩んでた人がいるみたいなので、自分の設定例を軽くまとめてみる。
SSLアクセラレータ配下にあるapache上でクライアントからのアクセスがhttpsかhttpであるかの判別をする方法はありますか?
mod_rewriteを利用しhttpのアクセスをhttpsにリダイレクトする設定を考えていますが、SSLアクセラレータを経由してのアクセスとなるため、apacheへの接続は全てhttpとなります。
回答にもあるように、恐らくapache単独では解決できませんが、SSLアクセラレータの設定と組み合わせれば可能です。それも拡張ヘッダやmod_rewriteなどの特別な仕組みは使用せずに。ただし、ここで書いてるのはSSLアクセラレータ付きのロードバランサ(以下、LB)を使用してる例なので、他の全てのSSLアクセラレータには当てはまらないかもしれません。でも似たようなことはできるかと。
あと、記事を書きながらhttps時のリダイレクト(自己参照URL)についても思い出して、それについても考慮して書きました。単にportを判別するだけならもっと簡単で良さげな気もしますが、長々と書いちゃっています。
設定
とりあえず先に結論を。
LB,SSLアクセラレータの設定
実サーバにhttp/httpsのリクエストを送信する際に、転送先のポート番号をそれぞれ個別に設定する。例えば
言い換えると、http/httpsともに実サーバの80番にしか送れない場合は判別できません。たぶん大抵の場合は大丈夫だと思います。
### 2016 追記
L7まで見れる最近のロードバランサ(ADC)の場合、http headerをいじれるので
その場合はhttp/httpsともに実サーバが80番でも大丈夫ですね。
port:80のvirtualserverの場合はheaderをいじらず実サーバに渡す。
port:443のvirtualserverの場合はこのエントリ末尾に書いているように、"X_HTTPS: ON"のようなheaderをinsertする。
アプリケーション側でX_HTTPS headerがあるかどうかで判別する。
### 追記終わり
実サーバのapacheの設定
NameVirtualHostでポート番号を変えて、http用とhttps用の2つの設定を作ります。
後で詳しく書きますが自己参照URLとServerNameの設定が大事。apacheのServerNameのドキュメントにもこう書いています。
リバースプロキシやロードバランサやSSL負荷軽減装置のような、 SSLを処理するマシンの後ろでサーバを動かす場合は、 サーバが正しい自己参照 URLを確実に生成するように、 https:// スキームとクライアントが接続するポート番号を、 ServerName ディレクティブに指定してください。
Listen 80 Listen 8443 NameVirtualHost *:80 NameVirtualHost *:8443 # http用の設定 <VirtualHost *:80> ServerName www.hoge ## その他色々 </VirtualHost> # https用の設定 <VirtualHost *:8443> ServerName https://www.hoge ## その他色々 </VirtualHost>
schemeとport番号を明示的に書くならこう。
# http用の設定 <VirtualHost *:80> ServerName http://www.hoge:80 ## その他色々 </VirtualHost> # https用の設定 <VirtualHost *:8443> ServerName https://www.hoge:443 ## その他色々 </VirtualHost>
判別方法
こう設定しておけば、httpとhttpsで環境変数SERVER_PORTの値が変わります。cgi,phpなどで適当に出力すればわかります。
- http://www.hoge/ => 80
- https://www.hoge/ => 443
ここでhttpsの環境変数が8443ではなくて、443になっているのがポイントです。
しているので、8443になりそうですが。。。
以下、それの補足です。
ServerNameと自己参照URL
これを理解するまで色々試行錯誤しました。詳細はapacheの以下ドキュメントを参照。
- ServerName
- UseCanonicalName
- UseCanonicalPhysicalPort
- mod_dir
なぜhttps用のVirtualHostのServerNameにhttps://のschemaを指定しているのか。
話がややこしくならないように、ここでは以下のapacheのデフォルト設定が入ってるとします。
UseCanonicalName Off UseCanonicalPhysicalPort Off
そして、ServerNameに他の設定をした例を紹介します。
ServerNameにschemaを指定せず、かつポート番号を書かない場合
通常のVirtualHostのように設定すると、以下のようになると思います。
# https <VirtualHost *:8443> ServerName www.hoge ## 残りhttps用の設定 </VirtualHost>
このとき、clientからhttps://www.hoge/にアクセスされた場合の、apacheの自己参照URLはhttp://www.hoge/になっていると思われます。すると以下の問題が起こります。
- 環境変数で取得できるSERVER_PORTが80番になる
- 自己参照URLがhttp://のため、"/"なしのdirectoryにアクセスした場合などのリダイレクトがhttpになる
1番はそのままですね。本エントリの条件を満たしてないので問題外。2番は例を見てもらったほうがわかりやすいと思います。
"/"なしのディレクトリにアクセスした場合、
apacheデフォルトではmod_dirのDirectorySlashが働いて"/"が補完されます。このときにclientに返されるURLは、自己参照URLに/が補完された形です。こんな感じです。
- はてなグループで実験
※ はてなグループがSSLアクセラレータを使っているかは知りませんが、httpにリダイレクトするという例で紹介します。
- https://fragments.g.hatena.ne.jp/hogem https://のスラッシュなしにアクセスすると
- http://fragments.g.hatena.ne.jp/hogem/ http://にリダイレクト
opensslで確認するとよくわかります。
$ openssl s_client -connect fragments.g.hatena.ne.jp:443 -state (中略) HEAD /hogem HTTP/1.0 host: fragments.g.hatena.ne.jp HTTP/1.1 302 Found Date: Fri, 14 Nov 2008 12:22:30 GMT Server: Apache/2.2.3 (CentOS) Location: http://fragments.g.hatena.ne.jp/hogem/ X-Framework: Hatena/2.0 Content-Type: text/html; charset=iso-8859-1 Vary: Accept-Encoding Connection: close SSL3 alert read:warning:close notify closed SSL3 alert write:warning:close notify
302でhttpのURLが返ってきています。
ServerNameに8443のポート番号を明示的に指定する場合
最初はこの設定にしていました。この設定だとclientがhttpsに来た場合にSERVER_PORTを8443で取れるから。ただ、ServerNameの説明を読み直して、"これはひどい"状態になっていることが確認できたので慌てて元に戻しました。
同じようにclientからhttps://www.hoge/にアクセスされた場合を考えます。この設定だとapacheの自己参照URLがhttp://www.hoge:8443 になります。そのため
- 環境変数で取得できるSERVER_PORTが8443になってhttpとhttpsは判別できる、、、が!
- リダイレクトするとhttp://www.hoge:8443/directory/のようなURLになる
opensslで確認すると302でLocatin: http://www.hoge:8443/directory/ が返ってくるのが確認できます。8443番ポートなんてのは通常FWで閉じるのでアクセスすら出来ません。おまけに内部の仕組みが出てカッコ悪いですね。あわてて修正した苦い記憶が。。。
まとめ
httpかhttpsかを判別する方法。
20090323追記
よくよく考えたらこんな面倒なことしてポート番号を見なくてもいいのではないかということに気が付いた。mod_envで適当な環境変数作るだけでいい?
https用のVirtualHost内で
Setenv X_HTTPS ON
とか
Setenv HTTPS ON
とかを設定して、後はプログラム側で$ENV{X_HTTPS}を判定するだけ。(phpは$_SERVER["HTTPS"] でHTTPSを判別してると思うので後者のほうがいいかも?本当のSSLじゃないので個人的には環境変数 HTTPS を設定するのは気持ち悪いですが)
この場合でもやはりhttpとhttps用のvirtualhostをわけることは必須ですね。というか、わけなかった場合、DocumentRootやCustomLogの設定まで全て同じになるので、ポート番号を判別したくなくても個別に設定したほうが良いのか。
debianのbusybox - static link版
fedoraのyumでinstallしたbusyboxはlibraryをstatic linkしてたのに、debianはshared library使ってるのねー。
# apt-get install busybox # ldd `which busybox` linux-gate.so.1 => (0xffffe000) libcrypt.so.1 => /lib/tls/i686/cmov/libcrypt.so.1 (0xb7ed2000) libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0xb7ead000) libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7d7b000) /lib/ld-linux.so.2 (0xb7f0f000)
static版ねーのかなと探したら見つかった。/lib壊れたときのこととか考えたらstaticのほうがいいかなと思って。
# apt-cache search busybox busybox - Tiny utilities for small and embedded systems busybox-static - Standalone rescue shell with tons of builtin utilities mindi-busybox - Collection of shell utilities in a single executable for Mindi/Mondo # apt-get install busybox-static # ldd `which busybox` not a dynamic executable
mod_proxyでhot-standby
active/standbyって言ったりもするかも。日本語のドキュメントにはまだ翻訳されてないんですね。
- http://httpd.apache.org/docs/2.2/en/mod/mod_proxy.html
- http://httpd.apache.org/docs/2.2/ja/mod/mod_proxy.html
status=+Hでhot-standbyになる。
ProxyPass / balancer://hotcluster/ <Proxy balancer://hotcluster> BalancerMember http://1.2.3.4:8009 loadfactor=1 BalancerMember http://1.2.3.5:8009 loadfactor=2 # The below is the hot standby BalancerMember http://1.2.3.6:8009 status=+H ProxySet lbmethod=bytraffic </Proxy>
- statusパラメータの説明
Single letter value defining the initial status of this worker: 'D' is disabled, 'S' is stopped, 'I' is ignore-errors, 'H' is hot-standby and 'E' is in an error state. Status can be set (which is the default) by prepending with '+' or cleared by prepending with '-'. Thus, a setting of 'S-E' sets this worker to Stopped and clears the in-error flag.
ループバックデバイスって何さ
CDのisoイメージをmountするときに-o loopって慣習的にしてたけど、なんなの?
- http://www.itmedia.co.jp/help/tips/linux/l0171.html
- http://itpro.nikkeibp.co.jp/article/Keyword/20080118/291396/
どうやら任意のファイルをブロックデバイスのように扱う機能、のことらしい。なるほど。
実例
この人のやってること見てイメージできた。
- /dev/zeroから空のファイル作成
- mkfsでext用のファイルシステムとして作り直す
- -o loopでブロックデバイスのようにマウント
試しにloopオプションをつけないで、普通のファイルをマウントしたらブロックデバイスじゃないと怒られた。
# mount -t ext3 ./hoge.img /mnt/ mount: ./hoge.img is not a block device (maybe try `-o loop'?) # mount -t ext3 -o loop ./hoge.img /mnt/
SSLアクセラレータ使用時でもIEのSSL不具合対策用の設定は入れておいたほうがいいかも
参考
- http://kazu-360.cocolog-nifty.com/blog/2007/08/apache_mod_ssli_7c51.html
- http://support.microsoft.com/kb/305217/
- defaultのssl.conf
古いIEのSSL時に不具合があるらしくて、apacheのmod_sslでもdefaultのssl.confにこんなの書いてる。
BrowserMatch ".*MSIE.*" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0
なので、mod_ssl使用時は注意してたんだけど。。。