【新機能】Amazon Route 53 でアクセス元 IP をベースとしたルーティングがサポートされました

Route 53 で IP ベースでルーティングを設定可能になりました。

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

ウィスキー、シガー、パイプをこよなく愛する大栗です。

先日 Amazon Route 53 で IP アドレスを元にクエリの応答を決めるルーティングポリシーがサポートされたので試してみました。

Amazon Route 53 のアクセス元情報によるルーティング

Route 53 ではアクセス元の情報を元に応答するクエリ結果を分ける機能が複数ありました。

  • 位置情報ルーティングポリシー(Geolocation routing policy):ユーザーの位置情報に基づいてトラフィックをルーティング
  • 地理的近接性ルーティングポリシー(Geoproximity routing policy):リソースの場所に基づいてトラフィックをルーティングし、必要に応じて特定のリソースにルーティングするトラフィックの量を変更
  • レイテンシールーティングポリシー(Latency routing policy):複数の AWS リージョンにリソースがあり、より少ない往復時間で最良のレイテンシーを実現するリージョンにトラフィックをルーティング

これは EDNS0 の edns-client-subnet 拡張を活用1しています。各クライアントが権威サーバーに直接アクセスする場合はアクセス元の IP アドレスを元にクエリ結果を変更すれば解決できます。しかし、一般的には ISP やプラットフォームのリゾルバや8.8.8.8などのパブリックリゾルバを経由してアクセスするため、権威サーバーはクライアントの IP アドレスを確認する方法がありません。ここで各リゾルバが edns-client-subnet 拡張に対応すると権威サーバーでもアクセス元 IP アドレスが分かるため、クエリ結果の振り分けが可能になります。HTTP アクセスにおけるX-Forwarded-ForヘッダーやTrue-Client-IPヘッダーの DNS 版と言えるかもしれません。

しかし、今まで edns-client-subnet 拡張を活用していたルーティングポリシーはアクセス元を場所やレイテンシーというように抽象化して判断していました。

IP ベースルーティング

既存のルーティングポリシーと異なり IP ベースルーティングではアクセス元の IP アドレスの CIDR を元にクエリ結果を変えることが可能になりました。

  • CIDR block:IPv4 は /0〜/24, IPv6 は /0〜/48 で指定できます
  • CIDR location:CIDR block の名前付きリストです。複数の CIDR block を指定できます。
  • CIDR collection:CIDR location の名前付きリストです。複数の CIDR location を指定できます。

IP ベースルーティングのユースケース

AWS 上にホストされたアプリケーションは、サーバーの場所はリージョンごとに考慮すべきです。そのためグローバルなアプリケーションであれば位置情報ルーティングポリシーやレイテンシールーティングポリシーを使用すれば問題が発生しません。特定の国や地域を対象にしたアプリケーションでの場合は、CDN などを利用すればレイテンシーが問題になることは少ないため近い場所からの配信を重要視しなくても良くなります。1カ国でレイテンシーが問題になりそうなアメリカの場合は位置情報ルーティングポリシーで州ごとにルーティングの設定を行えます。

そのため IP ベースルーティングが必要な場面は限定的になります。一般的に必要となる以下のような場面です。

  1. ネットワークトランジットのコストやパフォーマンスを最適化するためにエンドユーザーが利用している ISP の中や近い場所にあるサーバからコンテンツを配信するルーティングをする
  2. 特定の ISP などでアクセス元 IP アドレスの国情報が実態とあっておらずアップデートされていない場合に、コンテンツプロバイダが上書きする

1.は日本国内でのコンテンツプロバイダーでは CDN で解決できることが多くあるため必要性は不透明です。2.は特定の ISP で日本国外からのアクセスと判定される場合がありますが、コンテンツプロバイダ側でメンテナンスすることができるようになるということです。すでに Route 53 は多彩なルーティングポリシーがあるため、ニッチなユースケースへの対応が目的に思われます。

なお、DNS リゾルバ側の対応が必要になるため注意が必要です。Cloudflare が運営しているパブリック DNS リゾルバである 1.1.1.1 はプライバシーを重視するため edns-client-subnet 拡張に対応していません2。しかし、IP Anycast (グローバルに分散されていても 1 個の IP アドレスでアクセスできる技術) が実装されているマルチリージョンアプリケーション、例えば AWS Global Accelerator や Google Cloud の Cloud Load Balancing でグローバル負荷分散を使用したアプリケーションであればクライアントの IP アドレスではなくルーティング情報を元に最も近いサーバーへルーティングするため、edns-client-subnet 拡張に対応していない DNS リゾルバを使用していても問題はありません。

