Mercurial 勉強中 (7) - Web 経由の push と HTTP 認証

Web 経由で Mercurial のレポジトリを公開すると,デフォルトの状態では clone / pull しかできません。push するためには設定が必要になります。

なお今回は hgweb.cgimod_wsgi 経由*1等で Apache と絡めた場合の話になります。

というのは,hg serve コマンドで起動される HTTP サーバは BaseHTTPServer をもとにしているのですが,ビルトインの機能としては Authentication をサポートしておらず((自力で WWW-Authenticate ヘッダ等やりとりすればいけるんじゃとは思います。詳しくないので自信ないです。)),また hgweb.server モジュールでもハンドリングしていないので認証関連の機能が実装されていないためです。

設定子

hgrc 設定ファイルで下記のものが特に関係のある設定子です。

  • web
    • allowpull
    • allow_push
    • deny_push
    • push_ssl

push 可能に設定する

[][web][] セクションの allow_push 設定子を指定します。

allow_push
Whether to allow pushing to the repository. If empty or not set, push is not allowed. If the special value "*", any remote user can push, including unauthenticated users. Otherwise, the remote user must have been authenticated, and the authenticated user name must be present in this list (separated by whitespace or ","). The contents of the allow_push list are examined after the deny_push list.

hgrc

レポジトリ下の .hg/hgrc ファイルに下記の内容を追加します。

[web]
allow_push = *

暫定的に「*」を指定しています。下記で説明している HTTP 認証と絡めて具体的なユーザ名を指定することもできます。

HTTPS(HTTP)でも push 可能に

デフォルトでは HTTPS 経由でないと push できません。イントラなど HTTP 経由で構わない場合は [][web][] セクションの push_ssl 設定子に false を指定します。

push_ssl
Whether to require that inbound pushes be transported over SSL to prevent password sniffing. Default is true.

hgrc

具体的にはレポジトリ下の .hg/hgrc ファイルに下記の内容を追加します。

[web]
push_ssl = false

HTTP 認証の設定を加える

Mercurial の Web インタフェースの認証関連の機能は Remote-User ヘッダを見ています。ですので,通常の Apache と同様に設定すれば OK です。

<Location path-to-repository>
AuthType Basic
AuthUserFile /foo/bar/htpasswd

Require valid-user
</Location>

これで allow_push 設定子に具体的なユーザ名を指定することができるようになります。

ちなみに上の例では Basic 認証ですが,実際には私の環境では mod_auth_ntlm_winbind で AuthType NTLM を使用しています*2

push だけ認証を要求したい

clone だけは anonymous OK だけど push できるのは認証したユーザだけにしたい,というのはよくある要件です。

アクセスログを見ると,どうやら push の際には

  • POST repository URI?cmd=unbundle&heads=HOGEHOGEHOGE

というアクセスが発行されるようです。

ですので <LocationMatch>?cmd=unbundle をひっかけてもいいんですが,ざっとみた感じメソッドが POST に限定されているようなので,Subversion と揃えて次のような設定で制限をかけるようにしました。

AuthType Basic

<LimitExcept GET PROPFIND OPTIONS REPORT>
    Require valid-user
</LimitExcept>

念のため deny_push も指定しておく

いままでの内容では allow_push に「*」を設定しました。もし手違いで Apache 側の認証関連の設定を lost してしまった場合,anonymous に push 可能になってしまいます。

あくまで認証しているユーザには push をさせたい,だけど anonymous に push されてしまうのも困る,ということを Mercurial 側に設定しておきましょう。deny_push を使うと可能になります。

deny_push
Whether to deny pushing to the repository. If empty or not set, push is not denied. If the special value "*", all remote users are denied push. Otherwise, unauthenticated users are all denied, and any authenticated user name present in this list (separated by whitespace or ",") is also denied. The contents of the deny_push list are examined before the allow_push list.

hgrc

副次的効果ですが deny_push になんらかの設定がなされている場合,REMOTE_USER が設定されていない場合に push できなくなります(コードも見て確認しました)。

ですので,ダミーですが

[web]
allow_push = *
deny_push = unauthenticated_user

のようにしておくと,万一 Apache の設定が失われても push できなくなります。

push するたびに毎度ユーザ名とパスワードを聞かれてうざい

Subversion の場合,一度 commit する際にユーザ名とパスワード入力すると,その後は認証情報を覚えてくれます。ですが,Mercurial の場合,push する度に聞かれます。

% hg push
pushing to http://localhost/hg/example
searching for changes
http authorization required
realm: Mercurial
user: dayflower
password: ********
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files

なんとかなりませんか。

その答え,FAQ にあります。

How can I store my HTTP login once and for all ?

You can specify the usename and password in the URL like:

http://user:password@mydomain.org

Then add a new entry in the paths section of your hgrc file.

How can I store my HTTP login once and for all ? - FAQ - Mercurial

ううう,なんかダサい。

どうでもいいけど

Unauthorized でハネられるときにも Status 200 を返すのがダサいと思いました。