色々出来ることに制限のある環境で以下のような要件を満たす httpd.conf の設定を編み出したメモ。
まず httpd-2.2 でほぼ静的な設定の既存WEBサーバ環境があった。で、そこの一部ディレクトリにBASIC認証をかけたいという依頼を受けた。しかし色々縛りがあって以外に大変だったわ…というネタ。
ちなみに結構変態に分類される設定だと思うが、単に縛りの中で頑張ったら出来ましたってだけの設定を吐き出してるだけなので、他の誰の参考にもならない気がする。.htaccess
が使えるなら使えば良いし、httpd-2.2
以外にして良いとか、間に別のProxy挟んで良いとか、他のモジュール入れて良いとか、他の手段が選べるならもっと楽で真っ当な方法を検討したほうが良い。
要件
- 要求
- /secret/ 以下にBASIC認証をかけたい
- /secret/foo と /secret/bar はそれぞれ別のID/PASSにしたい。
- /secret/ 以下のディレクトリ名は不定(依頼者が新しく任意のディレクトリを作って、ID/PASSを発行する)
- パスワードの発行管理は依頼者自身が行えるようにしたい。( /secret/.htpasswd の編集で完結させる)。
- 制限事項
- httpd のバージョンは 2.2 系(2.4ではない)
- .htaccess は使わせられない(
AllowOverride None
) - /secret/ 以下に新しいディレクトリを作る毎に面倒は見れない
- 依頼者は新規ディレクトリの作成とパスワードファイルの編集のみ自由にできる。
- やっかないなポイント
- .htaccess は有効化出来ず、httpd.conf に初回に1度設定追加を行うことしか出来ない。
- ないのでBASIC認証の設定が出来るのは /secret ディレクトリに対する1つだけ
-
require valid-user
だけじゃ駄目なのは明らか(ディレクトリ毎に別ユーザに出来ない) -
AuthGroupfile
のグループ制御でどうにか出来んか?とも思ったけどrequire group xxx
を自由に変えられない(htaccessが使えない)ので駄目 -
httpd-2.4
なら require で env を見たり式が使えたりするのでなんとでもなりそうだが…。
- .htaccess は有効化出来ず、httpd.conf に初回に1度設定追加を行うことしか出来ない。
出来た
色々試行錯誤した経緯を端折って結果だけ書くと以下の様な設定で実現できた。
<Location /secret/>
# とりあえず普通のBASIC認証設定
AuthUserfile /path/to/docroot/secret/.htpasswd
AuthGroupfile /dev/null
AuthName "Secret"
AuthType "Basic"
require valid-user
# ユーザ名と同名のディレクトリのみを許可する(ただしadminユーザは全てのディレクトリを閲覧可)
RewriteEngine on
RewriteCond %{REMOTE_USER},%{REQUEST_URI} !^([A-Za-z0-9_-]+),/secret/\1/
RewriteCond %{REMOTE_USER} !=admin
RewriteRule . - [F,E=retry_basic]
Header set WWW-Authenticate "Basic realm=\"Secret\"" env=retry_basic
</Location>
制限事項が1つあり、ディレクトリ毎のユーザ名はディレクトリ名と同名のユーザ名が強制される。
その点さえ目をつぶれば、新規ディレクトリを作ったら .htpasswd にディレクトリ名と同じユーザとパスワードを追加するだけで良い。また、管理者はディレクトリ毎にユーザ名を変えてみるのは面倒だと思うので admin というユーザ名はディレクトリ名の縛りが効かないようにした。
力技ポイント
valid-user が通ったユーザに対して改めてBASIC認証をやり直させる方法
-
require valid-user
が通ったユーザに対して、mod_rewrite で改めてユーザ名とディレクトリ名の不一致をチェックして強制的に403 Forbidden
を返す。 - また
F
と同時に環境変数を介してHeader set
と連携し、単に 403 にするだけでなく、BASIC認証の要求ヘッダを出力する。 - これにより
valid-user
であるにも関わらず、ブラウザには改めてBASIC認証の入力ダイアログが表示されることになる。
地味に面倒だった 『ディレクトリ名とユーザ名の一致チェック』
-
RewriteCond %{REQUEST_URI} ^/secret/%{REMOTE_USER}/
とかどうだ?- →
%{REMOTE_USER}
の{
と}
の部分が正規表現エラー。まぁそうよね…。
- →
-
RewriteCond %{REQUEST_URI} ^/secret/%\{REMOTE_USER\}/
だと?- →
%{REMOTE_USER}
っていう文字列そのままの名前のディレクトリにマッチしちゃう。ですよねー…。
- →
-
RewriteCond %{REQUEST_URI} ^/secret/([^/]+)
して更にRewriteCond %{REMOTE_USER} %1
とか後方参照で分けたらどうよ!?- → ちょっと期待してたんだが
%1
ていう2文字の名前のBASIC認証ユーザ名が通るようになったよ! - つまり第2引数に変数入れられないってことなんだな…。しかしそれだと REQUEST_URI と REMOTE_USER を比較する方法ってあるんか?
- → ちょっと期待してたんだが
💡 ピコーン! そういえば RewriteCond
の第1引数って単なる文字列だから変数2つとも突っ込んで、1個の正規表現の中で後方参照使って2箇所に分かれた部分文字列の一致チェックをすればいいんじゃね!?
ってことで ↓ この1行マッチが生まれた。
RewriteCond %{REMOTE_USER},%{REQUEST_URI} !^([A-Za-z0-9_-]+),/secret/\1/
結果は見事成功。やったね! 🎉