AWS IPアドレス範囲ファイルをDuckDBで読み込んで、いろいろ見てみる

AWS IPアドレス範囲ファイルをDuckDBで読み込んで、いろいろ見てみる

Clock Icon2025.04.14

AWSサービスが使用するIPアドレスの範囲はJSON形式(ip-ranges.json)で公開されています。

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/aws-ip-ranges.html

  • オンプレミスのファイアウォール設定で「特定サービスへのアウトバウンド通信」に絞りたい
  • ログにあるIPアドレスがどのAWSサービス/リージョンのものか把握したい

といったユースケースで良く使われます。

今回は ip-ranges.json を DuckDB に読み込ませて、いろいろ見てみます。 DuckDBはオンライン分析処理(OLAP)に特化したデータベースシステムです。 CSVやParquetだけでなく、JSONも読み込むことができます。 拡張機能も豊富なため多いため、とても便利です。

DuckDBに取り入れる

DuckDBを起動します。

DuckDBを起動(今後作成するテーブルを ip-ranges.db に永続化)
duckdb ip-ranges.db
# v1.2.2 7c039464e4
# Enter ".help" for usage hints.
# D

以下コマンドで ip-ranges.json を取得できます。

---- デフォルトで自動ロードされるので基本的には実行不要
-- INSTALL httpfs;
-- LOAD httpfs;

