AWS CDKでIPv6オンリーなVPCとEC2を作成してみたけど、なかなか厳しいものがある
AWSのパブリックIPv4アドレスの有料化されたので、無駄なお金は使いたくないなーと思って、パブリックIPv4は持たずにIPv6を持つ検証用のEC2を作りたいなーと思っていました。
僕はAWS CDKが好きなので、AWS CDKでIPv6なVPCとEC2を作成します。
以前調べたときは、AWS CDKではまだIPv6に対応していなかったのですが、バージョン v2.122.0
で対応したようです。やったぜ。
Features
ec2: add dual stack vpc support (#28480) (caf83f1), closes #894
どうせならSSMセッションマネージャーでEC2に接続できたら便利じゃん?とも思いましたが、現時点(2024/03/08)ではIPv6に対応していないようです。残念。
詳しくはこちらのブログを御覧ください。
今回は、SSHでEC2に直接接続する方針で、IPv6オンリーなEC2を作成します。
前提条件
AWS CDKはv2を使用します。 v2.122.0
以降であれば動くと思います。
$ cdk --version 2.131.0 (build 92b912d)
CDKでIPv6なVPCとEC2を作成する
TypeScriptのcdkでIPv6なVPCとEC2を作成するコードは次の通りです。
import { Construct } from "constructs"; import * as cdk from "aws-cdk-lib"; import * as ec2 from "aws-cdk-lib/aws-ec2"; import * as iam from "aws-cdk-lib/aws-iam"; const myKeyPairName: string = "<<YOU_KEY_PAIR_NAME>>"; const myIpV6Cidr: string = "<<YOUR_IPv6_CIDR_BLOCK>>"; export class SampleStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc = new ec2.Vpc(this, "MyVpc", { natGateways: 0, maxAzs: 1, ipProtocol: ec2.IpProtocol.DUAL_STACK, subnetConfiguration: [ { name: "PublicSubnet", subnetType: ec2.SubnetType.PUBLIC, }, ], }); const instance = new ec2.Instance(this, "MyInstance", { vpc, instanceType: ec2.InstanceType.of( ec2.InstanceClass.T3A, ec2.InstanceSize.NANO, ), machineImage: ec2.MachineImage.latestAmazonLinux2023(), vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, keyPair: ec2.KeyPair.fromKeyPairName(this, "MyKeyPair", myKeyPairName), allowAllIpv6Outbound: true, associatePublicIpAddress: false, }); instance.connections.allowFrom( ec2.Peer.ipv6(myIpV6Cidr), ec2.Port.tcp(22), "Allow SSH access from my IPv6 CIDR", ); instance.role.addManagedPolicy( iam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess"), ); new cdk.CfnOutput(this, "GetIpv6AddressCommand", { value: `INSTANCE_ID=${instance.instanceId} aws ec2 describe-instances --instance-ids \${INSTANCE_ID} --query "Reservations[].Instances[].Ipv6Address" --output text`, }); } } const app = new cdk.App(); new SampleStack(app, "SampleStack");
次のコマンドを実行してデプロイすると、IPv6なVPCとEC2が作成できます。簡単ですね。
$ cdk deploy SampleStack
ちょっとコードの解説します。
コピペして使う場合は 6,7行目 の、 myKeyPairName
に自分で作成したキーペア名を指定してください。
myIpV6Cidr
に自分のIPv6アドレスのCIDRを指定してください。
自分のIPv6アドレスがわからなかったら、次のような自分のIPv6アドレスを表示するサイトとか使ってください。
16行目 で、VPCの ipProtocol
に ec2.IpProtocol.DUAL_STACK
を指定しています。
こうすることでIPv4,IPv6どちらも持つVPCを作成できます。
EC2といっしょに作られるセキュリティグループは、デフォルトIPv6のアウトバウンドトラフィックを許可しません。
36行目 で、Instanceの allowAllIPv6Outbound
を true
に設定することで、IPv6のアウトバウンドトラフィックを全て許可できます。
37行目 で、EC2はIPv4のパブリックIPアドレスは持ちたくないので、Instanceの associatePublicIpAddress
を false
に設定しています。
40〜44行目 で、Instanceの connections.allowFrom
を利用して、自分のIPv6アドレスからのSSH接続を許可しています。
50〜52行目 で、現状、CDK(CloudFormation)でEC2のIPv6アドレスを出力する方法がないので、苦肉の策としてAWS CLIでEC2のIPv6アドレスを取得するコマンドをエクスポートしています。
このcdkをデプロイすると、アウトプットとして、IPv6アドレスを取得するためのこんな感じのAWS CLIコマンドを出力します。
Outputs: SampleStack.GetIpv6AddressCommand = INSTANCE_ID=<<インスタンスID>> aws ec2 describe-instances --instance-ids ${INSTANCE_ID} --query "Reservations[].Instances[].Ipv6Address" --output text
SSHでEC2に接続してみる
EC2が構築できたら、IPv6アドレスを調べてSSHで接続してみましょう。
EC2のIPv6アドレスは、エクスポートしたAWS CLIを実行すれば取得できます。
$ INSTANCE_ID=<<インスタンスID>> aws ec2 describe-instances --instance-ids ${INSTANCE_ID} --query "Reservations[].Instances[].Ipv6Address" --output text 2001:0db8:0000:0000:0000:0000:0000:0001
IPv6でも普通にSSHできました。 curl ifconfig.io
で外部ネットワークに接続できることも確認できました。良いですね。
AWS CLIを使ってみる…?
ネットワークに繋がることが確認できたので、AWS CLIを使ってみます。 次のコマンドを実行してみると…、あれ?応答が返ってこないぞ。
$ aws ec2 describe-instances Connect timeout on endpoint URL: "https://ec2.ap-northeast-1.amazonaws.com/"
なぜAWS CLIが使えないのか
普段意識していないので忘れがちなのですが、AWS CLIはAWSのWebAPIのラッパーとして動いています。
そのため、IPv6オンリーなEC2で使うためには、AWS APIがIPv6に対応している必要があります。
このAWS APIのURLは エンドポイント と呼ばれています。
AWS CLIを使う際、エンドポイントを指定しなければ、基本的に各サービスの リージョンエンドポイント が使われます。
そしていくつかのサービスは、 デュアルスタックエンドポイント という IPv4/IPv6両方に対応したエンドポイント を持っています。
そして、AWSサービスによって リージョンエンドポイント と デュアルスタックエンドポイント が 異なる ものと、 同じ ものがあります。
例えば、EC2の場合、リージョンエンドポイントとデュアルスタックエンドポイントは 異なります 。
Secrets Managerの場合、リージョンエンドポイントとデュアルスタックエンドポイントは 同じ です。
IPv6に対応しているAWSサービスの詳細は、次のドキュメントで確認できます。
具体的にEC2のドキュメントを見ると、通常のリージョンエンドポイントは IPv4しかサポートしていない ことがわかります。だから、IPv6環境から繋がらなかったんですね…。
また、IPv6に対応したデュアルスタックエンドポイントを持っていることもわかります。 東京(ap-northeast-1)リージョンには無さそうですが…。
IPv6環境でAWS CLIを使ってみる
東京リージョンにデュアルスタックエンドポイントが無かったので、オレゴン(us-west-2)リージョンでEC2を再作成して試してみます。
普通にやると、やっぱり応答が返ってきません。
$ aws ec2 describe-instances Connect timeout on endpoint URL: "https://ec2.us-west-2.amazonaws.com/"
オレゴンリージョンのデュアルスタックエンドポイントは ec2.us-west-2.api.aws
です。
なので、AWS CLI実行時に --endpoint-url
オプションでエンドポイントを指定すると、IPv6環境でもAWS CLIを使うことができます。
$ aws ec2 describe-instances --endpoint-url https://ec2.us-west-2.api.aws
ちなみに、AWS CLIの場合、環境変数や設定ファイルに次のような設定を追加することで、デフォルトでデュアルスタックエエンドポイントを使えます。
なので、こういうコマンドでも動きます。こっちの方が楽ですね。
$ export AWS_USE_DUALSTACK_ENDPOINT=true $ aws ec2 describe-instances
AWS CLIの実行だけでも、IPv6オンリーだとちゃんとAWSサービスのエンドポイントがIPv6に対応しているのかとか、気にしなきゃいけないことが多いですね。厳しい。
まとめ
AWS CDKでIPv6なVPCとEC2を作成してみました。
作っては見たものの、IPv4だけ対応しているWebサービスも多いので、検証環境と言えどもIPv6オンリーな環境で動かすのは厳しいものがありますね。
まだまだ、IPv4のお世話になりそうです。