https リバースプロキシーサーバの作り方

はじめに

ひょんなことからhttpsを使うリバースプロキシーサーバを作ることになりました.個人情報を含んだ通信は暗号化すべき,なんて世の中の流れに乗ったわけです.で今回はそのメモ.今回作ったプロキシーはクライアントとの間をhttpsで通信しますが,サーバとの間はhttpsを使うものと,httpを使うものの2種類を実現します.ざっと絵に描くとこんな感じ.数字はポート番号です.

             --- https ---> 443        --- https ---> 443 サーバA
クライアント                  リバースプロキシー
             --- https ---> 8443       --- http  --->  80 サーバB

apacheコンパイル

今回は,リバースプロキシーをnetbsd3.0上で作ってみました.netbsdを選んだ理由は特にありません.以前使っていたので他のOSより慣れてる,というぐらいでしょうか.まぁ,それはともかく,apache2.2をダウンロードして展開,configureを以下の引数で実行してからmake, make installしました.

./configure --prefix=/usr/local/http_proxy --enabe-rewreite \
--enable-proxy --with-mpm=prefork --enable-ssl --with-ssl=/usr/local/ssl

httpd.confでの設定

昔からimport文なんてあったかなぁ,と思いつつ書いてみたのが以下の設定になります.実際にはコメント文なんかが沢山あるんですが,ここでは一切省いています.

ServerRoot "/usr/local/http_proxy"
User daemon
Group daemon
<Directory />
  Options FollowSymLinks
  AllowOverride None
  Order deny,allow
  Deny from all
</Directory>
ErrorLog logs/error_log
LogLevel warn
Include conf/extra/httpd-mpm.conf
Include conf/extra/httpd-default.conf
Include conf/extra/httpd-ssl.conf

やろうとしているのは,

  1. このサーバにはコンテンツを何も置かない
  2. エラーログのフォーマットはCommon Log Format(CLF)

の2点です.CLFのフォーマットは以下のようになっています.

Format 意味
%h リモートホストIPアドレス
%l identで得たクライアントアイデンティティ
%u http認証で得たリモートユーザ名
%t 受付時刻
%r リクエストの最初の行
%>s status
%b httpヘッダを除くレスポンスのバイト数

httpd-default.confとhttpd-mpm.confはデフォルトをそのまま使ったので,個々では説明を省略します.

httpd-ssl.conでの設定

ここが本題の設定になります.間違ってる箇所があったら指摘していただけるとうれしいです.ちなみにIPアドレスを明示しているのは,デフォルトの設定ではIPv6用のポートも開いてしまうので,それを防ごうとしているからです.

SSLRandomSeed startup file:/dev/urandom 1024
Listen 10.10.10.10:443
Listen 10.10.10.10:8443
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl    .crl
SSLPassPhraseDialog exec:/usr/local/http_proxy/conf/pp-filter
SSLSessionCache        shmcb:/usr/local/http_proxy/logs/ssl_scache(512000)
SSLSessionCacheTimeout  300
SSLMutex  file:/usr/local/http_proxy/logs/ssl_mutex

<VirtualHost 10.10.10.10:443>
DocumentRoot "/usr/local/http_proxy/htdocs"
ServerName proxy.foo.co.jp:443
ServerAdmin proxymaster@foo.co.jp
ErrorLog /usr/local/http_proxy/logs/error443_log
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP
SSLCertificateFile /usr/local/http_proxy/conf/server.crt
SSLCertificateKeyFile /usr/local/http_proxy/conf/private_key.pem
BrowserMatch ".*MSIE.*" \
  nokeepalive ssl-unclean-shutdown \
  downgrade-1.0 force-response-1.0
CustomLog /usr/local/http_proxy/logs/ssl_request443_log \
  "%t %h %{SSL_PROTOCOL}x %{Cookie}i \"%r\" %b"
SSLProxyEngine On
RewriteEngine On
RewriteRule ^(.*) https://serverA.foo.co.jp$1 [P]
</VirtualHost>

<VirtualHost 10.10.10.10:8443>
DocumentRoot "/usr/local/http_proxy/htdocs"
ServerName proxy.foo.co.jp:8443
ServerAdmin proxymaster@foo.co.jp
ErrorLog /usr/local/http_proxy/logs/error8443_log
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP
SSLCertificateFile /usr/local/http_proxy/conf/server.crt
SSLCertificateKeyFile /usr/local/http_proxy/conf/private_key.pem
BrowserMatch ".*MSIE.*" \
  nokeepalive ssl-unclean-shutdown \
  downgrade-1.0 force-response-1.0
CustomLog /usr/local/http_proxy/logs/ssl_request8443_log \
  "%t %h %{SSL_PROTOCOL}x %{Cookie}i \"%r\" %b"
RewriteEngine ON
ProxyPreserveHost On
ServerName proxy.foo.co.jp:8443
RewriteRule ^(.*) http://serverB.foo.co.jp$1 [P]
</VirtualHost>

いくつかポイントを絞って説明します.

SSLPassPhraseDialog exec:/usr/local/http_proxy/conf/pp-filter

秘密鍵パスフレーズをつけていると,apache起動時に毎回パスフレーズの入力を求められます.セキュリティを確保するためには当然な方法ですが,メンドクサイ.そのため,パスフレーズを自動的に入力しています.pp-filerの中身はこんな感じになります.

#!/bin/sh
echo PASSPHRASE

簡単すぎですね.ちなみに,pp-filterはrootを所有者にしてアクセス権を700にしておくことをお勧めします.

SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP

SSLCipherSuiteは奥深い話で,とても一言では書けません.ニゲだ.Pound の SSL セキュリティレベルを制限(上げる)方法 - drk7jpがSSLCipherSuiteに詳しく書いているので,そちらを参考にしてください.

CustomLog /usr/local/http_proxy/logs/ssl_request443_log \
  "%t %h %{SSL_PROTOCOL}x %{Cookie}i \"%r\" %b"

ログのフォーマットをCLFとはちょっと変更してみました.

Format 意味
%t 受付時間
%h リモートホストIPアドレス
%{SSL_PROTOCOL}x mod_sslで使える拡張フォーマット,環境変数を表示
%{Cookie}i リクエストヘッダ中の値
%r リクエスト最初の行
%b httpヘッダを除くレスポンスのバイト数

クライアントとの間で使っているSSLのバージョンとクッキーが正しく流れているかどうかを確認するために使っています.

SSLProxyEngine On
RewriteEngine On
RewriteRule ^(.*) https://serverA.foo.co.jp$1 [P]

ここいら辺がリバースプロキシーの核になります.httpsでサーバにアクセスするためには,SSLProxyEngineをOnにする必要があります.

RewriteEngine ON
ProxyPreserveHost On
ServerName proxy.foo.co.jp:8443
RewriteRule ^(.*) http://serverB.foo.co.jp$1 [P]

httpでサーバにアクセスする場合にはSSLProxyEngineを使う必要はありません.この例ではProxyPreserveHostとServerNameというディレクティブを設定しています.これはServerBがホストヘッダの値を元に処理を行なっているために設定した項目です.お使いの環境では必要ないかもしれません.バーチャルホストの例 - Apache HTTP サーバ バージョン 2.2に説明が記載されています.

終わりに

ロキシーの設定は大変です.何が大変と言って,サーバにバグがあればプロキシーがおかしいんじゃないかと責められるし,クライアントの環境設定にミスがあれば,やっぱりプロキシーがおかしいんじゃないかと責められます.でも,疑いを晴らそうと思ってがんばると,ログのフォーマットについて妙に詳しくなるというすばらしい(?)メリットが待っているのです.はぁ〜