Application Load Balancerでパス毎にIPアドレスを制限する方法をまとめてみた

ALBのリスナールールで頑張りすぎると管理が大変なので、個人的にはAWS WAFをオススメします。

こんにちは、AWS事業本部の荒平(@0Air)です。

最近、Application Load Balancerでパス毎にIP制限を掛けたいという要望を受けました。
アプリケーション側で処理できるなら手っ取り早いですが、難しいケースもあるかもしれません。

Developers IOの記事でもいくつか制限を掛ける方法は紹介されており、ページ下部の参考資料に記載しています。
ただ、方法が色々あるために混乱しそうになったので、一旦まとめることにしました。

概要

そもそも、Application Load Balancer(以下、ALB)にIPアドレス単位でのアクセス制限を行う場合は、主に3つの手段が存在します。
※ API GatewayやNLBなどを組み合わせることでも可能ですが、構成が複雑になるため今回は除外しています

  • ALB + Security Group
    • ALBにアタッチされているセキュリティグループで許可IPを設定する方法
    • 不許可IPを設定することはできない
    • 特定のパスにのみ設定はできない(全てのパスで適用される)
  • ALB リスナールール
    • ALBのリスナールールを作成し、送信元IPを許可設定する方法
    • 不許可IPを設定することはできない
    • 特定のパスを指定することができる
    • パスルールは細かい指定が難しい
  • ALB + AWS WAF
    • ALBにAWS WAFをアタッチし、IP setで許可・不許可設定する方法
    • 不許可IPを設定できる
    • 特定のパスを指定することができる
    • パスルールは細かい指定が可能

まとめると以下のような表になります。

項目 ALB + Security Group ALB リスナールール ALB + AWS WAF
全てのパス
許可IPアドレスの設定
(ホワイトリスト)
特定のパスのみ
許可IPアドレスの設定
不許可IPアドレスの設定
(ブラックリスト)

本エントリのゴール

本エントリでお伝えしたい内容は以上ですが、せっかくなのであまり検索でヒットしないパターンを検証します。条件は次の通りです。

  1. 特定パスのみインターネット公開(制限なし)
  2. 上記の特定パス以外はIPアドレスを制限

この2つの条件をクリアできるように進めます。
なお、この条件とは逆で、「特定パスのみIPアドレス制限、それ以外は制限なし」のケースはリスナールールのみで実現可能なため、以下の記事をご参照ください。

ただし、ALBはリスナールール1つにつき、IPアドレスの登録上限は5つ(パス条件とANDする場合は4つ)のため、多くのIPアドレス制限を掛ける場合はAWS WAFを用いたほうが管理上良いと考えます。

検証してみた

構成図

許可されているIPアドレスはWAF, ALBを経由してEC2の全てのパスにアクセスできます。
許可されていないIPアドレスは、例として/Page-A にのみアクセスを許可するようにしました。

(1) 検証用のEC2, ALBを作成

検証用のEC2, ALBを作成しました。詳細は割愛しますが、EC2のユーザーデータは以下のように記述しました。

#!/bin/bash

# Apacheのインストール
yum update -y
yum install -y httpd

# サービスの起動と自動起動の設定
systemctl start httpd
systemctl enable httpd

# ディレクトリの作成
mkdir -p /var/www/html/page-a
mkdir -p /var/www/html/page-b
mkdir -p /var/www/html/page-c

# HTMLファイルの作成
echo "Hello World! Page-A" > /var/www/html/page-a/index.html
echo "Hello World! Page-B" > /var/www/html/page-b/index.html
echo "Hello World! Page-C" > /var/www/html/page-c/index.html

# Apacheの設定更新
cat <<EOL > /etc/httpd/conf.d/pages.conf
<Directory "/var/www/html/page-a">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

<Directory "/var/www/html/page-b">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

<Directory "/var/www/html/page-c">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>
EOL

# Apacheの再起動
systemctl restart httpd

何も設定していない状態では、Page-A, Page-B, Page-Cのパスにそれぞれアクセスできる状態です。

http://test-arap-xxx.ap-northeast-1.elb.amazonaws.com/page-a/  
http://test-arap-xxx.ap-northeast-1.elb.amazonaws.com/page-b/  
http://test-arap-xxx.ap-northeast-1.elb.amazonaws.com/page-c/

今回の条件は「①特定パスのみインターネット公開(制限なし)」「②特定パス以外はIPアドレス制限」なので、Page-Aを公開したまま、Page-A以外をIPアドレス制限します。
※ この例では、「Page-A以外」は「Page-B, C」と判明していますが、これは本来不明であるものとします。

(2) AWS WAFのWeb ACLを作成・ALBにアタッチ

AWS WAFの画面でWeb ACLを作成し、テスト用のALBにアタッチします。
このとき、ルールはALBと同一のリージョンに作成します。

(3) 必要なルールを設定

WebACLにルールを設定します。今回の条件で必要なのは、以下3つのルールです。

  1. リクエストURIパスが /Page-A から始まる場合は、/Page-A へのアクセスを許可
  2. リクエストURIパスが/Page-A 以外、かつ、IPアドレスが事前定義のIP Setに一致する場合は許可
  3. 上記に一致しない場合はブロック(拒否)

まずは、リクエストURIパスが Page-A にマッチした場合のルールを作成します。

項目
Name 任意
Type Regular rule
Inspect URI Path
Match type Starts with string
String to match /page-a
Action Allow

続いて、/Page-A 以外のアクセスにIP一致を求めるルールを作成します。

項目
Name 任意
Type Regular rule
If a request matches all the statements (AND)
Statement 1 -
Inspect Originates from an IP address in
IP set 許可するIP Set
IP address to use as the originating address Source IP address
NOT Statement 2 -
Inspect URI Path
Match type Starts with string
String to match /page-a
Action Allow

最後に、どのルールにも一致しないリクエストをブロックする設定にします。

(4) 動作検証

まずは許可IPアドレスからアクセスを行ったところ、想定通り3URLともにアクセスできました。

$ curl http://test-arap-XXX.ap-northeast-1.elb.amazonaws.com/page-a/
Hello World! Page-A

$ curl http://test-arap-XXX.ap-northeast-1.elb.amazonaws.com/page-b/
Hello World! Page-B

$ curl http://test-arap-XXX.ap-northeast-1.elb.amazonaws.com/page-c/
Hello World! Page-C

続いて許可IPアドレス外からアクセスを行ったところ、こちらも想定通り、/Page-A のみアクセスでき、それ以外のページ(/Page-B, /Page-C)へはアクセスできませんでした。

$ curl http://test-arap-XXX.ap-northeast-1.elb.amazonaws.com/page-a/
Hello World! Page-A

$ curl http://test-arap-XXX.ap-northeast-1.elb.amazonaws.com/page-b/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

$ curl http://test-arap-XXX.ap-northeast-1.elb.amazonaws.com/page-c/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

おわりに

前述の通り、ALBはリスナールール1つにつき、IPアドレスの登録上限は5つ(パス条件とANDする場合は4つ)のため、多くのIPアドレス制限を掛ける場合は、AWS WAFを用いましょう。
(リスナールールを複数作成すれば、この上限は関係ないのですが、構成がカオスになりやすくなります)

また、WAFではNOTステートメントがあるため、ブラックリスト型の制御を行いやすいのが利点です。
料金との兼ね合いもありますが、要件に応じて、適切なアクセス制限方法を選ぶことをおすすめします。

このエントリが誰かの助けになれば幸いです。

それでは、AWS事業本部 コンサルティング部の荒平(@0Air)がお送りしました!

参考