3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【図解】CIDR単位で別アカウントに接続できるPrivateLinkトンネルエンドポイントの仕組み

3
Posted at

概要

VPCエンドポイントのトンネルエンドポイントがリリースされました。

これまでのリソースエンドポイントは、Resource Configurationをリソース単位で作成する方式でした。トンネルエンドポイントではResource ConfigurationにCIDR範囲を指定できるので、10.0.0.0/16と指定すれば、その範囲にあるリソースへまとめて到達できます。

その代わり、DNS名に接続するだけで済む従来の使い方ではなく、接続元が自分でパケットをGENEVEでカプセル化して、トンネルエンドポイントに投げる必要があります。

全体構成は下記のとおりです。コンソールからの設定は次の順番になります。

スライド1.PNG

  • Resource Gatewayを作成(接続先)
  • Resource Configuration(CIDRタイプ)を作成(接続先)
  • Resource Configuration(CIDRタイプ)をRAMで共有(接続先)
  • RAMのリソース共有を承認(接続元)
  • VPCエンドポイント(トンネルタイプ)を作成(接続元)

パケットが流れる仕組みは下記のとおりです。今回は接続元EC2から接続先EC2の22番へTCP接続して疎通確認を行います(①)。カーネルのルーティング判断によってトンネルデバイス(tun0)へ書き込まれ(②③)、リレーが8バイトのGENEVEヘッダを付与して(④)、ens5からVPCエンドポイントへ送出されます(⑤)。

スライド2.PNG

ここからは簡単に接続元EC2での動きを見ていきます。大きくは下記の流れです。

  1. tun0というL3のトンネルデバイスを作る
  2. 接続先CIDR宛のパケットをtun0に向けるルートを追加する
  3. tun0に出てきたパケットに8バイトのGENEVEヘッダを付けてトンネルエンドポイントのENIへUDP 6081で送り、返ってきたら8バイトを外してtun0に戻す

実際の設定

AWS側のリソース(Resource Configuration / Resource Gateway / RAM共有 / Tunnelエンドポイント)は作成済みの状態から始めます。

環境

役割
接続元VPC 10.1.0.0/16
接続元EC2 10.1.139.57
TunnelエンドポイントのENI 10.1.132.45
接続先VPC 10.0.0.0/16
接続先EC2 10.0.130.80

① リレーを配置する

cat > /tmp/relay.py <<'EOF'
import fcntl, os, select, socket, struct, sys
t = os.open("/dev/net/tun", os.O_RDWR)
fcntl.ioctl(t, 0x400454CA, struct.pack("16sH", b"tun0", 0x0001 | 0x1000))
os.system("ip link set tun0 mtu 1464 up")
h = struct.pack("!BBHI", 0, 0, 0x0800, 0)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", 6081))
tep = (sys.argv[1], 6081)
while True:
    r, _, _ = select.select([t, s], [], [])
    if t in r:
        s.sendto(h + os.read(t, 65535), tep)
    if s in r:
        d, _ = s.recvfrom(65535)
        if len(d) > 8:
            os.write(t, d[8:])
EOF

このスクリプトがやっていることは、「tun0を作ること」と「その両側をつなぐこと」の2つです。

まず/dev/net/tunを開きます。これはOSに最初から存在する特殊なファイルで、開いてioctlで名前とモードを渡すと、その名前のトンネルデバイスが作られます。ここではtun0という名前と、IPパケットがそのまま流れるL3モードを指定しています。作成後、ip link set tun0 mtu 1464 upで起動します。

tun0から読んだパケットには先頭に8バイトのGENEVEヘッダを付けて、トンネルエンドポイントへUDP 6081で送ります。UDP 6081で返ってきたパケットは先頭8バイトを捨ててtun0に書き戻します。

② リレーを起動する

nohup python3 /tmp/relay.py 10.1.132.45 > /tmp/relay.log 2>&1 &

上記スクリプトをバックグラウンドで起動し、出力は/tmp/relay.logに書きます。引数の10.1.132.45はTunnel VPCエンドポイントのENIのプライベートIPで、コード内のtep = (sys.argv[1], 6081)の送信先として渡されます。

ip link show tun0
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1464 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 500
    link/none

tun0が起動していることを確認します。tun0はこのプロセスが動いている間だけ存在します。

③ ルートを追加する

ip route add 10.0.0.0/16 dev tun0 src 10.1.139.57

接続先VPCのCIDR宛だけをtun0に向けます。またtun0にIPアドレスが付いていないため、srcで送信元IPを明示します。

④ 疎通確認

timeout 5 cat < /dev/tcp/10.0.130.80/22
SSH-2.0-OpenSSH_9.9

接続先EC2の22番にTCP接続し、sshdのバナーが表示されることを確認します。

まとめ

CIDR単位で共有できるのは、接続先のリソースが増えていく構成ではかなり効いてくると思っています。ただし現時点では、AWSコンソールからの設定に加えて、接続元でGENEVEのカプセル化を自分で用意する必要があります。この部分を運用できるかどうかが採用の分かれ目になりそうです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?