IP ベースルーティングの料金

  • $0.80 100 万クエリごと - 月間で最初の 10 億クエリまで
  • $0.40 100 万クエリごと - 月間で 10 億クエリ超過分

最大 1,000 IP(CIDR) ブロックの保存には料金はかかりませんが、1,000ブロックを超える保存されたIPブロックごとに月額 $ 0.0015(時間で案分) の料金が発生します。

やってみる

EDNS Client Subnet ヘッダーによって、以下のようにアクセス先のサイトを振り分けてみます。

  • 東京 (ap-northeast-1): 192.0.2.0/24
  • オレゴン (us-west-2): 198.51.100.0/24
  • アイルランド (eu-west-1): 203.0.113.0/24

事前に上記リージョンで httpd を起動して公開しておきます。

注意点として

  • ここではブラウザを Google Chrome で検証しています。
  • AWS CLI で実行する場合は、1 系は 1.25.1 以上を使用してください。2 系は現在最新の 2.7.5 でも使用できないようです。

CIDR collection の作成

Route 53 のコンソールでIP-based routingの下のCIDR collectionsを開き、Create CIDR collectionをクリックします。

CIDR コレクションの画面

Collection nameで CIDR collection の名称を入力します。CIDR locationsはルーティング先ごとに分けます。CIDR blocksにクライアントの IP アドレスの CIDR を入力します。ここでは以下のように入力します。なおCIDR locationsを増やすにはAdd another locationをクリックします。

CIDR location CIDR blocks
tokyo 192.0.2.0/24
[自分のアクセス元 IP アドレスを含む /24 の CIDR]
oregon 198.51.100.0/24
ireland 203.0.113.0/24

AWS CLI の場合には以下のようになります。

まず、CIDR Collection を作成します。

$ aws route53 create-cidr-collection --name "test-ip-based-routing" --caller-reference "unique-client-token-1"
{
    "Location": "https://route53.amazonaws.com/2013-04-01/cidrcollection/a1b2c3d4-1234-abcd-1234-a1b2c3d4e5f6",
    "Collection": {
        "Arn": "arn:aws:route53:::cidrcollection/a1b2c3d4-1234-abcd-1234-a1b2c3d4e5f6",
        "Id": "a1b2c3d4-1234-abcd-1234-a1b2c3d4e5f6",
        "Name": "test-ip-based-routing",
        "Version": 1
    }
}

CIDR Collection を変更して CIDR location や CIDR block を設定します。

$ aws route53 change-cidr-collection --id "a8c09379-0cc9-e9dc-8903-c7533e33688f" --changes '
> [
>   {
>     "LocationName": "tokyo",
>     "Action": "PUT",
>     "CidrList": ["192.0.2.0/24", "<自分のアクセス元 IP アドレスを含む /24 の CIDR>"]
>   },
>   {
>     "LocationName": "oregon",
>     "Action": "PUT",
>     "CidrList": ["198.51.100.0/24"]
>   },
>   {
>     "LocationName": "ireland",
>     "Action": "PUT",
>     "CidrList": ["203.0.113.0/24"]
>   }
> ]'
{
    "Id": "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHHHHHAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHHHHHAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHHHHHAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHHHHHAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHHHHHAAAAAAAABBBBBBBBCCCCC"
}

作成すると、以下のようになります。

DNS レコードの作成

Hosted zones の画面で、対象のホストゾーンを選択します。

Create recordをクリックします。

IP-basedを選択してNextをクリックします。

Record name でドメイン名を入力して、レコードタイプや TTL を設定します。

Define CIDR recordをクリックします。

トラフィックのルーティング先の IP アドレスを入力して、IP-based(Default location)を選択、レコード ID を入力してDefine CIDR recordをクリックします。ここで選択した(Default location)は CIDR Collection で設定していない CIDR からアクセスが来た場合のルーティング先です。

次に別の CIDR を設定します。まず東京へのルーティングを行います。トラフィックのルーティング先に東京のサーバーの IP アドレスを設定します。IP-basedに CIDR location で設定したtokyoを選択、レコード ID を入力してDefine CIDR recordをクリックします。

同様にオレゴン、アイルランドでも CIDRを登録してCreate recordsをクリックします。

このようにデフォルトと各場所の合計 4 件が作成されます。

