IPv6-onlyなEC2からCloudflare WARPでIPv4通信してみた
2024年2月1日より、AWSのPublic IPv4アドレスに課金が開始されました。その料金は$0.005/時間、月額(30日)に換算すると約$3.6/アドレスとなり、t4g.nanoの月額料金(約$3.94)に匹敵するコストが発生するようになりました。
一方で、SSM(Systems Manager)など、IPv4のみで提供されるAWSサービスが2025年1月時点では存在。IPv4パブリックアドレスを持たないEC2からこれらのサービスを利用するためには、NAT GatewayやVPCエンドポイントなどが必要でした。
今回、IPv4パブリックIPやNAT Gatewayを省略しても、Cloudflare WARPのトンネル(IPv4 over IPv6)経由でIPv4通信が可能であるか、確認を試みる機会がありましたので、紹介します。
アーキテクチャ概要
今回構築する環境のコンポーネントは以下の通りです。
- VPC: IPv4/IPv6デュアルスタック(ただしIPv4はプライベートのみ、IGWも省略)
- Egress-Only Internet Gateway: IPv6アウトバウンド専用
- S3 Gateway Endpoint: Amazon Linux 2023公式リポジトリからのパッケージ取得や、WARP RPMの仮置場のS3接続経路として利用
- EC2 Instance Connect Endpoint: Egress-Only Gateway環境で外部からのSSH接続経路として利用
- Cloudflare WARP: IPv4 over IPv6 実現
なぜこの構成が動くのか
Cloudflare WARPの仕組み
Cloudflare WARPはWireGuardベースのVPNトンネルです。IPv6経由でCloudflareに接続し、CloudflareがIPv4への変換を行うことで、IPv4 over IPv6を実現しています。
環境構築
VPCの構築(vpc-template.yaml)
以下のリソースを含むVPCをCloudFormationで構築しました。
- VPC(IPv4/IPv6デュアルスタック)
- プライベートサブネット x 2
- Egress-Only Internet Gateway
- S3 Gateway Endpoint
- EC2 Instance Connect Endpoint
テンプレート全文
AWSTemplateFormatVersion: '2010-09-09'
Description: 'IPv6-only VPC Test - Complete private VPC with IPv6 connectivity'
Parameters:
ProjectName:
Type: String
Default: 'ipv6-vpc-test'
Description: 'IPv6-only VPC Test - Project name prefix for AWS resources'
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: '192.168.0.0/18'
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Ref ProjectName
# IPv6 CIDR Block
IPv6CidrBlock:
Type: AWS::EC2::VPCCidrBlock
Properties:
VpcId: !Ref VPC
AmazonProvidedIpv6CidrBlock: true
# Private Subnets
PrivateSubnet1:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
CidrBlock: '192.168.0.0/20'
AvailabilityZone: !Select [0, !GetAZs '']
Ipv6CidrBlock: !Select [0, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 2, 64]]
AssignIpv6AddressOnCreation: true
Tags:
- Key: Name
Value: !Sub '${ProjectName}-private-subnet-1a'
PrivateSubnet2:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
CidrBlock: '192.168.16.0/20'
AvailabilityZone: !Select [1, !GetAZs '']
Ipv6CidrBlock: !Select [1, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 2, 64]]
AssignIpv6AddressOnCreation: true
Tags:
- Key: Name
Value: !Sub '${ProjectName}-private-subnet-1c'
# Egress-Only Internet Gateway (IPv6)
EgressOnlyInternetGateway:
Type: AWS::EC2::EgressOnlyInternetGateway
Properties:
VpcId: !Ref VPC
# Private Route Table
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${ProjectName}-private-rt'
# IPv6 Route (via Egress-Only IGW)
PrivateRouteIPv6:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationIpv6CidrBlock: '::/0'
EgressOnlyInternetGatewayId: !Ref EgressOnlyInternetGateway
# Subnet Associations
PrivateSubnet1Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
# VPC Endpoint - S3
S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
VpcEndpointType: Gateway
RouteTableIds:
- !Ref PrivateRouteTable
# EC2 Instance Connect Endpoint
EC2InstanceConnectEndpoint:
Type: AWS::EC2::InstanceConnectEndpoint
Properties:
SubnetId: !Ref PrivateSubnet1
SecurityGroupIds:
- !Ref EC2InstanceConnectSecurityGroup
Tags:
- Key: Name
Value: !Sub '${ProjectName}-eice'
# Security Group for EC2 Instance Connect Endpoint
EC2InstanceConnectSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for EC2 Instance Connect Endpoint
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 192.168.0.0/18
Tags:
- Key: Name
Value: !Sub '${ProjectName}-eice-sg'
Outputs:
VPCId:
Description: 'VPC ID'
Value: !Ref VPC
Export:
Name: !Sub '${ProjectName}-vpc-id'
PrivateSubnet1Id:
Description: 'Private Subnet 1 ID'
Value: !Ref PrivateSubnet1
Export:
Name: !Sub '${ProjectName}-private-subnet-1-id'
PrivateSubnet2Id:
Description: 'Private Subnet 2 ID'
Value: !Ref PrivateSubnet2
Export:
Name: !Sub '${ProjectName}-private-subnet-2-id'
WARP RPMの準備
Amazon Linux 2023用のCloudflare WARP公式リポジトリは存在しなかったため、CentOS/RHEL8用のものを代用しました。
WARPの依存パッケージ( desktop-file-utils nftables nss-tools)はAmazon Linux 2023の公式リポジトリからインストール。
WARP本体はCloudflareダウンロードページからCentOS/RHEL8用のRPMをダウンロードしてS3に保存、インストールに利用しました。
- ダウンロード元: https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/warp/download-warp/
- 使用バージョン: Linux用 CentOS/RHEL8(arm64)
- Version: 2025.9.558.0
- Date: 2025-11-11
EC2インスタンスの構築(ec2-warp-launch-template.yaml)
以下のリソースを含むEC2環境をCloudFormationで構築しました。
- IAMロール(SSM + S3アクセス用)
- セキュリティグループ(EC2 Instance Connect EndpointからのSSHを許可)
- Launch Template(UserDataでCloudflare WARPを設定)
- EC2インスタンス(Launch Templateを利用して起動)
テンプレート全文
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Launch Template with Cloudflare WARP in IPv6-only VPC (ARM64 Optimized)'
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: 'VPC ID'
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: 'Subnet ID'
InstanceType:
Type: String
Default: 't4g.micro'
AllowedValues: ['t4g.nano', 't4g.micro', 't4g.small']
Description: 'EC2 instance type (ARM64 only)'
KeyPairName:
Type: AWS::EC2::KeyPair::KeyName
Description: 'EC2 Key Pair for SSH access'
WarpRpmS3Uri:
Type: String
Description: 'S3 URI for WARP RPM'
ImageId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64'
Description: 'AMI ID for EC2 instance'
Resources:
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${AWS::StackName}-ec2-role'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Policies:
- PolicyName: WarpS3Access
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:GetObject
Resource: arn:aws:s3:::cloudflare-warp-temp/*
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub '${AWS::StackName}-instance-profile'
Roles:
- !Ref EC2Role
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security group for WARP EC2 instance'
VpcId: !Ref VpcId
SecurityGroupEgress:
- IpProtocol: -1
CidrIpv6: '::/0'
Description: 'All outbound traffic over IPv6'
- IpProtocol: -1
CidrIp: '0.0.0.0/0'
Description: 'All outbound traffic over IPv4'
Tags:
- Key: Name
Value: 'warp-ec2-sg'
WarpLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub '${AWS::StackName}-launch-template'
LaunchTemplateData:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
KeyName: !Ref KeyPairName
IamInstanceProfile:
Arn: !GetAtt EC2InstanceProfile.Arn
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref SubnetId
Ipv6AddressCount: 1
Groups:
- !Ref EC2SecurityGroup
UserData:
Fn::Base64: !Sub |
#!/bin/bash
# Install dependencies
dnf install -y desktop-file-utils nftables nss-tools
# Download and install WARP
aws s3 cp "${WarpRpmS3Uri}" /tmp/cloudflare-warp.rpm
rpm -ivh /tmp/cloudflare-warp.rpm
# Start WARP service
systemctl enable --now warp-svc
for i in {1..30}; do warp-cli status &>/dev/null && break; sleep 2; done
# Register and configure WARP
script -q -c "yes | warp-cli registration new" /dev/null
warp-cli mode warp
# Exclude IPv6 from WARP tunnel (IPv4 only through WARP)
warp-cli tunnel ip add-range ::/0
warp-cli connect
# Restart SSM agent
systemctl restart amazon-ssm-agent
WarpEC2Instance:
Type: AWS::EC2::Instance
Properties:
LaunchTemplate:
LaunchTemplateId: !Ref WarpLaunchTemplate
Version: !GetAtt WarpLaunchTemplate.LatestVersionNumber
Outputs:
InstanceId:
Description: 'EC2 Instance ID'
Value: !Ref WarpEC2Instance
Export:
Name: !Sub '${AWS::StackName}-instance-id'
WARP設定のポイント
- warp-svcの起動には数十秒かかる場合があるため、待機処理を追加しました
# WARPサービスの有効化・起動
systemctl enable --now warp-svc
# サービス起動待機(最大60秒)
for i in {1..30}; do warp-cli status &>/dev/null && break; sleep 2; done
warp-cli registration new時、インタラクティブに規約への同意を求められますが、疑似TTYで対応しました
# WARPの登録
script -q -c "yes | warp-cli registration new" /dev/null
- IPv6通信は、WARPトンネル対象から除外する、スプリットトンネル設定を実施しました。
# IPv6をWARPトンネルから除外(IPv4のみWARP経由)
warp-cli tunnel ip add-range ::/0
Cloudflare WARPの利用規約や制約は、公式ドキュメント、FAQ(https://developers.cloudflare.com/warp-client/known-issues-and-faq/ )を確認してご利用ください。
動作確認
EC2 Instance Connect Endpoint経由でSSH接続して確認を行いました。
$ aws ec2-instance-connect ssh --instance-id i-xxxxxxxxxxxxx71f5 --region ap-northeast-1
WARPステータス確認
$ warp-cli status
Status update: Connected
Network: healthy
IPv6通信の確認
$ curl -6 -s https://ipv6.icanhazip.com
2406:da14:xxxx:xxxx:xxxx:xxxx:848b:afaa
EC2に割り当てられたグローバルIPv6アドレスが返却されました。これはEgress-Only IGWを経由しています。
IPv4通信の確認
$ curl -4 -s https://ipv4.icanhazip.com
104.xx.xxx.105
Cloudflareが保有するIPv4アドレスが返却されました。WARPトンネルが正常に機能していることを確認できました。
SSM接続の確認
WARPの設定後、SSMの「start-session」が利用できることを確認できました。
$ aws ssm start-session --target i-xxxxxxxxxxxxx71f5 --region ap-northeast-1
パブリックなIPv4アドレスや、NAT Gateway、VPCエンドポイント(ssm、ssmmessages、ec2messages)が存在しないEC2でも、セッションマネージャーを利用した接続ができました。

コスト比較
| 項目 | EC2パブリックIP | NAT Gateway | Cloudflare WARP |
|---|---|---|---|
| 固定費 | $0.005/時間(約$3.6/月) | $0.062/時間(約$45/月) | $0 |
| EC2データ転送(イン) | $0 | $0 | $0 |
| EC2データ転送(アウト) | $0.114/GB | $0.114/GB | $0.114/GB |
| NAT Gateway処理 | - | $0.062/GB | - |
まとめ
IPv6-only、NAT Gatewayの存在しないVPC環境でも、Cloudflare WARPを利用することで、IPv4通信を必要とするSSMなどのサービスが利用できることを確認しました。
IPv6通信を主に利用し、補助用途でIPv4通信を行うワークロード、特に低スペックなインスタンスや、 2026年末まで延長されたt4g.smallの無料枠を活用したコストを抑制した開発環境では、WARP による IPv4 over IPv6 接続が効果的です。
ただし、Cloudflare WARPの動作対象OSにAmazon Linux 2023は含まれておらず、Cloudflare・AWS双方からのサポートも期待できません。今回紹介した構成については、サービス影響のない検証環境や個人利用に留めることを強くおすすめします。
将来的には、AWS公式で低コストで利用できるIPv4 over IPv6の仕組みの提供や、AWSを含めた各社サービスのDualStackサポートが進み、IPv6ネイティブで実用可能な環境が整備されることに期待したいと思います。
参考リンク







