2010/11/13

CouchDBのSecurity Featuresについて

CouchDBはとりあえず触ってみるのが目的でしたが、1.0.1を手動でインストールしてからは、いろいろとUbuntuのdebパッケージになっている0.10.0と違うところが目につくようになりました。

特にセキュリティ周りについては後になってからドキュメントを発見したり、authentication_dbの使い方を学んだりしたところがあるので、まとめておくことにしました。

対象はCouchDB 1.0.1です。

"JSON"という名称はデータ構造だったり、データをPOSTすることだったり、いろいろな意味で使われています。

"/db_name"となっているところは適宜、定義されているDatabase名で置き換えて読んでください。

CouchDB Wiki - Security Featuresを読んで

最初は CouchDB WikiにあるSecurity Featuresを読みました。

大切そうなところだけ、抜き出しておきます。

CouchDBでのユーザ認証について
  • CouchDBでは3種類のユーザが定義されている
    • server admins - サーバの設定を変更できるadminユーザ ("_admin" role?)
    • database readers - DB単位で存在し、全てのdocumentのreadと、design document以外のdocumentへのwrite/update権限を持つ
    • database admins - DB単位で存在し、DBの追加と削除以外の権限を持つ。readers権限に加えてdocumentに対する全ての操作と、database admins/readersの追加、削除が行なえる
  • DB毎のユーザ定義は "/db_name/_security" のパスでアクセスできる
    • database admins定義が一つもない場合には、server adminsがその操作を代行する
    • readers定義が一つもない場合には、全てのユーザがreader権限を持つことになる
  • local.iniのrequired_valid_userがtrueの場合には、どんなコンディションでも認証失敗は401が返る(はず
  • required_valid_userがfalseの場合:
    • local.iniの[admins]でserver adminsが定義されていない場合は、_admin"ロールが無条件に追加され、リクエストはserver admins権限でされたものとなる
    • local.iniの[admins]でserver adminsが定義されている場合は、ロールが付与されない状態で"anonymous"として通過する

"/db_name/_security" の内容によって、アクセスできる人を特定するためには、readers定義に何かを追加する必要があります。

とりあえず、あらかじめreadersのrolesに"db_name_reader"のようなネーミングルールでrole名を加えておく事にしました。

adminではないユーザの認証はBasic認証とCookie認証では、/_users/org.couchdb.user:user_name のdocumentにある情報を元に行なわれているようです。 手動で文書を作成したところ、Basic認証と(後で確認する)Cookie認証で正しく動く事がわかりました。

"/_users/"がauthentication_dbに定義されていて、0.10.0では"/users"だった点には注意が必要かもしれません。

この部分を読んで、proxy_auth*_handlerが認証結果がnilの場合にReqを返しているのは、required_valid_user=falseの振舞いを常にしているようです。

その点で、Proxy_auth*_handlerは、この文書が想定している基本的なCouchDBのセキュリティモデルとは少し違った振舞いをしているという事がわかりました。

/_config経由での*.iniファイルへのアクセス
  • *.iniにある設定内容には /_config 経由でアクセスできる
    • http://127.0.0.1:5984/_config
    • http://127.0.0.1:5984/_config/admins
    • http://127.0.0.1:5984/_config/httpd
    • などなど
  • /configに適切なJSONをPUTすれば、更新もできる(*.iniファイルへの書き込み権限が必要)

/_config経由で default.ini にある内容を変更してみたところ、local.iniに反映されていました。

また変更はRubyでいうとHash.to_jsonではなくて、String.to_jsonをPUTする方法でないと失敗しました。(e.x. couch.put("/_config/log/file", "/var/lib/couchdb/couch.log".to_json))

ファイルへの書き込み権限がなければエラーになります。

[Thu, 11 Nov 2010 10:38:01 GMT] [error] [<0.78.0>] ** Generic server couch_config terminating 
** Last message in was {set,"query_servers","javascript",
                            "\"\\\"/usr/local/bin/couchjs /usr/local/share/couchdb/server/main.js\\\"\\n\"\n",
                            true}
** When Server state == {config,[{<0.2450.0>,#Fun<couch_config.2.4103357>},
                                 {<0.2450.0>,#Fun<couch_config.2.4103357>},
...
authentication_db("/_users/")に必要なフィールド
  • "_admin" roleの定義はlocal.ini等の[admins]セクションで行なう
  • "_admin"以外の任意のrole設定は、ユーザ名毎に "/_users/org.couchdb.user:user1" のようなdocumentを作成して行なう
  • authentication_dbに指定されたDB(デフォルトは"_users")の中に"org.couchdb.user:user_name"の形式で非admin向けユーザのIDを登録する

"/_users/org.couchdb.user:user1"のようなドキュメントを作る時には、Webインタフェースを経由してDocumentを作成するのが簡単でしょう。

必要なフィールドは"/_design/_auth"文書に貼られているJavaScriptがポップアップを出してくれるので、"_id"を設定したら"Save Document"ボタンを押してみると早いと思います。

  • "_id" - org.couchdb.user:user_nameの形式(文字列)
  • "name" - "_id"の"user_name"と一致していないと登録できない(文字列)
  • "type" - user(文字列固定)
  • "roles" - ["role1","role2",..](配列)
  • "salt" - 適当な長さの文字列(文字列, adminsは不要)
  • "password_sha" - 生passwordと"salt"を連結した文字列をSHA1で変換したハッシュ値(文字列)

最後のpassword_shaは、やっぱりコマンドラインで求める事になります。

$ echo -n "raw-password""salt" | sha1sum

コマンド名は適宜変更してFreeBSDだと、コマンド名はsha1sumじゃなくてsha1だったりします。

商用OSだとechoの'-n'オプションがなかったりするので、'echo -n'の部分はprintfで置き換えた方がよかったのかもしれません。

CouchDB - The Definitive Guide, Securityを読む

次はO'Reillyから出ている The Definitive GuideのドラフトにあるSecurityの章を読みました。

Draft版なのでバグにハマりそうになりましたが、とりあえず気になったところをメモしておきます。

/db_name/_desing/の使い方

  • /db_name/_design/で始まるdocumentの validate_doc_update フィールドにfunction(newDoc,oldDoc,userCtx)の引数を取る関数定義を入れておくとドキュメントが作成、変更される度に実行される。
  • 1.0.1のコードをみるとuserCtxで参照できるパラメータは次の2つ。
    • userCtx.name
    • userCtx.roles
  • こういうオブジェクトの中身を確認するために、JavaScriptの中でlog()が使える。(e.x. function(newDoc, oldDoc, userCtx) { log(userCtx); })
  • documentには"language"フィールドも作成し、valueは"javascript"としておく。

validate_doc_updateに指定されている関数が呼ばれる部分のコードをみると、関数には四番目の引数"SecObj"をとる事ができます。 内容は"/db_name/_security"で得られるJSONと同じでした。

Cookie認証を試しながら読んでみた

  • Cookie認証でTokenが有効なのはデフォルトで10分間
  • curlを使ってCookieを入手する方法が記述されているが、POSTするデータ部には'username='ではなく、'name='を使う
  • 同じガイドの Appendix D. Installing from Sourceにセキュリティ上の懸念についてまとめられているとある

混乱したのはcurlを使ってCookie認証に必要なAuthSession Cookieを入手するところです。

またガイドのコマンドラインは不十分で、実際にはBasic認証を全体にかけているので'-u'オプション、または、URLでhttp://USER:PASSWD@172.16.73.197:5984/...の形式を使う必要があります。

$ curl -v -u user1:ab5e29d1 -X POST http://172.16.73.197:5984/_session -H 'application/x-www-form-urlencoded' -d 'name=user1&password=ab5e29d1'

この結果表示されるAuthSessionの値を使って、アクセスすると無事に10分間だけ内容が表示されました。

...
< HTTP/1.1 200 OK
< Set-Cookie: AuthSession=eWFzdTo0Q0RENkYzRToTTFf2ObO73hWM-PmW-DIPMHop3g; Version=1; Path=/; HttpOnly
...
curl -v -X GET http://172.16.73.197:5984/example/ --cookie AuthSession='eWFzdTo0Q0RENkYzRToTTFf2ObO73hWM-PmW-DIPMHop3g' -H "X-CouchDB-WWW-Authenticate: Cookie"

Appendix D. Installing from Source

Installing from Sourceに書かれていることは、rootではなくcouchdbユーザIDを使う点と、基本的なディレクトリのパーミッションの設定についてだけでした。

さいごに

とりあえず使い始めたCouchDBの気になったところをいろいろ調べて、使い方を知っているような状況です。

最終的にはCouchDBのインストールやらセットアップの方法やら、Java, Ruby, JavaScript辺りからの使い方もまとめなきゃかなぁと考えています。

何にしても、まとまりのない文書になってしまいました。

0 件のコメント: