特定リージョンの特定 AWS サービスが取りうる IP レンジをまとめてマネージドプレフィックスリストに登録してみた

while でがんばったりしましたが、 jq でやった方が圧倒的に楽でした。

コンバンハ、千葉(幸)です。

突然ですが皆さんは東京リージョンの EC2 が取りうるパブリック IP アドレスの範囲を教えて、と言われたら調べられるでしょうか。私は調べられます。

そして調べたからにはその結果をマネージドプレフィックスリストに登録したくなるのではないでしょうか。私はなります。

ということで、なるべく複雑なことをせずに実現してみました。結果的に割と力技で解決することになりました。

AWS サービスの IP レンジを確認する

以下ページからダウンロードできるip-ranges.jsonを用いて確認していきます。

ここ から直接内容を確認することもできますが、量が膨大でなかなか大変です。よって、すでにip-ranges.jsonをダウンロード済みで、jqを使用できる環境での実行を前提に進めていきます。

ip-ranges.json の構文

33000 行を超える膨大なボリュームですが、構文は以下のように比較的シンプルです。

{
  "syncToken": "0123456789",  #UNIX エポック時刻形式での公開時刻
  "createDate": "yyyy-mm-dd-hh-mm-ss",  #公開日時(UTC)
  "prefixes": [
    {
      "ip_prefix": "cidr",
      "region": "region",  #AWS リージョンまたは GLOBAL
      "network_border_group": "network_border_group"  #AWS がパブリック IP アドレスをアドバタイズするアベイラビリティーゾーンまたはローカルゾーンの一意のセット
      "service": "subset"
    }
  ],
  "ipv6_prefixes": [
    {
      "ipv6_prefix": "cidr",
      "region": "region",
      "network_border_group": "network_border_group",
      "service": "subset"
    }
  ]  
}

作成日時を確認する

IP レンジは変動しうるものですので、いつ時点の情報かは意識するようにしましょう。

% jq .createDate < ip-ranges.json
"2021-06-11-03-44-15"

特定リージョンにおける特定サービスのレンジを確認する

例えば東京リージョンの EC2 であれば、以下のように指定することで確認できます。select(.region=="ap-northeast-1" and .service=="EC2")により絞り込みを行なっています。

% jq -r '.prefixes[] | select(.region=="ap-northeast-1" and .service=="EC2") | .ip_prefix' < ip-ranges.json | sort -n
3.112.0.0/14
3.5.152.0/21
13.112.0.0/14
13.230.0.0/15
15.177.79.0/24
15.193.1.0/24
18.176.0.0/15
18.178.0.0/16
18.179.0.0/16
18.180.0.0/15
18.182.0.0/16
18.183.0.0/16
35.72.0.0/13
46.51.224.0/19
52.192.0.0/15
52.194.0.0/15
52.196.0.0/14
52.68.0.0/15
52.94.248.80/28
52.95.243.0/24
52.95.255.48/28
54.150.0.0/16
54.168.0.0/16
54.178.0.0/16
54.199.0.0/16
54.238.0.0/16
54.248.0.0/15
54.250.0.0/16
54.64.0.0/15
54.92.0.0/17
54.95.0.0/16
64.252.110.0/24
64.252.111.0/24
64.252.112.0/24
64.252.113.0/24
99.150.48.0/21
99.77.139.0/24
99.77.160.0/24
103.4.8.0/21
175.41.192.0/18
176.32.64.0/19
176.34.0.0/19
176.34.32.0/19

EC2と言いつつ、 Lambda 関数や Systems Manager オートメーションの実行環境が取りうる IP レンジもここに含まれます。

サービスコードとして指定できるのは、2021/06時点で以下の通りです。

  • AMAZON
  • AMAZON_APPFLOW
  • AMAZON_CONNECT
  • API_GATEWAY
  • CHIME_MEETINGS
  • CHIME_VOICECONNECTOR
  • CLOUD9
  • CLOUDFRONT
  • CODEBUILD
  • DYNAMODB
  • EBS
  • EC2
  • EC2_INSTANCE_CONNECT
  • GLOBALACCELERATOR
  • KINESIS_VIDEO_STREAMS
  • ROUTE53
  • ROUTE53_HEALTHCHECKS
  • S3
  • WORKSPACES_GATEWAYS

AMAZONはすべてのサービスコードの IP プレフィックスを内包しています。そして、AMAZONにしか含まれない IP プレフィックスもあります。

マネージドプレフィックスリストとは

マネージドプレフィックスリストは一つ以上の CIDR ブロックのセットであり、SecuriryGroup やルートテーブルのエントリとして使用できます。

例えば先ほど確認した東京リージョンの EC2 の IP レンジを SecuriryGroup に登録したいとなった場合、一つずつ個別に追加していくのは手間です。プロトコルやポートをそれぞれ指定しなければならないですし、SecuriryGroup が複数存在する場合には同じことを何度も繰り返さなければいけません。

マネージドプレフィックスリストを使えば、複数の CIDR ブロックを一つのルールとして定義できますし、IP レンジに変更が加わったなどのメンテナンスの際も修正箇所が限定されます。

デフォルトで AWS マネージドなプレフィックスリストが用意されており、各リージョンの S3 と DynamoDB のサービス IP レンジが定義されています。ゲートウェイ型の VPC エンドポイントを使用する際によくお世話になります。

詳細は以下を参照してください。

やってみた

今回は以下の AWS CLI コマンドを使用しカスタマー管理のプレフィックスリストを新規作成します。

プレフィックスリストに登録するエントリは、上で確認した東京リージョンの EC2 の CIDR ブロックです。

コマンド実行の際に付与するオプションは以下のとおりです。

aws ec2 create-managed-prefix-list\
  --prefix-list-name <value>\  # プレフィックスリスト名。必須
  --max-entries <value>\  # 最大エントリ数。必須
  --address-family <value>\  # IPv4 or IPv6 の指定。必須
  --entries <value>  # エントリの内訳。省略可

いくつかオプションについて補足します。

最大エントリ数

マネージドプレフィックスリスト作成時に指定する必要があり、後から変更はできません

マネージドプレフィックスリストを SecuriryGroup やルートテーブルで使用する際には、(実際のエントリ数にかかわらず)ここで指定した最大エントリ数の分、それぞれの枠が消費されます。

例えばデフォルトでの上限はそれぞれ以下です。

  • SecuriryGroup のルール数:60
  • ルートテーブルのルート数:50

最大エントリ数が 30 のマネージドプレフィックスリストを作成し適用した場合、リスト内の実際のエントリ数に関わらず、上記の枠のうち 30 を消費します。

多すぎると無駄に枠を消費しますし、少なすぎると将来的なエントリの追加に対応できません。適切な値を検討し、決定しましょう。

今回はip-ranges.jsonで確認した CIDR ブロック数と同一の値を指定します。

エントリ

指定の仕方は二種類あります。

簡略構文

Cidr=xx.xx.xx.xx/24,Description=string Cidr=yy.yy.yy.yy/16,Description=string ...

JSON 構文

[
  {
    "Cidr": "xx.xx.xx.xx/24",
    "Description": "string"
  },
  {
    "Cidr": "yy.yy.yy.yy/16",
    "Description": "string"
  }
  ...
]

今回は前者の簡略構文で指定します。

jq によって出力された CIDR ブロックのリストをどうにかこの構文に当てはめればいいのですが……今回は以下の形で実現しました。

# 事前に変数 PREFIXLIST に CIDR ブロックのリストが格納されているとして
$ echo $PREFIXLIST | while read i ; do ENTRIES+="Cidr=$i,Description=EC2-ap-northeast-1 " ; done

上記の結果、変数ENTRIESには以下が格納されます。力技ですね。

% echo $ENTRIES
Cidr=3.112.0.0/14,Description=EC2-ap-northeast-1 Cidr=3.5.152.0/21,Description=EC2-ap-northeast-1 Cidr=13.112.0.0/14,Description=EC2-ap-northeast-1 Cidr=13.230.0.0/15,Description=EC2-ap-northeast-1 Cidr=15.177.79.0/24,Description=EC2-ap-northeast-1 Cidr=15.193.1.0/24,Description=EC2-ap-northeast-1 Cidr=18.176.0.0/15,Description=EC2-ap-northeast-1 Cidr=18.178.0.0/16,Description=EC2-ap-northeast-1 Cidr=18.179.0.0/16,Description=EC2-ap-northeast-1 Cidr=18.180.0.0/15,Description=EC2-ap-northeast-1 Cidr=18.182.0.0/16,Description=EC2-ap-northeast-1 Cidr=18.183.0.0/16,Description=EC2-ap-northeast-1 Cidr=35.72.0.0/13,Description=EC2-ap-northeast-1 Cidr=46.51.224.0/19,Description=EC2-ap-northeast-1 Cidr=52.192.0.0/15,Description=EC2-ap-northeast-1 Cidr=52.194.0.0/15,Description=EC2-ap-northeast-1 Cidr=52.196.0.0/14,Description=EC2-ap-northeast-1 Cidr=52.68.0.0/15,Description=EC2-ap-northeast-1 Cidr=52.94.248.80/28,Description=EC2-ap-northeast-1 Cidr=52.95.243.0/24,Description=EC2-ap-northeast-1 Cidr=52.95.255.48/28,Description=EC2-ap-northeast-1 Cidr=54.150.0.0/16,Description=EC2-ap-northeast-1 Cidr=54.168.0.0/16,Description=EC2-ap-northeast-1 Cidr=54.178.0.0/16,Description=EC2-ap-northeast-1 Cidr=54.199.0.0/16,Description=EC2-ap-northeast-1 Cidr=54.238.0.0/16,Description=EC2-ap-northeast-1 Cidr=54.248.0.0/15,Description=EC2-ap-northeast-1 Cidr=54.250.0.0/16,Description=EC2-ap-northeast-1 Cidr=54.64.0.0/15,Description=EC2-ap-northeast-1 Cidr=54.92.0.0/17,Description=EC2-ap-northeast-1 Cidr=54.95.0.0/16,Description=EC2-ap-northeast-1 Cidr=64.252.110.0/24,Description=EC2-ap-northeast-1 Cidr=64.252.111.0/24,Description=EC2-ap-northeast-1 Cidr=64.252.112.0/24,Description=EC2-ap-northeast-1 Cidr=64.252.113.0/24,Description=EC2-ap-northeast-1 Cidr=99.150.48.0/21,Description=EC2-ap-northeast-1 Cidr=99.77.139.0/24,Description=EC2-ap-northeast-1 Cidr=99.77.160.0/24,Description=EC2-ap-northeast-1 Cidr=103.4.8.0/21,Description=EC2-ap-northeast-1 Cidr=175.41.192.0/18,Description=EC2-ap-northeast-1 Cidr=176.32.64.0/19,Description=EC2-ap-northeast-1 Cidr=176.34.0.0/19,Description=EC2-ap-northeast-1 Cidr=176.34.32.0/19,Description=EC2-ap-northeast-1

末尾に余計な半角スペースが入りますが、まぁ動作には影響がないのでご愛嬌です。出来上がりのパラメータにも影響しません。

aws ec2 create-managed-prefix-list の実行

変数の格納から一連の流れで実行していきます。

まずはip-ranges.jsonから東京リージョンの EC2 のプレフィックスのリストを取得します。

% PREFIXLIST=`jq -r '.prefixes[] | select(.region=="ap-northeast-1" and .service=="EC2") | .ip_prefix' < ip-ranges.json | sort -n`

取得したリストの行数を変数に格納します。

% MAXENTRIES=`echo $PREFIXLIST | wc -l | tr -d " "`

以下のオプションを指定したコマンドを実行します。

