はじめに
2026年9月18日、AWS PrivateLinkに新しいVPCエンドポイントのタイプ「トンネルエンドポイント」が追加されました。
これまでPrivateLinkで他アカウントにリソースを共有する場合、Resource Configurationをリソースごとに1つずつ作成する必要がありましたが、今回のアップデートで、CIDR範囲を表すResource Configurationを作成し、その範囲内のリソースへまとめてアクセスさせることができるようになったとのことなので、触ってみたいと思います。
本記事では以下の3点を確認していきます。
- どうやって作るのか(マネジメントコンソールで2アカウント構成を構築)
- どのようなユースケースに合うのか
- VPCピアリングとどう使い分けるのか
構成図
今回の検証構成です。アカウントA(提供側)の 10.0.10.0/24 だけを、アカウントB(利用側)に貸し出します。
トンネルエンドポイントとは
登場人物
| リソース | 作成するアカウント | 役割 |
|---|---|---|
| リソースゲートウェイ | 提供側 | 提供側VPCへの入口 |
| Resource Configuration(CIDR型) | 提供側 | 「どのCIDRを貸し出すか」の定義 |
| AWS RAM リソース共有 | 提供側 | Resource Configurationを他アカウントに共有 |
| トンネルエンドポイント | 利用側 | 利用側VPC内の窓口。GENEVEで包まれた通信を受け取る |
GENEVEとは
トンネルエンドポイントを使うには、クライアント側で通信をGENEVEでカプセル化する必要があります。
GENEVE(Generic Network Virtualization Encapsulation)は、パケットを別のパケットで包んで運ぶカプセル化プロトコルで、2020年に RFC 8926 として標準化。
VXLAN や NVGRE など乱立していたネットワーク仮想化向けプロトコルを統合する後継として設計され、可変長のオプション(TLV)でメタデータを載せられる拡張性が特徴らしい。
AWS では Gateway Load Balancer とアプライアンス間の通信で使われているので、そちらで見覚えのある方も多いと思います。僕はGateway Load Balancerを触ったことがなかったので初耳でした。
+----------------+--------------+-------------+--------------------------+
| 外側IPヘッダ | UDPヘッダ | GENEVE | 元のパケット(内側) |
| 宛先: | 宛先ポート: | ヘッダ | 宛先: 共有されたCIDR内の. |
| トンネルEPのIP | 6081 | VNI=0 | リソース(TCP / DNSのみUDP) |
+----------------+--------------+-------------+--------------------------+
← トンネルエンドポイントまでの配送用 → ← 相手VPCで解釈される通信 →
ポイントは「外側の宛先は自VPC内のトンネルエンドポイント、内側の宛先は相手VPCのリソース」という二重構造です。
トンネルエンドポイントはGENEVEヘッダを外し、内側パケットの宛先が共有されたCIDR内かを確認したうえで転送します。
前提条件
- Resource ConfigurationはCIDR型で作成し、DNS解決が
VPC内のリソースゲートウェイに紐づけること - 別アカウントから共有された場合は、RAMのリソース共有を承認すること
- 利用側VPCに、提供側リソースゲートウェイと同じAZのサブネットがあること
- クライアント側でGENEVEカプセル化ができること(VNIは0、内側はL3のIPパケット)
- トンネルエンドポイントとクライアントの両方のセキュリティグループで UDP 6081 を許可すること
制約
- 接続は利用側からのみ開始できる(提供側から利用側への接続開始は不可)
- 内側パケットでUDPが使えるのはDNSクエリのみで、アプリケーション通信はTCPのみ
- Private DNS名は非対応
- 名前解決は、ネームサーバーに
169.254.168.253を指定してGENEVE経由で問い合わせる。提供側VPCのDHCPオプションセットやRoute 53プライベートホストゾーンを踏まえて解決される
ping(ICMP)は通りません。疎通確認はTCP(curlなど)で行いましょう。
作ってみた
検証環境
VPC・サブネット・EC2などの前提リソースはCloudFormationで作成し、記事の主役となる以下のリソースはマネジメントコンソールで作成しました。
- リソースゲートウェイ
- Resource Configuration(CIDR型)
- RAM共有
- トンネルエンドポイント
2アカウント構成では、サブネットのAZをAZ名ではなくAZ ID(apne1-az1 など)で揃えましょう。ap-northeast-1a が指す物理AZはアカウントごとに異なるため、AZ名で揃えると「同じAZのはずなのにサブネットが選べない」状態になります。
STEP 1【アカウントA】リソースゲートウェイの作成
VPCコンソールの「PrivateLinkとLattice」→「リソースゲートウェイ」から作成します。

