EC2から自宅経由でIPv4インターネット接続して料金を節約する
目次
はじめに
プライベート用に Amazon EC2 インスタンスを t3a.nano のリザーブドインスタンスで1台使っています。
2024年2月からパブリック IPv4 アドレス料金がかかることになり、インスタンス料金の倍以上もかかるというのは割に合わないですし、基本的に外向きのアクセスしかしていないので、IPv4 インターネット接続は自宅経由で行わせることにしました。
自宅のルータと EC2 インスタンスの間で何らかのトンネルを張るわけですが、まずは実験としてシンプルに IPv4 over IPv6 トンネルで実現してみます。
環境説明
AWS 側
検証用の EC2 インスタンスや AWS 側のネットワークは次のようにしました。
- VPC 関連
- VPC アドレス : 172.31.0.0/16
- サブネット : 172.31.0.0/20
- Internet Gateway : あり
- EC2 インスタンス関連
- パブリック IPv4 アドレス : なし
- プライベート IPv4 アドレス : 172.31.0.4
- IPv6 アドレス : 2406:XXXX:XXXX:XXXX::4
- OS : Amazon Linux 2023 (kernel 6.1.61-85.141.amzn2023.x86_64)
パブリック IPv4 アドレスがないので、インスタンスから直接 IPv4 インターネットにはアクセスできません。
自宅側
私の自宅のインターネット回線は OCN 光で、IPv6 IPoE 上で IPv4 インターネットアクセスをしています。 自宅から IPv4 と IPv6 の両方でインターネットアクセスできる環境であれば、回線の種類は問わず実現できます。
ルータとその設定は 「NEC UNIVERGE IX2105 で OCN 光の IPoE 接続を行う」 に書いたものを使っています。 一般の家庭用ルータでは本記事のようなことはおそらく行えないと思います。
UNIVERGE IX2105 の設定
ルータ側では次の設定を行いました。
- IPv4 over IPv6 トンネルインターフェースを作成
- EC2 インスタンス宛の経路をトンネルインターフェースに向ける
- IPv4 がカプセル化された IPv6 通信を許可する
1interface Tunnel1.0
2 tunnel mode 4-over-6
3 tunnel destination 2406:XXXX:XXXX:XXXX::4
4 tunnel source GigaEthernet1.0
5 ip unnumbered GigaEthernet1.0
6 no shutdown
7
8ip route 172.31.0.4/32 Tunnel1.0
9
10ipv6 access-list permit-ec2-i permit 4 src 2406:XXXX:XXXX:XXXX::4/128 dest 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX/128
11ipv6 access-list permit-ec2-o permit 4 src 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX/128 dest 2406:XXXX:XXXX:XXXX::4/128
12
13interface GigaEthernet0.0
14 ipv6 filter permit-ec2-i 11 in
15 ipv6 filter permit-ec2-o 11 out
トンネルインターフェースには IPv4 アドレスを付ける理由がないので、unnumbered としています。
設定後、トンネルインターフェースのステータスは次のようになっていました。
1Interface Tunnel1.0
2 Tunnel mode is 4-over-6
3 Tunnel is ready
4 Destination address is 2406:XXXX:XXXX:XXXX::4
5 Source address is 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX
6 Source interface GigaEthernet1.0
7 Nexthop address is fe80::XXXX:XXXX:XXXX
8 Outgoing interface is GigaEthernet0.0
9 Interface MTU is 1460
10 Path MTU is 1500
11 Statistics:
12 0 packets input, 0 bytes, 0 errors
13 0 packets output, 0 bytes, 0 errors
14 Received ICMP messages:
15 0 errors
16 0 no route to destination
17 0 administratively prohibited
18 0 beyond scope of source address
19 0 address unreachable
20 0 port unreachable
21 0 packet too big
22 0 hop limit exceeded in transit
23 0 parameter problem
自宅の IPv4 インターネット回線の MTU が 1460 (1500 - 40) ですし、トンネルインターフェースの MTU が 1460 になっているのはベストですね。
EC2 インスタンス側の設定
OS 上のネットワーク設定の確認
OS 上のネットワーク設定は次のようになっています。
1$ ip addr
21: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
3 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4 inet 127.0.0.1/8 scope host lo
5 valid_lft forever preferred_lft forever
6 inet6 ::1/128 scope host noprefixroute
7 valid_lft forever preferred_lft forever
82: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
9 link/ether 06:e5:b7:f3:b5:b1 brd ff:ff:ff:ff:ff:ff
10 altname enp0s5
11 altname eni-0804132b9abc1d7cc
12 altname device-number-0
13 inet 172.31.0.4/20 metric 512 brd 172.31.15.255 scope global dynamic ens5
14 valid_lft 2736sec preferred_lft 2736sec
15 inet6 2406:XXXX:XXXX:XXXX::4/128 scope global dynamic noprefixroute
16 valid_lft 383sec preferred_lft 73sec
17 inet6 fe80::4e5:b7ff:fef3:b5b1/64 scope link
18 valid_lft forever preferred_lft forever
19
20
21$ ip route
22default via 172.31.0.1 dev ens5 proto dhcp src 172.31.0.4 metric 512
23172.31.0.0/20 dev ens5 proto kernel scope link src 172.31.0.4 metric 512
24172.31.0.1 dev ens5 proto dhcp scope link src 172.31.0.4 metric 512
25172.31.0.2 dev ens5 proto dhcp scope link src 172.31.0.4 metric 512
Amazon Linux 2023 だと metric 付きで経路が設定されるのですね。これは若干ありがたいです。
セキュリティグループの設定
自宅ルータから IPv4 がカプセル化された IPv6 通信が EC2 インスタンスに来ることにもなるため、セキュリティグループでインバウンド方向を許可しておきます。
ルールのタイプは "カスタムプロトコル" で、プロトコル番号は "4 (IP-in-IP)" です。
IPv4 over IPv6 トンネルインターフェースの設定
IPv4 over IPv6 トンネルは kernel でサポートされているので、特別なソフトウェアをインストールすることなく設定できます。
次のようなコマンドでトンネルインターフェースを作成できます。
remote は自宅ルータのアドレス、local は EC2 インスタンスのアドレスです。 また、ens5 で IPv6 パケットを送信するので、ens5 に紐付けておくと安全です。
1$ sudo ip -6 tunnel add ip6tnl mode ipip6 remote 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX local 2406:XXXX:XXXX:XXXX::4 dev ens5
2
3$ ip addr show
4(1,2省略)
53: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
6 link/tunnel6 :: brd :: permaddr 62e8:8546:de3c::
74: ip6tnl@ens5: <POINTOPOINT,NOARP> mtu 8953 qdisc noop state DOWN group default qlen 1000
8 link/tunnel6 2406:XXXX:XXXX:XXXX::4 peer 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX permaddr 20c:7c8c:c338::
9
10$ ip -6 tunnel show
11ip6tnl0: ipv6/ipv6 remote :: local :: encaplimit 0 hoplimit inherit tclass 0x00 flowlabel 0x00000 (flowinfo 0x00000000)
12ip6tnl: ip/ipv6 remote 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX local 2406:XXXX:XXXX:XXXX::4 dev ens5 encaplimit 4 hoplimit 64 tclass 0x00 flowlabel 0x00000 (flowinfo 0x00000000)
13
14$ lsmod | grep tunnel
15ip6_tunnel 49152 0
16tunnel6 16384 1 ip6_tunnel
ip6tnl が今回作成したトンネルインターフェースです。
MTU が大きすぎるので 1460 に変更し、インターフェースを有効化しておきます。
1$ sudo ip link set dev ip6tnl mtu 1460
2$ sudo ip link set dev ip6tnl up
3
4$ ip addr show dev ip6tnl
54: ip6tnl@ens5: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1460 qdisc noqueue state UNKNOWN group default qlen 1000
6 link/tunnel6 2406:XXXX:XXXX:XXXX::4 peer 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX permaddr 20c:7c8c:c338::
7 inet6 fe80::c:7cff:fe8c:c338/64 scope link
8 valid_lft forever preferred_lft forever
IPv4 経路の設定
さて、IPv4 経路をトンネルインターフェースに向ければ、IPv4 インターネットにもアクセスできるようになります。
Amazon Linux 2023 ではデフォルトルートが metric 512 として設定されているので、それより小さな metric でデフォルトルートを追加すれば良いです。
1$ sudo ip route add default dev ip6tnl
2
3$ ip route
4default dev ip6tnl scope link
5default via 172.31.0.1 dev ens5 proto dhcp src 172.31.0.4 metric 512
6172.31.0.0/20 dev ens5 proto kernel scope link src 172.31.0.4 metric 512
7172.31.0.1 dev ens5 proto dhcp scope link src 172.31.0.4 metric 512
8172.31.0.2 dev ens5 proto dhcp scope link src 172.31.0.4 metric 512
これで OK と言いたいところですが、これだとリンクローカルアドレス "169.254.0.0/16" や VPC アドレス "172.31.0.0/16" のうち他のサブネットのアドレスまで自宅ルータ側に転送されてしまいます。
特にリンクローカルアドレスはインスタンスメタデータへのアクセスや Time Sync Service の NTP 通信で使われていますので、これらは当初と同じく ens5 から直接送信する必要があります。
そのため、次の経路も忘れずに追加しておきます。
1$ sudo ip route add 169.254.0.0/16 via 172.31.0.1 dev ens5
2$ sudo ip route add 172.31.0.0/16 via 172.31.0.1 dev ens5
3
4$ ip route
5default dev ip6tnl scope link
6default via 172.31.0.1 dev ens5 proto dhcp src 172.31.0.4 metric 512
7169.254.0.0/16 via 172.31.0.1 dev ens5
8172.31.0.0/20 dev ens5 proto kernel scope link src 172.31.0.4 metric 512
9172.31.0.0/16 via 172.31.0.1 dev ens5
10172.31.0.1 dev ens5 proto dhcp scope link src 172.31.0.4 metric 512
11172.31.0.2 dev ens5 proto dhcp scope link src 172.31.0.4 metric 512
動作確認
ターミナルを2つ立ち上げて、片方で ping を送信しつつ、もう片方で tcpdump で通信を確認します。
インターネット (8.8.8.8) との通信
tcpdump は次のような結果になり、ip6tnl インターフェースに IPv4 パケットが書き込まれ、それがカプセル化された IPv6 パケットが ens5 インターフェースを通して自宅ルータに送信されています。
また、戻りの IPv6 パケットが自宅ルータから ens5 に届き、カプセル化が解除されて ip6tnl から出てきており、全て期待通りの結果です。
116:22:09.619390 ip6tnl Out IP 172.31.0.4 > 8.8.8.8: ICMP echo request, id 4, seq 1, length 64
216:22:09.619401 ens5 Out IP6 2406:XXXX:XXXX:XXXX::4 > 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX: DSTOPT IP 172.31.0.4 > 8.8.8.8: ICMP echo request, id 4, seq 1, length 64
316:22:09.630760 ens5 In IP6 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX > 2406:XXXX:XXXX:XXXX::4: IP 8.8.8.8 > 172.31.0.4: ICMP echo reply, id 4, seq 1, length 64
416:22:09.630760 ip6tnl In IP 8.8.8.8 > 172.31.0.4: ICMP echo reply, id 4, seq 1, length 64
自宅マシンとの通信
自宅マシンに ping を送信しても、問題なく通信できています。
116:20:53.040679 ip6tnl Out IP 172.31.0.4 > 192.168.1.10: ICMP echo request, id 3, seq 1, length 64
216:20:53.040693 ens5 Out IP6 2406:XXXX:XXXX:XXXX::4 > 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX: DSTOPT IP 172.31.0.4 > 192.168.1.10: ICMP echo request, id 3, seq 1, length 64
316:20:53.048117 ens5 In IP6 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX > 2406:XXXX:XXXX:XXXX::4: IP 192.168.1.10 > 172.31.0.4: ICMP echo reply, id 3, seq 1, length 64
416:20:53.048117 ip6tnl In IP 192.168.1.10 > 172.31.0.4: ICMP echo reply, id 3, seq 1, length 64
逆に、自宅マシンから EC2 インスタンスへの ping も当然成功しています。
116:23:57.926658 ens5 In IP6 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX > 2406:XXXX:XXXX:XXXX::4: IP 192.168.1.10 > 172.31.0.4: ICMP echo request, id 6, seq 1, length 64
216:23:57.926658 ip6tnl In IP 192.168.1.10 > 172.31.0.4: ICMP echo request, id 6, seq 1, length 64
316:23:57.926701 ip6tnl Out IP 172.31.0.4 > 192.168.1.10: ICMP echo reply, id 6, seq 1, length 64
416:23:57.926710 ens5 Out IP6 2406:XXXX:XXXX:XXXX::4 > 2400:4050:XXXX:XXXX:260:XXXX:XXXX:XXXX: DSTOPT IP 172.31.0.4 > 192.168.1.10: ICMP echo reply, id 6, seq 1, length 64
残タスク:OSを再起動すると設定が消える
トンネルインターフェースや経路を手で設定したので、OS を再起動すると当然設定が消えます。
また、"systemd-networkd.service" が restart されると、リンクローカルアドレスや VPC アドレス宛の経路が消えます。
OS 再起動時に再設定すれば良いだけではありますが、ちゃんとやるなら次のようにすれば良いですね。
- トンネルインターフェースとデフォルトルートの設定は、systemd で OS 起動時に行う
- リンクローカルアドレスや VPC アドレス宛の経路設定は、systemd-networkd.service の追加設定で行う
これについては特に思い入れがなくて検証が面倒なので、とりあえず手で設定する方法にしておきます。
今回のトンネル方式の問題
今回の記事ではシンプルな IPv4 over IPv6 トンネルを用いて実現しましたが、この方式では送信元 IPv6 アドレスが EC2 インスタンスのものに偽装されたパケットを自宅ルータが受信した場合に問題が生じます。
悪意を持った人にカプセル化されたパケットを観測されない限りそうはならないはずですが、そうなった場合には例えば次のような問題にも繋がります。
- インターネット上のサーバに対する DDoS 攻撃の踏み台にされる
- カプセル化された IPv4 をそのままインターネットに転送しているため
- 自宅内ネットワーク情報が外部に露出する
- 自宅内への IPv4 通信に対する応答が暗号化されずに EC2 インスタンスに返るため
そのため、本格的に実施する際は暗号化されたトンネルを利用するべきです。
現在自宅で利用しているルータでは IPsec サーバ機能があるのでそれを利用するか、あるいは自宅内で常時起動している Raspberry PI を使って WireGuard でトンネルを張るか、選択肢はいくつかあるので後日試してみます。
※追記 : WireGuard トンネルの利用については「EC2から自宅経由でIPv4インターネット接続 (WireGuardトンネル編) に、IPsec の利用については「LibreswanとUNIVERGE IX2105の間でIPsec IKEv2トンネルを張る」に書きました。
まとめ
ひとまず無事に EC2 インスタンスから自宅経由で IPv4 インターネットにアクセスできるようになりました。
パブリック IPv4 アドレスは年間 43.8 ドル程度 ($0.005 * 24 * 365) かかりますが、これを節約できたことになります。
EC2 インスタンスからの外向きのトラフィックはもともと多くなく、カプセル化によってオーバーヘッドが増えるものの、IPv4 アドレス料金と比べれば微々たるものです。
また、自宅ネットワーク側での何らかのトラブルやインターネット回線のメンテナンスがあると通信できなくなるのも難点です。
ただし、単なる IPv4 over IPv6 トンネルだけではいろいろと問題があるため、本格的に実施するなら暗号化されたトンネルを利用する必要があります。
早く IPv6 インターネットが当たり前な世の中になって欲しいものです。