よかろうもん!

アプリからインフラまで幅広くこなすいまどきのクラウドエンジニアが記す技術ブログ

Lighttpdの設定で注意すべきこと

Rails製のOP(OpenID Provider)をMongrelで動かすと問題なく動作するが、Lighttpdで動かすとうまくいかないよ!」とお困りの方がいらっしゃいましたら、是非このエントリを読んでみてください。

OpenIDのOPをLighttpdで動かす時には、注意しないとLighttpdの設定次第ではdiscoverがうまくできない場合があります。
結論を先に書くと、discoverでアクセスするURLを変換(最後に"/"を追加するなど)する際に、Lighttpdの設定でリダイレクトではなくリライトするようにしなければならないようです。

ここでは実際にirbを利用してdiscoverに成功するかを順に記載していきます。

環境

※RPもOPもRails製のものとし、今回はRPとして情報共有ソーシャルウェアであるSKIPを、OPにはmasqueradeを利用しました。

OpenID 2.0に準拠した仕様とし、RPはOPにユーザを特定する情報(はてなの場合は http://www.hatena.ne.jp/interu/ )を送信しない構成(Yahoo!の場合のように http://yahoo.co.jp )とします。

relative_url_rootでアクセスURLをそれぞれ変更していますので、WEBサーバとしてLighttpdを利用されている方は、http://localhost:3200/testop でアクセスがあったら、http://localhost:3200/testop/ に変更するという設定をLighttpの設定ファイルに記述されているかと思います。
アクセスURLの変更には大きく分けると以下の2種類があります。

  • ユーザのブラウザに「アクセス先が変更になりましたよ!」と通知して再度指定されたURLにアクセスするとリダイレクト
  • ユーザのアクセスURLをWEBサーバが勝手に書き換えてしまうリライト

アクセスURLの変更でリダイレクトを利用されている方は以下のような設定を記述されているはずです。
#以下がLighttpdの設定ファイルでその中の赤色部分がリダイレクトの設定箇所となっています。

# Default configuration file for the lighttpd web server
# Start using ./script/server lighttpd

server.bind = "0.0.0.0"
server.port = 3200

server.modules = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi", "mod_compress", "mod_expire", "mod_redirect", "mod_alias" )

extforward.forwarder = ("127.0.0.1" => "trust")

server.pid-file = CWD + "/tmp/pids/lighttpd.pid"
server.document-root = CWD + "/public/"

server.errorlog = CWD + "/log/lighttpd.error.log"
accesslog.filename = CWD + "/log/lighttpd.access.log"

#url.rewrite = ( "^/$" => "index.html" )
#url.rewrite += ("^([^.]+)$" => "$1.html" )

compress.filetype = ( "text/plain", "text/html", "text/css", "text/javascript" )
compress.cache-dir = CWD + "/tmp/cache"

expire.url = ( "/favicon.ico" => "access 3 days",
"/images/" => "access 3 days",
"/stylesheets/" => "access 3 days",
"/javascripts/" => "access 3 days" )

# Change *-procs to 2 if you need to use Upload Progress or other tasks that
# *need* to execute a second request while the first is still pending.

url.redirect = ( "^/testop(\?.*)?$" => "/testop/$1" )
$HTTP["url"] =~ "^/testop/" {
server.document-root = CWD + "/public"
server.error-handler-404 = "/testop/dispatch.fcgi"

alias.url = ( "/testop/" => CWD + "/public/" )

fastcgi.server = ( ".fcgi" => ( "localhost" => (
"min-procs" => 1,
"max-procs" => 1,
"socket" => CWD + "/tmp/sockets/fcgi.socket",
"bin-path" => CWD + "/public/dispatch.fcgi",
"bin-environment" => ( "RAILS_ENV" => "development","APPNAME" => "testop" )
) ) )
}

mimetype.assign = (
".css" => "text/css",
".gif" => "image/gif",
".htm" => "text/html",
".html" => "text/html",
".jpeg" => "image/jpeg",
".jpg" => "image/jpeg",
".js" => "text/javascript",
".png" => "image/png",
".swf" => "application/x-shockwave-flash",
".txt" => "text/plain"
)

# Making sure file uploads above 64k always work when using IE or Safari
# For more information, see http://trac.lighttpd.net/trac/ticket/360
$HTTP["useragent"] =~ "^(.*MSIE.*)|(.*AppleWebKit.*)$" {
server.max-keep-alive-requests = 0
}

このような設定にしておくこと http://localhost:3200/testop でのアクセスをリダイレクトで http://localhost:3200/testop/ に変換するようにしますが、http://localhost:3200/testop でアクセスした場合に認証サーバであるOPを見つけることができなくなります。

以下に『簡単にOPエンドポイントURLが発見できるかどうか確認する方法』で紹介した方法でdiscoverできるかどうかを確認してみます。

http://localhost:3200/testop/ にアクセスした場合

irb(main):003:0> OpenID.discover("http://localhost:3200/testop/")
=> ["http://localhost:3200/testop/", [#]]

http://localhost:3200/testop にアクセスした場合

irb(main):003:0> OpenID.discover("http://localhost:3200/testop")
OpenID::DiscoveryFailure: Failed to fetch identity URL http://localhost:3200/testop : Error encountered in redirect from http://localhost:3200/testop: Error fetching /testop/: undefined method `request_uri' for #
from /usr/local/ruby/1.8.7/lib/ruby/gems/1.8/gems/ruby-openid-2.1.2/lib/openid/yadis/discovery.rb:79:in `discover'
from /usr/local/ruby/1.8.7/lib/ruby/gems/1.8/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery.rb:382:in `discover_yadis'
from /usr/local/ruby/1.8.7/lib/ruby/gems/1.8/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery.rb:478:in `discover_uri'
from /usr/local/ruby/1.8.7/lib/ruby/gems/1.8/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery.rb:487:in `discover'
from (irb):4
from /usr/local/ruby/1.8.7/lib/ruby/site_ruby/1.8/rubygems/dependency.rb:21

以上の結果より、http://localhost:3200/testop でアクセスすると、"OpenID::DiscoveryFailure: Failed to fetch identity URL"となってしまいます。
discoverするときのURLに注意しておけば問題ないと言えばそうなのですが、しかしながら私たちは人間であり、いつの日にかサービス・ストップという事態を招いてしまうことがあるかもしれません。
それを避けるためにもアクセスURLがどちらであっても正常に動作するようにしておくべきであると私は思っています。#実は自分が納得できていないだけなんですけどw

それでは、OPをLighttpdで動かしていても、discoverするときのURLを気にしなくていいようにしたいと思います。
そのためには、リダイレクトを利用して http://localhost:3200/testophttp://localhost:3200/testop/ に変換する方法を変更しなければなりません。
具体的には、リダイレクトするのではなくリライトするようにすれば、この問題を解決できます。

上記で記載したredirectの設定部分を以下のように変更します。

url.rewrite-once = ( "^/testop(\?.*)?$" => "/testop/$1" )

すると、http://localhost:3200/testop でアクセスしてもdiscoverに失敗することはなくなります。

念のためにirbで確認してみます。

■ redirectからrewriteに変更し http://localhost:3200/testop/ にアクセス

irb(main):006:0> OpenID.discover("http://localhost:3200/testop")
=> ["http://localhost:3200/testop", [#]]

うまくいきました。

このようにアクセスURLの変換でリダイレクトするようにしていると正常に動作しないことがありますので、リライトを利用するようにしてください。
#何でもかんでもリライトにするのではなく、要件に応じてリダイレクト、リライトを選択するようにしましょう。

これでRPをLighttpdで動作させて、discoverを正常に行えるようになりました。

参考までにですが本エントリの序盤で、'''Mongrelで動かすと問題なく動作するが、Lighttpdで動かすとうまくいかないよ'''と書きましたがこれには訳があります。
それは、Mongrelで動作させる場合、prefixを指定するだけ、リダイレクトするのかリライトするのかを気にする必要がないためです。
Mongrelの起動については『lighttpdからmongrelへ』を参考にしてください。

prefixを /testop とし、3200番ポートでdevelopmentモードで動かす時は以下のコマンドでRPを起動します。

[linux]$ cd ${RAILS_ROOT}
[linux]$ mongrel_rails start --prefix /testop -e development -p 3200

まだ調査していないためMongrel内部で prefix まわりの処理がどのように実装されているかはわかりませんが、Mongrelの場合は、discoverするURLの最後に"/"が含まれてもそうでなくても正常にOP Endpoint URL等を返してくれます。

まとめ

  • LighttpdでOPを動かす場合は、リダイレクトとリライトの使い分けに注意が必要
    • discoverで利用するURLについて変換を行う場合はリライト

  • Mongrelでアプリを動かす際は、リダイレクトとリライトを意識しなくてよい

補足

今回、RPでLighttpdを用いる場合は注意が必要ということをお伝えしましたが、なぜリライトでないといけないかについては原因特定ができていません。
また、Mongrelで動かした場合はなぜうまくいくかについても理由が明確になっていませんので、知っていらっしゃる方がいらっしゃいましたらコメントお願いします。

#さて次はMongrelの内部処理について追ってみよっかな。