DNS解決は VPC内 を選択します。この設定は後から変更できないため、間違えると作り直しになります。

サブネットはアカウントA側で作成した2つのサブネットを指定します。
STEP 2【アカウントA】Resource Configuration(CIDR型)の作成
「リソース設定」から作成し、STEP 1のリソースゲートウェイと、共有するCIDR 10.0.10.0/24 を指定します。

IPアドレス範囲は以下で指定します。ポート番号はとりあえず全開放にします。

STEP 3【アカウントA】RAMでアカウントBに共有
RAMコンソール →「リソースの共有」→「リソースの共有を作成」をクリックしてRAMのリソースを作成していきます。
マネージド型許可:デフォルトのまま
プリンシパル:「AWSアカウント」を選び、アカウントBのアカウントIDを入力して「追加」

STEP 4【アカウントB】RAM共有の承認
RAMコンソール →「自分と共有」→「リソースの共有」→ tunnel-test-share(アカウントA側のリソース共有) を選択 →「リソースの共有を承諾」

STEP 5【アカウントB】トンネルエンドポイントの作成
VPCコンソールの「エンドポイント」から、タイプ「トンネル」を選択して作成します。

作成後、エンドポイントのAZごとのIPアドレスを確認しておきます。
STEP 6【アカウントB】クライアントEC2でGENEVEを設定
ここからはEC2上のLinuxでの作業です。EC2 Instance Connect Endpoint経由で接続しました。
ドキュメントでは、GENEVEの処理を専用のネットワーク名前空間(network namespace)で行うことが推奨されています。
今回は tunnel という名前空間を作成し、共有CIDR宛ての通信だけをGENEVEデバイスに流します。
# トンネルエンドポイントのIP(AZ1)
EP_IP=10.1.1.xxx ※ネットワークインターフェースからVPCエンドポイントのIPアドレスを記載
# geneveモジュールの確認
lsmod | grep geneve
# 名前空間を作成
sudo ip netns add tunnel
# GENEVEデバイスを作成(VNI=0、内側はL3パケット)し、名前空間に移動
sudo ip link add gnv0 type geneve id 0 remote ${EP_IP} innerprotoinherit
sudo ip link set gnv0 netns tunnel
# 名前空間内でデバイスを有効化し、アドレスとルートを設定
sudo ip netns exec tunnel ip addr add 【クライアントEC2の送信元アドレス】/32 dev gnv0
sudo ip netns exec tunnel ip link set gnv0 up
sudo ip netns exec tunnel ip route add 10.0.10.0/24 dev gnv0
sudo ip netns exec tunnel ip route add 169.254.168.253/32 dev gnv0
GENEVEデバイスをデフォルトの名前空間で作成してから tunnel 名前空間に移動しているのがポイントです。外側のUDP通信はデフォルト名前空間(通常のVPC通信)で行われ、内側の通信だけが tunnel 名前空間に閉じ込められます。
疎通確認
HTTP(TCP 80)
10.0.10.10 は、アカウントA側のEC2プライベートIPアドレスです。
[ec2-user@ip-10-1-3-76 ~]$ sudo ip netns exec tunnel curl http://10.0.10.10/
Hello from Account A target (10.0.10.10)
期待通りのコマンドが返ってきており、アカウントBのクライアントEC2 → GENEVEトンネル → トンネルエンドポイント → リソースゲートウェイ → アカウントAの接続先EC2という一連の経路が、実際に成立したことが確認できました。
名前解決
sudo ip netns exec tunnel dig @169.254.168.253 web.tunnel-test.internal
Aレコードの値がEC2のIPアドレスになっており、アカウントA側のプライベートホストゾーンのレコードが、アカウントBから解決できてます。
[ec2-user@ip-10-1-3-76 ~]$ sudo ip netns exec tunnel dig @169.254.168.253 web.tunnel-test.internal
; <<>> DiG 9.18.50 <<>> @169.254.168.253 web.tunnel-test.internal
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41920
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 0
;; QUESTION SECTION:
;web.tunnel-test.internal. IN A
;; ANSWER SECTION:
web.tunnel-test.internal. 60 IN A 10.0.10.10
;; Query time: 0 msec
;; SERVER: 169.254.168.253#53(169.254.168.253) (UDP)
;; WHEN: Mon Sep 21 13:45:39 UTC 2026
;; MSG SIZE rcvd: 69
アカウントA側から見た送信元IP
接続先EC2のアクセスログを確認します。
sudo journalctl -u testweb -f
ログに記録された送信元IPは 以下の通り、10.0.1.178 でした。これはクライアントEC2の実IP(10.1.3.76)ではなく、アカウントAの RgwSubnet1(10.0.1.0/24)の範囲内のIPになっています。
つまり、リソースゲートウェイが送信元IPを自分自身のIPに変換してから、接続先EC2に転送していることが確認できました。
[ec2-user@ip-10-0-10-10 ~]$ sudo journalctl -u testweb -f
Sep 21 10:44:00 ip-10-0-10-10.ap-northeast-1.compute.internal systemd[1]: Started testweb.service - Test web server for tunnel endpoint verification.
Sep 21 13:42:38 ip-10-0-10-10.ap-northeast-1.compute.internal python3[1991]: 10.0.1.178 - - [21/Sep/2026 13:42:38] "GET / HTTP/1.1" 200 -
試してはないですが、提供側からはリソースゲートウェイのIPアドレスとして認識するなら、利用側と提供側のIPアドレス範囲が被ってても利用できそうですね。
ハマりどころ
- AZはAZ IDで揃える
- セキュリティグループは3か所
- トンネルエンドポイント:クライアントからの UDP 6081 を許可
- クライアントEC2:トンネルエンドポイントからの UDP 6081 を許可
- 接続先EC2:リソースゲートウェイのSGからの TCP 80 を許可
- リソースゲートウェイのDNS解決設定は後から変更できない
- pingは通らない(ICMPは非対応)
VPCピアリングとの比較
| 観点 | VPCピアリング | トンネルエンドポイント |
|---|---|---|
| 接続の方向 | 双方向 | 利用側からの開始のみ(一方向) |
| 公開範囲 | 相手VPCのCIDR全体(ルートテーブル・SGで絞る) | 提供側が指定したCIDRのみ。範囲外は拒否 |
| クライアント側の作り | 通常のルーティングだけ | GENEVEカプセル化が必須 |
| プロトコル | 制約なし | アプリ通信はTCPのみ(UDPはDNSのみ) |
| 共有方法 | ピアリング接続の申請・承認 | RAMでResource Configurationを共有 |
| 名前解決 | DNS解決オプションで相手のプライベートDNSを解決 | 169.254.168.253経由で相手VPCの文脈で解決 |
| CIDRの重複 | 不可 | 多分いける(未検証) |
| コスト | 接続自体は無料(データ転送料のみ) | エンドポイント時間課金+データ処理課金+提供側のリソースゲートウェイ課金 |
両者の違いを一言でまとめると、次のようになると考えています。
- VPCピアリング:ネットワーク同士を「繋ぐ」
- トンネルエンドポイント:ネットワークの一区画を「貸し出す」
VPCピアリングは、接続を張った時点で双方のネットワークが対等に繋がります。
アクセスを絞るのはルートテーブルやセキュリティグループの仕事で、設定を誤れば意図しない範囲まで到達できてしまいます。
トンネルエンドポイントは、提供側が「このCIDRだけ」と決めた範囲しか利用側に見せません。
また、提供側から利用側への接続の開始は仕様としてできません。
「相手に入ってきてもらうが、こちらから相手のネットワークには入らない」関係を、設定ではなく構造で保証できるのが最大の違いです。
一方で、クライアント側にGENEVEの実装が必要で、プロトコルもTCPに限られます。
アプリケーション同士を普通に繋ぎたいだけなら、VPCピアリングやTransit Gatewayのほうが素直です。
どんなユースケースに合うか
向いているケース
-
外部ベンダーや運用保守事業者に、特定のセグメントだけアクセスさせたい
- 公式が想定している代表的なユースケースです。逆方向の接続を構造的に遮断できる点が効いてきます。
-
Auto ScalingやコンテナなどIPが頻繁に変わるリソース群を共有したい
- リソースごとのResource Configurationでは追従しにくい対象です。
-
セキュリティスキャナーや監視製品など、アプライアンス型のサービス
- GENEVEはGateway Load Balancerでも使われており、製品側での対応が進みやすい領域だと考えています。
向いていないケース
- 双方向の通信が必要なシステム連携
- UDPやICMPを使うアプリケーション
- クライアント側にGENEVEを実装できない(OSやネットワーク設定に手を入れられない)環境
- 単一のリソースだけを共有したい場合(従来のリソースエンドポイントで十分)
料金と後片付け
トンネルエンドポイントには、次の料金がかかります。
- エンドポイントの時間課金
- データ処理量に応じたGB課金
- 提供側のリソースゲートウェイのGB課金(VPC Latticeの料金体系)
検証が終わったら、次の順番で削除します。
- 【アカウントB】トンネルエンドポイント
- 【アカウントA】RAMのリソース共有
- 【アカウントA】Resource Configuration
- 【アカウントA】リソースゲートウェイ
- 両アカウントのCloudFormationスタック
コンソールで作成したリソースがサブネットやセキュリティグループを参照している間は、スタックの削除が失敗します。先にコンソールで作成したものから消しましょう。
まとめ
思ってたより手順が多かったです。特にクライアントEC2でGENEVEを設定するところは中々理解が追いつかず、まだ全部は理解できてないなと感じてます(単純な勉強不足です)。
なんとなく理解できたところは以下です。
- トンネルエンドポイントは、CIDR単位でネットワークの一区画を他アカウントに「貸し出す」ための新しいVPCエンドポイント
- 利用側からの一方向の接続、共有範囲外の拒否を構造的に保証できる
- クライアント側のGENEVE実装とTCP限定という制約があるため、汎用的なVPC間接続はVPCピアリングやTransit Gatewayのほうが素直
- 外部ベンダーへのアクセス提供など「入ってきてもらうが、こちらからは入らない」関係で真価を発揮する
参考
- AWS PrivateLink がトンネルエンドポイントに対応(What's New)
https://aws.amazon.com/jp/about-aws/whats-new/2026/9/privatelink-tunnel-endpoint/ - Access CIDR ranges through AWS PrivateLink(AWS ドキュメント)
https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-access-cidr-ranges.html - Create a resource configuration(AWS ドキュメント)
https://docs.aws.amazon.com/vpc/latest/privatelink/create-resource-configuration.html - RFC 8926 - Geneve: Generic Network Virtualization Encapsulation
https://datatracker.ietf.org/doc/html/rfc8926