振り分けを確認

まずは dig コマンドで IP ベースの振り分けを確認してみます。東京へ振り分ける CIDR をヘッダに指定して dig を実行すると東京のサーバーへ振り分けられます。

$ dig test-ip-based-routing.example.com +subnet=<自分の IP アドレス>/24 @8.8.8.8

; <<>> DiG 9.10.6 <<>> test-ip-based-routing.example.com +subnet=106.73.21.0/24 @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23971
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 106.73.21.0/24/24
;; QUESTION SECTION:
;test-ip-based-routing.example.com. IN A

;; ANSWER SECTION:
test-ip-based-routing.example.com. 60 IN A <東京サーバーの IP アドレス>

;; Query time: 13 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Jun 03 11:11:01 JST 2022
;; MSG SIZE  rcvd: 95

オレゴンへ振り分ける CIDR を指定して dig を実行するとオレゴンのサーバーへ振り分けられます。

% dig test-ip-based-routing.example.com +subnet=198.51.100.0/24 @8.8.8.8

; <<>> DiG 9.10.6 <<>> test-ip-based-routing.example.com +subnet=198.51.100.0/24 @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10769
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 198.51.100.0/24/24
;; QUESTION SECTION:
;test-ip-based-routing.example.com. IN A

;; ANSWER SECTION:
test-ip-based-routing.example.com. 60 IN A <オレゴンサーバーの IP アドレス>

;; Query time: 22 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Jun 03 11:11:12 JST 2022
;; MSG SIZE  rcvd: 95

アイルランドへ振り分ける CIDR を指定して dig を実行するとアイルランドのサーバーへ振り分けられます。

$ dig test-ip-based-routing.example.com +subnet=203.0.113.0/24 @8.8.8.8

; <<>> DiG 9.10.6 <<>> test-ip-based-routing.example.com +subnet=203.0.113.0/24 @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5119
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 203.0.113.0/24/24
;; QUESTION SECTION:
;test-ip-based-routing.example.com. IN A

;; ANSWER SECTION:
test-ip-based-routing.example.com. 60 IN A <アイルランドサーバーの IP アドレス>

;; Query time: 14 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Jun 03 11:11:21 JST 2022
;; MSG SIZE  rcvd: 95

なお、192.0.2.0/24を指定すると A レコードが返ってきませんでした。これは192.0.2.0/24が bogon3 として扱われるからかもしれません。

% dig test-ip-based-routing.example.com +subnet=192.0.2.0/24 @8.8.8.8

; <<>> DiG 9.10.6 <<>> test-ip-based-routing.example.com +subnet=192.0.2.0/24 @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 25143
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 192.0.2.0/24/0
;; QUESTION SECTION:
;test-ip-based-routing.example.com. IN A

;; Query time: 9 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Jun 03 11:25:06 JST 2022
;; MSG SIZE  rcvd: 79

では実際にブラウザでアクセスしてみると東京のサーバーへアクセスできました。

ここで CIDR Colloction で私のマシンの IP を含む CIDR を削除してみます。

しばらく待ってからブラウザの DNS キャッシュを削除します。chrome://net-internals/#dnsにアクセスして、DNS ページのClear host cacheをクリックします。

またブラウザでアクセスしてみます。するとデフォルトのサーバーへアクセスします。

アクセス元 IP アドレスによってルーティングされることを確認できました。

さいごに

今までクライアント側の IP アドレスを認識して Route 53 が振り分ける機能は、位置情報ルーティングポリシー、地理的近接性ルーティングポリシー、レイテンシールーティングポリシーがありました。国やレイテンシーなどに抽象化されて扱われますが、多くの場合は十分ですがきめ細かな設定が行なえませんでした。IP ベースルーティングによってニッチな要件にも対応できるため、細かく配信場所を設定する要件がある場合に活用してはいかがでしょうか。

参考資料


  1. How Amazon Route 53 uses EDNS0 to estimate the location of a user 
  2. Akamai のプロバイダー間のデバッグを支援するためのwhoami.ds.akahelp.netは例外的に EDNS Client Subnet ヘッダーを送信します。​Does 1.1.1.1 send EDNS client subnet header? 
  3. 「bogon」とは、インターネットの経路制御において、 広告可能アドレスとして登録されていないアドレスブロックやAS番号を指し、 ルーティングテーブル(経路表)(*1)に本来現れるべきではない経路情報を「bogon経路」と言います。Bogonとは