ELB配下のApacheでのアクセス制御

2014.04.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。望月です。
Apacheやnginxとアプリケーションサーバを組み合わせてシステムを構成するのは、オンプレでもAWSでもよくあるパターンです。AWSでそのシステムを実現しようとすると、ELB + EC2という構成が定石ですが、ELBがWebサーバのフロントに立つことによりEC2単独で動作させる時と比較すると挙動が異なるところもあります。
例えばWebサーバから見たアクセス元のIPアドレスが変わってしまうことです。本来の送信元IPアドレスがロードバランサのプライベートIPアドレスに変わり、本来の送信元IPアドレスはX-Forwarded-Forヘッダの末尾へと格納されます。X-Forwarded-ForはRFCに記載された正式な仕様ではありませんが、プロキシやロードバランサでは一般的に利用されるデファクトスタンダードとなっています。

ELB配下でのアクセス制御

さて、以下のようなWebサイトの例を考えてみましょう。同一ホスト名(site.example.com)以下にサイトAとサイトBが存在しますが、サイトAはグローバルに公開されているのに対して、サイトBは限られたIPアドレスからしかアクセスが出来ない状態を実現したいとします。

Untitled

サイトAとサイトBに別のホスト名が割り当てられているのであれば、ELBを2つ用意し、サイトA用のELBは全てのIPアドレスからのアクセスを許可し、サイトBは特定のIPアドレスからのアクセスを許可するセキュリティグループをそれぞれに設定することで簡単に実現可能ですが、この例のように単一ホスト名の中で複数サイトに対する別のアクセス制御を実現したい場合は、利用するELBは一つとなります。このELBに設定するセキュリティグループは、内部のアプリケーションの緩い方の要件に合わせなければならないため、セキュリティグループはHTTP全開放で設定しておく必要があります。

ここはApacheでアクセス制御を実施しましょう。クライアントからのリクエストを直接EC2が受け付ける形であればRequire ip 192.168.x.yのように書けば、特定のIPアドレスのアクセス制限を実現可能です。 *1

しかし、上の例で示したIPアドレスは送信元のIPアドレスを示すため、ELB + Apacheの環境ではELBのプライベートIPアドレスが送信元IPアドレスとして認識されるので、この通りではいけません。ELBのIPアドレスを許可してしまうと、結果的にELBを介した全クライアントからのアクセスを許可することになってしまいます。そこで、先ほど紹介したX-Forwarded-Forを参照して許可させるようにします。

では、特定のIPアドレスからのアクセスを許可する設定例を以下に示しておきます。Apache2.2ではmod_setenvifを利用して、以下の様な形で利用します。

<Location /site_a>
  # 全てのIPアドレスからのアクセスを許可
  Order Allow,Deny
  Allow from all
</Location>

<Location /site_b>
  SetEnvIf X-Forwarded-For "192\.168\.0\.0.*" allowed_ip

  # 許可されたIPアドレスからのアクセスのみ許可する
  Order Deny,Allow
  Deny from all
  Allow from env=allowed_ip
</Location>

次にApache2.4での設定は以下のようになります。Apache2.4ではmod_remoteipというモジュールが用意されており、Amazon Linuxのhttpd24パッケージではデフォルトで有効になっています。このモジュールの提供するRemoteIPHeaderディレクティブは、X-Forwarded-Forヘッダの内容を確認して、その値を送信元IPアドレスとしてApacheに認識させます。この機能を利用すると、アクセス制限をかなり単純な形で書き直すことができます。

# 送信元IPアドレスとして、X-Forwarded-Forを利用する
RemoteIPHeader X-Forwarded-For

<Location /site_a>
  # 全てのIPアドレスからのアクセスを許可
  Require all granted
</Location>

<Location /site_b>
  # 許可されたIPアドレスからのアクセスのみ許可する
  Require ip 192.168.0.0
</Location>

RemoteIPHeaderは、LocationやDirectory等のディレクティブの中ではなく、グローバルコンテキストかVirtualHostの中に書く必要があります。送信元IPアドレスを自然に扱えるようになっているので、Apache2.4で送信元IPアドレスに対するアクセス制限を利用したい場合はこの書き方をするのが良さそうです。

脚注

  1. Apache2.4では、以前から利用可能であったOrder, Allow, Denyディレクティブはdeprecatedとなっています。ドキュメントを参照下さい。