Apache2.2でリモートアドレスベースでの制限 "または" SSLクライアント証明書による認証を行う

現行でリモートアドレスベースでのアクセス制限を行っているサーバがあるんだけど、新たにSSLクライアント証明書をインストールした端末にもアクセスを許可したいという話があった。ちょっとトリッキーだけどなんかうまくいったので記録。むしろ情報求む。

証明書の発行とApacheの設定

この辺参照。

以下、サーバのSSLCACertificateFileなどの設定と、ブラウザへのクライアント証明書のインストールが終わった状態として話を進める。*1

クライアントに証明書を要求する

まず、アクセス時にクライアントに証明書を要求するわけだけど、リモートアドレスベースで許可されている場合には証明書は不要なので、ここではSSLVerifyClient optionalを指定する。

##
## SSL Virtual Host Context
##
<VirtualHost _default_:443>
...
  <Directory "/var/www/html_ssl">
    Options -Indexes FollowSymLinks
    AllowOverride All
  </Directory>

  SSLVerifyClient optional
  SSLVerifyDepth 1
</VirtualHost>

今回はSSLVerifyClient optionalをバーチャルホストまたはサーバレベルで設定する必要がある。DirectoryやLocationの中ではダメ。

Allow設定に追加する

SSL認証が成功した場合、SSL_CLIENT_VERIFYというヘッダに"SUCCESS"という文字列が設定されるので、それで条件判定した結果をAllowに追加する。

order deny,allow
Deny from all

# IPアドレスベースでのAllowが並んでいる
Allow from XXX.XXX.XX.XXX
...

# SSL_CLIENT_VERIFYがSUCCESSの場合に認証済みとする ※これは動かない!!
SetEnvIf SSL_CLIENT_VERIFY "^SUCCESS$" clcertified
Allow from env=clcertified

と思いきやこれは動かない。デフォルトでSetEnvIfで使えるヘッダは標準的なHTTPヘッダに限られていて、SSL_CLIENT_VERIFYヘッダを参照することはできないようだ。

てことは他の方法で環境変数セットするしかない。が、いろいろやってみるがうまくいかない。

# RewriteRuleで環境変数にセットしてやる ※これは動かない!!
RewriteEngine on
RewriteCond     %{SSL:SSL_CLIENT_VERIFY} "^SUCCESS$"
RewriteRule .* - [E=clcertified:SUCCESS,L]

どうやら、Allow from env=で使えるのはSetEnvIfで設定した環境変数だけらしい(cf: 「Allow | Turboflash's Blog」。) が、RewriteRuleはSetEnvIfより後に評価されるので、RewriteRuleの処理結果をSetEnvIfに伝えることもできない。

困っていたら、RequestHeaderディレクティブにearlyというキーワードを発見した。これを使ってみる。

##
## SSL Virtual Host Context
##
<VirtualHost _default_:443>
...
  SSLVerifyClient optional
  SSLVerifyDepth 1
  RequestHeader set SSL_CLIENT_VERIFY_early "%{SSL_CLIENT_VERIFY}s" early
</VirtualHost>

早期処理の設定はメインサーバかバーチャルホストコンテキストに書かないとダメらしい。

なんと、RequestHeader setでセットしたヘッダはSetEnvIfで使える!これで認証済みかどうかの判定をAllowディレクティブに伝えられる。

# IPアドレスベースでのAllowが並んでいる
Allow from XXX.XXX.XX.XXX
...
# SSL_CLIENT_VERIFY_earlyがSUCCESSの場合に認証済みとする
SetEnvIf SSL_CLIENT_VERIFY_early "^SUCCESS$" clcertified
Allow from env=clcertified

これでリモートアドレスベースで登録されているクライアントと、クライアント証明書がインストールされているクライアントからのアクセスを許可できた(一応。)


穴が無いかとかもう少し調べてみる。

*1:ちなみにサーバのSSL証明書のCA局とクライアント証明書のCA局は別でも大丈夫みたい。つまりサーバはVerisignとかで署名貰って、クライアント証明書はオレオレCA局でも認証はできる。