📩

LambdaからIPv6でアクセスできるようにする

2024/04/21に公開

AWSでは各種サービスのグローバルIPv4アドレスが有料化されることになり、EC2などで一部無料枠もあるものの、基本的にはIPアドレスあたり概ね500〜600円程度の課金額がかかるようになりました。

無償版G Suiteが廃止されるのでAWS SESとDockerでメール送受信システムを作ったで作成したシステムでは最終的にLightsailで中継用のIMAP/POP3サーバーを作ることになりましたが、こちらもIPv6オンリーのインスタンスに変更しなければ値上げとなりました。

LightsailをIPv6インスタンスに変更するにあたって、LambdaのコードからIPv6にアクセスが必要となりましたが、そのためにはLambdaにVPCアウトバウンド接続の設定が必要となったので、上記のメール受信システムのアップデートに合わせて行った設定の要点をAWS CDKのコードベースで説明します。

要点

  • LambdaからIPv6接続をしようとする場合、VPC Lambdaを構成し、IPv6の設定を加える必要がある
  • 適当にVPCを設定するとNAT Gatewayが立ち上がってしまい、課金額が上がってしまう
  • VPC Lambdaでお金をかけずにアクセスできるAWSサービスはS3とDynamoDBのみ

node.jsのバージョンアップ

アプリケーションのnode.jsバージョンが古かったこともありますが、特にIPv6周りの挙動はnode.js 18以降で変わっているとのことだったので二度手間を防ぐ意味もありnode.js 20系にアップグレードしました。

CDKのコードを修正し、IPv6でのアクセスが必要な処理をVPC Lambdaにする

ここからは、MailForwarder(メール受信システム)のCDKスクリプトの変更点を元に、必要だった設定を見ていきます。

VPCの作成

まず、VPCを作成します。
IPv4/IPv6のデュアルスタックし、PRIVATE_WITH_EGRESSのサブネットを作りました。

const vpc = new ec2.Vpc(this, "MailForwarderVPC", {
  natGateways: 0,  // NAT Gatewayは必ず0に!
  maxAzs: 2,
  subnetConfiguration: [
    {
      name: "private",
      subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,  // 外部へ出て行けるプライベートネットワーク
    }
  ],
  ipProtocol: ec2.IpProtocol.DUAL_STACK,  // IPv4/IPv6デュアルスタック
  enableDnsHostnames: true,
  enableDnsSupport: true,
});

注意ポイントとしては、NAT Gatewayを無効にすることです。NAT Gatewayの料金は東京リージョンで0.062ドル/時、概ね6700円くらいです(1ドル150円換算)。個人で使うには高いので絶対に停めておきます。

Gateway Endpoint を追加する

VPCの内部からAWSサービスのAPIを使用しようとする場合、インターネット接続が必要となりますが、このためにはNAT GatewayもしくはPrivateLinkが必要になります。上述の通り、NAT Gatewayの費用は高いため使用したくありません。
ただし、S3とDynamoDBに限りGateway Endpointが使用でき、無料でアクセスできるように設定できます。

処理によってはこれでは不足し、場合によってはアーキテクチャ変更が必要になるかもしれませんが、今回の処理で依存していたのはDynamoDBとS3のみだったためこれだけで足りました。

というわけで、S3とDynamoDBのGateway EndpointをVPCに設定します。

vpc.addGatewayEndpoint("S3Gateway", {
  service: ec2.GatewayVpcEndpointAwsService.S3,
});
vpc.addGatewayEndpoint("DynamoDBGateway", {
  service: ec2.GatewayVpcEndpointAwsService.DYNAMODB,
});

Security Groupを定義する

VPCから外部に出ていくためのセキュリティグループを定義します。

const securityGroup = new ec2.SecurityGroup(this, "MailForwarderSecurityGroup", {
  vpc,
  allowAllOutbound: true,
  allowAllIpv6Outbound: true,
});

Lambdaの実行ロールにVPCアクセス実行のロールを追加する

VPC Lambdaのデプロイ時にはLambdaにVPCの設定をするための権限が必要となります。
service-role/AWSLambdaVPCAccessExecutionRole というマネージドポリシーがあるのでこれを追加します。

const functionRole = new iam.Role(this, "ForwarderFunctionsRole", {
  assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"),
    iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaVPCAccessExecutionRole"),
  ],
})

Lambda に VPC の設定を加える

ここまでで設定したVPCとセキュリティグループの設定をLambdaに追加します。
また ipv6AllowedForDualStack の設定も必要です。

const forwardMailFunction = new lambda.Function(this, "ForwardMailHandler", {
  runtime: lambda.Runtime.NODEJS_20_X,
  code,
  environment,
  role: functionRole,
  handler: "index.forwardMailTopicHandler",
  timeout: Duration.seconds(20),
/* ↓↓↓↓↓↓ ここから ↓↓↓↓↓↓ */
  vpc,
  securityGroups: [securityGroup],
  ipv6AllowedForDualStack: true,
/* ↑↑↑↑↑↑ ここまで ↑↑↑↑↑↑ */
});

ここまでの設定をデプロイすることで、LambdaからAAAAレコードのみを定義したドメインに接続が可能になりました。

Discussion