現状への不満
Apacheを使って接続元のIPアドレスを制限したい場合、Requireディレクティブの承認プロバイダipを使って、IPアドレスを記述する場合が多いと思います。
<Directory "/var/www/html/dirA"> <RequireAny> Require ip 192.168.0.1 192.168.0.100 Require ip 192.168.3.0/24 </RequireAny> </Directory>
シンプルで直感的にわかりやすい記述方式なので、私もApache2.4では基本的にこの方式でアクセス制限していました。
しかし運用しているシステムによっては、だんだんと不便さを感じるようになりました。
複数のURLパスに対して、それぞれの関係者や連携システムからのみアクセスできるように制限しているシステムでは、URLパスごとに<Directory>や<Location>を記述し、それぞれに許可するIPアドレスを列挙することになります。
たいていの場合は、ひとつの連携先の接続元として複数のIPアドレスを登録する必要がありますし、異なるURLパスに同じような連携先リストを登録する場合には、それぞれに冗長なIPアドレスのリストを記述する必要があります。
こうして対象のURLパスや連携先が増えてくると、設定ファイルはIPアドレスで溢れていきます。
この状態では、可読性が落ちますし、IPアドレスの変更や追加をするときに修正が必要な箇所も増えてしまいます。
そこで、関係者や連携システムのIPアドレスを管理する記述と、URLパスへのアクセス制限を行う記述とを分離して、効率よく管理する方法を紹介します。
アクセス許可に環境変数を利用する
まずはどのような記述になるかをシンプルな設定例で紹介します
# allow ip list SetEnvIfExpr "-R '192.168.0.1/32'" allow_ip SetEnvIfExpr "-R '192.168.0.100/32'" allow_ip SetEnvIfExpr "-R '192.168.3.0/24'" allow_ip <Directory "/var/www/html/dirA"> Require env allow_ip </Directory>
Requireディレクティブのenvという承認プロバイダを利用すると、環境変数を条件にアクセス制限できます。
接続元IPアドレスを条件にして環境変数をセットすることで、<Directory>や<Location>には環境変数で抽象化された接続先を記述できます。
SetEnvIfExprディレクティブはmod_setenvifモジュールで提供されています。
環境変数をセットする条件に式(Expressions)を利用できるので、柔軟に条件を表現できます。
接続元IPアドレスを条件にするために"-R 'address/netmask'"という式が用意されているので、CIDR形式でIPアドレスを扱うことができます。
mod_setenvif - Apache HTTP Server Version 2.4
Expressions in Apache HTTP Server - Apache HTTP Server Version 2.4
関係者や連携システムごとに環境変数をセットする
続いて、複数のURLパスに対し、それぞれ異なる関係者や連携システムからのアクセスを許可する場合の記述方法を紹介します。
さきほどの設定例では、allow_ipという単一の環境変数を利用していました。
連携先ごとにそれぞれの環境変数名をセットすることで、アクセス制限の設定では、接続元を抽象化した論理グループとして表現できます。
IPアドレス環境変数設定
## Local NW SetEnvIfExpr "-R '192.168.0.0/24' \ || -R '192.168.128.0/24' \ " allow_ip_local_nw ## My Office SetEnvIfExpr "-R '172.16.32.0/24' \ || -R '192.168.10.10/32' \ " allow_ip_my_office ## System A SetEnvIfExpr "-R '192.168.2.100/32' \ || -R '192.168.34.100/32' \ || -R '10.40.0.0/24' \ || -R '172.16.40.40/32' \ " allow_ip_system_A ## System B SetEnvIfExpr "-R '10.20.0.0/24' \ || -R '10.20.10.5/32' \ " allow_ip_system_B
アクセス制限設定
<Directory "/var/www/html/private"> <RequireAny> Require env allow_ip_local_nw allow_ip_my_office </RequireAny> </Directory> <Directory "/var/www/html/dirA"> <RequireAny> Require env allow_ip_local_nw allow_ip_my_office Require env allow_ip_system_A </RequireAny> </Directory> <Directory "/var/www/html/dirB"> <RequireAny> Require env allow_ip_local_nw allow_ip_my_office Require env allow_ip_system_B </RequireAny> </Directory>
このように、IPアドレスを管理する記述と、アクセス制限を管理する記述を分離することができました。
他のディレクティブでも接続元IPの環境変数を利用する
接続元を環境変数として定義することで、Requireディレクティブ以外でもその変数を再利用できるというメリットがあります。
具体的な利用例として、メンテナンス画面を表示するためのリダイレクト設定を、特定の接続元からは解除したい場合の設定を紹介します。
RewriteEngine on RewriteCond %{ENV:allow_ip_my_office} !^1$ RewriteCond %{REQUEST_URI} !^/error/.* [NC] RewriteRule ^ - [R=503,L] ErrorDocument 503 /error/maintenance.html
リダイレクト設定以外にも、ディレクティブの多くは、環境変数による出し分けをサポートしているので、
接続元IPアドレスを条件に動作を制御したい場合、設定箇所それぞれで毎回IPアドレスを記述しなくてもよくなります。
別記述パターン:環境変数に値をセットして式で判定する
先の設定例では、SetEnvIfExprで連携先ごとに異なる環境変数名を利用していました。
環境変数の使い方として、連携先の識別子を値として代入して使う記述方法もあります。
この場合は、アクセス制限に使う環境変数をallow_ipに固定して、Require exprで値を判別することになります。
## System A SetEnvIfExpr "-R '192.168.3.0/24'" allow_ip=system_A ## System B SetEnvIfExpr "-R '192.168.4.0/24'" allow_ip=system_B <Directory "/var/www/html/dirA"> Require expr "reqenv('allow_ip') == 'system_A'" </Directory>
環境変数の使い方としてはこちらのほうがしっくりくるかもしれません。
注意点は、複数のSetEnvIfExprで条件が重複した場合、allow_ipの値は、後勝ちになることです。
上記例でいうと、System AとSystem Bのネットワーク帯域に重複するIPアドレスがある場合、そのIPからのリクエストはallow_ip=system_Bがセットされた状態になります。
また、他のディレクティブでもこの環境変数を流用する場合にも注意が必要です。
ディレクティブによっては、環境変数の値まで参照できないものも多いです。
その場合は、<if>ディレクティブを駆使して記述していくことになります。
<if>ディレクティブはほかのディレクティブとの内部処理順序の問題で、想定する動作とならないリスクもあることに注意が必要です。
まとめ
本記事では、SetEnvIfExprを活用した、IPアドレス制限を環境変数で管理する方法を紹介しました。
IPアドレスの定義とアクセス権限の記述を分離することには、以下のメリットがあります。
- 可読性の向上:羅列された数値ではなく、意味のある名前(環境変数)で設定を記述できる
- 管理の効率化:複数のパスで利用されるIPリストを一元管理できる
- 設定ミスの防止:修正箇所が集約されるため、変更時の記述漏れやミスを防げる
複雑になりがちなアクセス制限の設定をスッキリさせたい場合は、ぜひこの方法を試してみてください。