% aws ec2 create-managed-prefix-list\
  --prefix-list-name EC2-ap-northeast-1\
  --max-entries $MAXENTRIES\
  --address-family IPv4\
  --entries `echo $PREFIXLIST | while read i ; do ENTRIES+="Cidr=$i,Description=EC2-ap-northeast-1 " ; done && echo $ENTRIES`
{
    "PrefixList": {
        "PrefixListId": "pl-0b3236f7ca125c242",
        "AddressFamily": "IPv4",
        "State": "create-in-progress",
        "PrefixListArn": "arn:aws:ec2:ap-northeast-1:012345678910:prefix-list/pl-0b3236f7ca125c242",
        "PrefixListName": "EC2-ap-northeast-1",
        "MaxEntries": 43,
        "Version": 1,
        "Tags": [],
        "OwnerId": "012345678910"
    }
}

この通り正常に作成されています。

CLI でも確認してみます。

% PREFIX_LIST_ID=pl-0b3236f7ca125c242
% aws ec2 get-managed-prefix-list-entries --prefix-list-id $PREFIX_LIST_ID --output yaml
Entries:
- Cidr: 3.112.0.0/14
  Description: EC2-ap-northeast-1
- Cidr: 3.5.152.0/21
  Description: EC2-ap-northeast-1
- Cidr: 13.112.0.0/14
  Description: EC2-ap-northeast-1
- Cidr: 13.230.0.0/15
  Description: EC2-ap-northeast-1
- Cidr: 15.177.79.0/24
  Description: EC2-ap-northeast-1
---略---
- Cidr: 99.150.48.0/21
  Description: EC2-ap-northeast-1
- Cidr: 99.77.139.0/24
  Description: EC2-ap-northeast-1
- Cidr: 99.77.160.0/24
  Description: EC2-ap-northeast-1
- Cidr: 103.4.8.0/21
  Description: EC2-ap-northeast-1
- Cidr: 175.41.192.0/18
  Description: EC2-ap-northeast-1
- Cidr: 176.32.64.0/19
  Description: EC2-ap-northeast-1
- Cidr: 176.34.0.0/19
  Description: EC2-ap-northeast-1
- Cidr: 176.34.32.0/19
  Description: EC2-ap-northeast-1

問題なく登録できています。あとはこれを必要に応じて SecuriryGroup なりルートテーブルなりに関連づけを行うだけです。

余談:JSON 構文でのエントリの指定

一通り書き終わった後に気がつきましたが、以下のようにip-ranges.jsonから直接 JSON 構文にマッチするような出力をしていればもっとスマートでした。

% ENTRIES=`jq '.prefixes[] | select(.region=="ap-northeast-1" and .service=="EC2") | {Cidr:.ip_prefix,Description:"HOGE"} ' < ip-ranges.json | jq -s`
% echo $ENTRIES
[
  {
    "Cidr": "54.248.0.0/15",
    "Description": "HOGE"
  },
  {
    "Cidr": "54.250.0.0/16",
    "Description": "HOGE"
  },
  {
    "Cidr": "54.92.0.0/17",
    "Description": "HOGE"
  },
---略---
]

まぁ……遠回りしたからこそたどり着いたということで。はい。

終わりに

特定リージョンの特定 AWS サービスの IP レンジを AWS CLI でまとめてマネージドプレフィックスリストに登録してみました。

具体的なモチベーションとしては、Systems Manager オートメーションからのインバウンドを許可する SecuriryGroup を設定したい、というものがありました。

これは以下のエントリを書いた時に生じたものです。オートメーションの実行環境から EC2 への SSH 接続を一時的に満たす必要があり、ここでは 0.0.0.0/0 からのインバウンドを許可していました。

EC2 や Lambda なども引っくるめて許可することになるので完全にセキュアというわけではありませんが、フル開放に比べればだいぶ絞ることができました。

他にもサービスを限定して通信を許可したいケースはあるかと思いますので、どこかでお役に立てば幸いです。

以上、 チバユキ (@batchicchi) がお送りしました。

参考