SELECT * FROM read_json('https://ip-ranges.amazonaws.com/ip-ranges.json');
-- ┌────────────┬─────────────────────┬──────────────────────┬────────────────────────────────────────────────────────┐
-- │ syncToken  │     createDate      │       prefixes       │                     ipv6_prefixes                      │
-- │  varchar   │       varchar       │ struct(ip_prefix v…  │ struct(ipv6_prefix varchar, region varchar, service …  │
-- ├────────────┼─────────────────────┼──────────────────────┼────────────────────────────────────────────────────────┤
-- │ 1744415597 │ 2025-04-11-23-53-17 │ [{'ip_prefix': 3.4…  │ [{'ipv6_prefix': 2600:1f69:7400::/40, 'region': mx-c…  │
-- └────────────┴─────────────────────┴──────────────────────┴────────────────────────────────────────────────────────┘

今回は prefixes にある IPv4 アドレス範囲情報を見ていきます。 ipv4 テーブルとして保存しておきましょう。

CREATE TABLE ipv4 AS
  SELECT unnest(prefixes, recursive := true) FROM read_json('https://ip-ranges.amazonaws.com/ip-ranges.json');

SELECT * FROM ipv4 LIMIT 5;
-- ┌─────────────────┬────────────────┬─────────┬──────────────────────┐
-- │    ip_prefix    │     region     │ service │ network_border_group │
-- │     varchar     │    varchar     │ varchar │       varchar        │
-- ├─────────────────┼────────────────┼─────────┼──────────────────────┤
-- │ 3.4.12.4/32     │ eu-west-1      │ AMAZON  │ eu-west-1            │
-- │ 3.5.140.0/22    │ ap-northeast-2 │ AMAZON  │ ap-northeast-2       │
-- │ 15.190.244.0/22 │ ap-east-2      │ AMAZON  │ ap-east-2            │
-- │ 15.230.15.29/32 │ eu-central-1   │ AMAZON  │ eu-central-1         │
-- │ 15.230.15.76/31 │ eu-central-1   │ AMAZON  │ eu-central-1         │
-- └─────────────────┴────────────────┴─────────┴──────────────────────┘

unnest 関数の挙動は以下を見るとイメージできると思います。

SELECT [{'a': 42, 'b': 84}, {'a': 100, 'b': NULL}] as XXX;
-- ┌─────────────────────────────────────────────┐
-- │                     XXX                     │
-- │       struct(a integer, b integer)[]        │
-- ├─────────────────────────────────────────────┤
-- │ [{'a': 42, 'b': 84}, {'a': 100, 'b': NULL}] │
-- └─────────────────────────────────────────────┘

SELECT unnest([{'a': 42, 'b': 84}, {'a': 100, 'b': NULL}]) as XXX;
-- ┌──────────────────────────────┐
-- │             XXX              │
-- │ struct(a integer, b integer) │
-- ├──────────────────────────────┤
-- │ {'a': 42, 'b': 84}           │
-- │ {'a': 100, 'b': NULL}        │
-- └──────────────────────────────┘

SELECT unnest([{'a': 42, 'b': 84}, {'a': 100, 'b': NULL}], recursive := true);
-- ┌───────┬───────┐
-- │   a   │   b   │
-- │ int32 │ int32 │
-- ├───────┼───────┤
-- │    42 │    84 │
-- │   100 │  NULL │
-- └───────┴───────┘

統計いろいろ

サービス名の一覧

サービス名一覧(エントリ数TOP10)を出力します。

SELECT
  service,
  COUNT(*) AS count
FROM ipv4
GROUP BY service
ORDER BY count DESC
LIMIT 10;
-- ┌───────────────────────┬───────┐
-- │        service        │ count │
-- │        varchar        │ int64 │
-- ├───────────────────────┼───────┤
-- │ AMAZON                │  4995 │
-- │ EC2                   │  1312 │
-- │ ROUTE53_RESOLVER      │   638 │
-- │ S3                    │   325 │
-- │ API_GATEWAY           │   207 │
-- │ CLOUDFRONT            │   186 │
-- │ GLOBALACCELERATOR     │   108 │
-- │ EBS                   │    99 │
-- │ KINESIS_VIDEO_STREAMS │    91 │
-- │ DYNAMODB              │    87 │
-- ├───────────────────────┴───────┤
-- │ 10 rows             2 columns │
-- └───────────────────────────────┘

AMAZON は「すべての」IPアドレス範囲を含むものです。 他のIPアドレス範囲(EC2,S3など)もここに含まれます。 つまり、個別サービス単位だと EC2 のエントリ数が 最も多いことになりますね。

リージョン名の一覧

リージョン名(とそのエントリ数)一覧を出力します。 日本は 8位でした。

SELECT
  region,
  COUNT(*) AS count
FROM ipv4
GROUP BY region,
ORDER BY count DESC
LIMIT 20;
-- ┌────────────────┬───────┐
-- │     region     │ count │
-- │    varchar     │ int64 │
-- ├────────────────┼───────┤
-- │ us-east-1      │  1169 │
-- │ us-west-2      │   536 │
-- │ us-west-1      │   514 │
-- │ us-east-2      │   501 │
-- │ eu-central-1   │   497 │
-- │ eu-west-1      │   449 │
-- │ GLOBAL         │   400 │
-- │ ap-northeast-1 │   348 │
-- │ eu-west-2      │   345 │
-- │ ap-southeast-1 │   344 │
-- ├────────────────┴───────┤
-- │ 10 rows      2 columns │
-- └────────────────────────┘

使われているサブネットマスクの分布

使われているサブネットマスク(CIDR)の分布を可視化してみました。

SELECT
  split_part(ip_prefix, '/', 2) as subnet_mask,
  count(*) as count,
  bar(count(*), 0, 2100, 30)
FROM ipv4
GROUP BY subnet_mask
ORDER BY subnet_mask DESC;
-- ┌─────────────┬───────┬────────────────────────────────┐
-- │ subnet_mask │ count │ bar(count_star(), 0, 2100, 30) │
-- │   varchar   │ int64 │            varchar             │
-- ├─────────────┼───────┼────────────────────────────────┤
-- │ 32          │  1139 │ ████████████████▎              │
-- │ 31          │   490 │ ███████                        │
-- │ 30          │   179 │ ██▌                            │
-- │ 29          │   345 │ ████▉                          │
-- │ 28          │   351 │ █████                          │
-- │ 27          │   301 │ ████▎                          │
-- │ 26          │   509 │ ███████▎                       │
-- │ 25          │   220 │ ███▏                           │
-- │ 24          │  2033 │ █████████████████████████████  │
-- │ 23          │   551 │ ███████▊                       │
-- │ 22          │   588 │ ████████▍                      │
-- │ 21          │   303 │ ████▎                          │
-- │ 20          │   203 │ ██▉                            │
-- │ 19          │    78 │ █                              │
-- │ 18          │   148 │ ██                             │
-- │ 17          │   101 │ █▍                             │
-- │ 16          │   528 │ ███████▌                       │
-- │ 15          │   316 │ ████▌                          │
-- │ 14          │   113 │ █▌                             │
-- │ 13          │    42 │ ▌                              │
-- │ 12          │    19 │ ▎                              │
-- │ 11          │     4 │                                │
-- ├─────────────┴───────┴────────────────────────────────┤
-- │ 22 rows                                    3 columns │
-- └──────────────────────────────────────────────────────┘

一番大きな /11 は 4エントリ(2サブネット)ありました。

SELECT * FROM ipv4
WHERE split_part(ip_prefix, '/', 2) = '11';
-- ┌───────────────┬───────────┬─────────┬──────────────────────┐
-- │   ip_prefix   │  region   │ service │ network_border_group │
-- │    varchar    │  varchar  │ varchar │       varchar        │
-- ├───────────────┼───────────┼─────────┼──────────────────────┤
-- │ 44.192.0.0/11 │ us-east-1 │ AMAZON  │ us-east-1            │
-- │ 44.224.0.0/11 │ us-west-2 │ AMAZON  │ us-west-2            │
-- │ 44.192.0.0/11 │ us-east-1 │ EC2     │ us-east-1            │
-- │ 44.224.0.0/11 │ us-west-2 │ EC2     │ us-west-2            │
-- └───────────────┴───────────┴─────────┴──────────────────────┘

ユースケース: 特定リージョン/サービスのIPアドレス範囲を洗い出す

例として東京リージョンの API Gateway のIPアドレス範囲一覧を洗い出します。

SELECT * FROM ipv4
WHERE
  region = 'ap-northeast-1' AND
  service = 'API_GATEWAY';
-- ┌──────────────────┬────────────────┬─────────────┬──────────────────────┐
-- │    ip_prefix     │     region     │   service   │ network_border_group │
-- │     varchar      │    varchar     │   varchar   │       varchar        │
-- ├──────────────────┼────────────────┼─────────────┼──────────────────────┤
-- │ 18.180.88.0/23   │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 3.112.162.0/23   │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 3.112.96.160/27  │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 35.73.115.128/25 │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 35.75.130.0/24   │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 35.75.131.0/26   │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 35.77.112.0/22   │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- │ 35.77.124.0/23   │ ap-northeast-1 │ API_GATEWAY │ ap-northeast-1       │
-- └──────────────────┴────────────────┴─────────────┴──────────────────────┘

常に最新のものを持ってきたい場合は、 以下のようなシェルスクリプトを実行すると良いでしょう。

aws_region='ap-northeast-1'
aws_service='API_GATEWAY'

duckdb_sql=$(cat <<EOF
WITH ipv4 AS (
  SELECT unnest(prefixes, recursive := true)
  FROM read_json('https://ip-ranges.amazonaws.com/ip-ranges.json')
)
SELECT ip_prefix FROM ipv4
WHERE
  region = '${aws_region}' AND
  service = '${aws_service}';
EOF
)

duckdb -list -c "${duckdb_sql}"
# ip_prefix
# 18.180.88.0/23
# 3.112.162.0/23
# 3.112.96.160/27
# 35.73.115.128/25
# 35.75.130.0/24
# 35.75.131.0/26
# 35.77.112.0/22
# 35.77.124.0/23

ユースケース: 特定IPアドレスの所属確認

inet 拡張 を使うことで IPv4、IPv6 アドレスに関する値や関数を使えるようになります。

例えば以下SQLでは ip_prefix に対してホスト部分とネットマスク部分を表示しています。

SELECT
  ip_prefix,
  host(ip_prefix::INET) AS host,
  netmask(ip_prefix::INET) AS netmask,
FROM ipv4
LIMIT 5;
-- ┌─────────────────┬──────────────┬────────────────────┐
-- │    ip_prefix    │     host     │      netmask       │
-- │     varchar     │   varchar    │        inet        │
-- ├─────────────────┼──────────────┼────────────────────┤
-- │ 3.4.12.4/32     │ 3.4.12.4     │ 255.255.255.255    │
-- │ 3.5.140.0/22    │ 3.5.140.0    │ 255.255.252.0/22   │
-- │ 15.190.244.0/22 │ 15.190.244.0 │ 255.255.252.0/22   │
-- │ 15.230.15.29/32 │ 15.230.15.29 │ 255.255.255.255    │
-- │ 15.230.15.76/31 │ 15.230.15.76 │ 255.255.255.254/31 │
-- └─────────────────┴──────────────┴────────────────────┘

特定IPアドレスのエントリを調べてみましょう。

SELECT * FROM ipv4
WHERE
  '52.94.10.1'::INET <<= ip_prefix::INET;
-- ┌───────────────┬───────────┬──────────┬──────────────────────┐
-- │   ip_prefix   │  region   │ service  │ network_border_group │
-- │    varchar    │  varchar  │ varchar  │       varchar        │
-- ├───────────────┼───────────┼──────────┼──────────────────────┤
-- │ 52.94.10.0/24 │ us-west-2 │ AMAZON   │ us-west-2            │
-- │ 52.94.10.0/24 │ us-west-2 │ DYNAMODB │ us-west-2            │
-- └───────────────┴───────────┴──────────┴──────────────────────┘

おわりに

AWS IPアドレス範囲ファイルをDuckDBで読み込んで、見てみました。 JSONも扱えるのは便利です。 便利な関数も多く、出力形式も多数サポートされているので、 いろいろなユースケースで活用できると思います。

以上、参考になれば幸いです。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.