Webサーバーを運用していると、特定のディレクトリやドメインに対してIPアドレスベースのアクセス制限をかけたい場面がよくあります。Apacheでは、<Directory>
ディレクティブを使った传统的なIP制限と、SetEnvIf
とRequire
を組み合わせたHostヘッダーベースの柔軟なIP制限が利用できます。
しかし、もしこの二つの設定が同じディレクトリに対して重複して記述されていたら、一体どのような挙動になるのでしょうか?片方の設定がもう片方を上書きするのか、それとも両方が考慮されるのか。
今回は、この疑問を解消すべく、実際のApache設定ファイルを使って検証を行いました。
検証対象のApache設定
※ドメインとIPアドレスは例示用のものに変換しています。
以下が今回の検証の元となる設定ファイルです。一つのVirtualHost
にtest.example.com
(公開用)とdev-test.example.com
(開発用)という二つのドメインが割り当てられています。
この設定には、2種類のIP制限が記述されています。
-
ドキュメントルートへのIP制御 (
Order deny,allow
): ディレクトリ/var/www/html/test-site
全体に対して、特定のIP (192.0.2.1
と127.0.0.1
) のみを許可する設定です。 -
HostヘッダーごとのIP制御 (
SetEnvIf
とRequire
):-
test.example.com
へのアクセスは、環境変数IS_PUBLIC_HOST
が設定され、IPに関係なく全員に許可されます。 -
dev-test.example.com
へのアクセスは、環境変数IS_INTERNAL_HOST
が設定され、指定されたIPのみが許可されます。
-
<VirtualHost *:80>
ServerName test.example.com
ServerAlias dev-test.example.com
DocumentRoot "/var/www/html/test-site"
ErrorLog "logs/test-site-error.log"
CustomLog "logs/test-site-access.log" combined
<Directory "/var/www/html/test-site">
AllowOverride None
Options Indexes FollowSymLinks
# ① ドキュメントルートへのIP制御
Order deny,allow
Allow from 192.0.2.1/255.255.255.255
Allow from 127.0.0.1/255.255.255.255
Deny from all
# ② Host ヘッダーごとのIP制御
SetEnvIf Host "^test\.example\.com$" IS_PUBLIC_HOST
SetEnvIf Host "^dev-test\.example\.com$" IS_INTERNAL_HOST
<RequireAny>
# 条件A:「IS_PUBLIC_HOST」なら全員許可
Require env IS_PUBLIC_HOST
# 条件B:「IS_INTERNAL_HOST」かつ IPが一致すれば許可
<RequireAll>
Require env IS_INTERNAL_HOST
<RequireAny>
Require ip 192.0.2.1
Require ip 127.0.0.1
</RequireAny>
</RequireAll>
</RequireAny>
</Directory>
</VirtualHost>
この設定では、test.example.com
は全員に許可しつつ、ディレクトリ全体ではIPを制限するという矛盾した状態になっています。この重複がどう影響するのかを見ていきましょう。
検証1:HostヘッダーごとのIP制限のみでテスト
まず、本来の挙動を確認するため、①のドキュメントルートへのIP制御をコメントアウトし、②のHostヘッダーごとのIP制御のみが有効な状態でテストします。
期待される挙動
-
test.example.com
: どのIPアドレスからでもアクセスできる (200 OK)。 -
dev-test.example.com
: 許可されたIP (192.0.2.1
) からのみアクセスでき、それ以外のIPからはアクセスが拒否される (403 Forbidden)。
実行結果
許可されたIP (192.0.2.1
) からのアクセス
# 現在のグローバルIPを確認
$ curl ifconfig.me
192.0.2.1
# 公開用ドメインにアクセス
$ curl -I test.example.com
HTTP/1.1 200 OK
...
# 開発用ドメインにアクセス
$ curl -I dev-test.example.com
HTTP/1.1 200 OK
...
許可されていないIP (192.0.2.2
) からのアクセス
# 現在のグローバルIPを確認
$ curl ifconfig.me
192.0.2.2
# 公開用ドメインにアクセス
$ curl -I test.example.com
HTTP/1.1 200 OK
...
# 開発用ドメインにアクセス
$ curl -I dev-test.example.com
HTTP/1.1 403 Forbidden
...
結果は期待通りでした。Hostヘッダーによる制御が正しく機能し、test.example.com
はオープンに、dev-test.example.com
はIP制限がかかっていることが確認できました。
検証2:ドキュメントルートとHostヘッダーのIP制限を両方有効にしてテスト
次に、最初の設定に戻し、①と②の両方のIP制限が有効な状態でテストします。
期待される挙動
Apacheの評価順序から、まずディレクトリ全体にかかっている①のOrder deny,allow
が評価されると推測されます。この時点で許可されていないIPは弾かれるため、②のRequire env IS_PUBLIC_HOST
(全員許可)という条件は評価される前にアクセスが拒否されるはずです。
つまり、どのドメインにアクセスしても、許可されたIPからしかアクセスできないと予想されます。
実行結果
許可されたIP (192.0.2.1
) からのアクセス
# 現在のグローバルIPを確認
$ curl ifconfig.me
192.0.2.1
# 公開用ドメインにアクセス
$ curl -I test.example.com
HTTP/1.1 200 OK
...
# 開発用ドメインにアクセス
$ curl -I dev-test.example.com
HTTP/1.1 200 OK
...
許可されていないIP (192.0.2.2
) からのアクセス
# 現在のグローバルIPを確認
$ curl ifconfig.me
192.0.2.2
# 公開用ドメインにアクセス
$ curl -I test.example.com
HTTP/1.1 403 Forbidden
...
# 開発用ドメインにアクセス
$ curl -I dev-test.example.com
HTTP/1.1 403 Forbidden
...
予想通り、許可されていないIPからのアクセスは、test.example.com
(全員許可のはず)であっても403 Forbiddenとなりました。これは、より広い範囲(ディレクトリ全体)に適用されるOrder deny,allow
の制限が優先されたためです。
結論
今回の検証から、<Directory>
ディレクティブでIP制限をかけた場合、その中でHostヘッダーごとにIP制限を緩和するような設定(全員を許可するなど)を記述しても、それは無効になるということが分かりました。
つまり、最初の設定ファイルにあった二つのIP制限の記述は重複しており、実質的にはドキュメントルートへのIP制限のみが機能している状態でした。
Hostヘッダーごとの制御(特に公開用ドメインを全員に許可する部分)は意図通りに動作しないことが確認できます。
参考になったら幸いです。