When communicating externally from an IPv4 environment, it is generally considered standard to do so via a NAT Gateway. Since NAT Gateways incur charges based on traffic volume, we want to utilize IPv6 wherever possible.
Even Interface-type VPCEndpoints can add up to significant costs over time.
Created with VPCv2 constructs
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { SubnetType, CfnEIP, CfnSubnet } from 'aws-cdk-lib/aws-ec2';
import * as aws_ec2 from '@aws-cdk/aws-ec2-alpha';
import { IpAddressType } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { AvailabilityZoneRebalancing } from 'aws-cdk-lib/aws-ecs';
export class VPCSubnet extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);
const myVpc = new aws_ec2.VpcV2(this, 'Vpc', {
primaryAddressBlock: aws_ec2.IpAddresses.ipv4('10.1.0.0/16'),
vpcName: 'vpc-Develop-Network',
secondaryAddressBlocks: [aws_ec2.IpAddresses.amazonProvidedIpv6({
cidrBlockName: 'AmazonProvided'
})]
});
// Internet Gateway for Public Subnet
const igw = new aws_ec2.InternetGateway(this, 'IGW', {
vpc: myVpc
});
// Egress-Only Internet Gateway for IPv6
const eigw = new aws_ec2.EgressOnlyInternetGateway(this, 'EIGW', {
vpc: myVpc
});
// Public Subnets (Multi-AZ)
const publicRouteTable = new aws_ec2.RouteTable(this, 'PublicRouteTable', {
vpc: myVpc
});
publicRouteTable.addRoute('PublicIGWRoute', '0.0.0.0/0', { gateway: igw });
const publicSubnetAZ1Name = cdk.Fn.join('', ['sub-public-', cdk.Fn.select(0, cdk.Fn.getAzs())]);
const publicSubnetAZ1 = new aws_ec2.SubnetV2(this, 'PublicSubnetAZ1', {
vpc: myVpc,
subnetName: publicSubnetAZ1Name,
availabilityZone: cdk.Fn.select(0, cdk.Fn.getAzs()),
ipv4CidrBlock: new aws_ec2.IpCidr('10.1.0.0/24'),
//ipv6CidrBlock: new aws_ec2.IpCidr(cdk.Fn.select(0, cdk.Fn.cidr(cdk.Fn.select(0, myVpc.ipv6CidrBlocks), 4, '64'))),
subnetType: SubnetType.PUBLIC,
routeTable: publicRouteTable
});
// Explicitly set Name tag
const cfnPublicSubnetAZ1 = publicSubnetAZ1.node.defaultChild as CfnSubnet;
cdk.Tags.of(cfnPublicSubnetAZ1).add('Name', publicSubnetAZ1Name);
const publicSubnetAZ2Name = cdk.Fn.join('', ['sub-public-', cdk.Fn.select(1, cdk.Fn.getAzs())]);
const publicSubnetAZ2 = new aws_ec2.SubnetV2(this, 'PublicSubnetAZ2', {
vpc: myVpc,
subnetName: publicSubnetAZ2Name,
availabilityZone: cdk.Fn.select(1, cdk.Fn.getAzs()),
ipv4CidrBlock: new aws_ec2.IpCidr('10.1.1.0/24'),
//ipv6CidrBlock: new aws_ec2.IpCidr(cdk.Fn.select(1, cdk.Fn.cidr(cdk.Fn.select(0, myVpc.ipv6CidrBlocks), 4, '64'))),
subnetType: SubnetType.PUBLIC,
routeTable: publicRouteTable
});
// Explicitly set Name tag
const cfnPublicSubnetAZ2 = publicSubnetAZ2.node.defaultChild as CfnSubnet;
cdk.Tags.of(cfnPublicSubnetAZ2).add('Name', publicSubnetAZ2Name);
// Elastic IP for NAT Gateway
// const eip = new CfnEIP(this, 'NatEIP', {
// domain: 'vpc'
// });
// NAT Gateway in Public Subnet
// const natGateway = new aws_ec2.NatGateway(this, 'NatGateway', {
// subnet: publicSubnetAZ1,
// allocationId: eip.attrAllocationId
// });
// Private Subnets (Multi-AZ) with NAT Gateway
const privateRouteTable = new aws_ec2.RouteTable(this, 'PrivateRouteTable', {
vpc: myVpc
});
// privateRouteTable.addRoute('PrivateNatRoute', '0.0.0.0/0', { gateway: natGateway });
privateRouteTable.addRoute('PrivateEIGWRoute', '::/0', { gateway: eigw });
const privateSubnetAZ1Name = cdk.Fn.join('', ['sub-private-', cdk.Fn.select(0, cdk.Fn.getAzs())]);
const privateSubnetAZ1 = new aws_ec2.SubnetV2(this, 'PrivateSubnetAZ1', {
vpc: myVpc,
subnetName: privateSubnetAZ1Name,
availabilityZone: cdk.Fn.select(0, cdk.Fn.getAzs()),
ipv4CidrBlock: new aws_ec2.IpCidr('10.1.2.0/24'),
ipv6CidrBlock: new aws_ec2.IpCidr(cdk.Fn.select(2, cdk.Fn.cidr(cdk.Fn.select(0, myVpc.ipv6CidrBlocks), 4, '64'))),
subnetType: SubnetType.PRIVATE_WITH_EGRESS,
routeTable: privateRouteTable
});
// Explicitly set Name tag
const cfnPrivateSubnetAZ1 = privateSubnetAZ1.node.defaultChild as CfnSubnet;
cdk.Tags.of(cfnPrivateSubnetAZ1).add('Name', privateSubnetAZ1Name);
const privateSubnetAZ2Name = cdk.Fn.join('', ['sub-private-', cdk.Fn.select(1, cdk.Fn.getAzs())]);
const privateSubnetAZ2 = new aws_ec2.SubnetV2(this, 'PrivateSubnetAZ2', {
vpc: myVpc,
subnetName: privateSubnetAZ2Name,
availabilityZone: cdk.Fn.select(1, cdk.Fn.getAzs()),
ipv4CidrBlock: new aws_ec2.IpCidr('10.1.3.0/24'),
ipv6CidrBlock: new aws_ec2.IpCidr(cdk.Fn.select(3, cdk.Fn.cidr(cdk.Fn.select(0, myVpc.ipv6CidrBlocks), 4, '64'))),
subnetType: SubnetType.PRIVATE_WITH_EGRESS,
routeTable: privateRouteTable
});
// Explicitly set Name tag
const cfnPrivateSubnetAZ2 = privateSubnetAZ2.node.defaultChild as CfnSubnet;
cdk.Tags.of(cfnPrivateSubnetAZ2).add('Name', privateSubnetAZ2Name);
}
}
Verifying IPv6 Connectivity with Lambda
Confirmed in VPC Lambda with "Allow IPv6 traffic for dual-stack subnets" enabled
This is a simple function that retrieves the source IPv6 address accessed from Lambda via ifconfig.me.
import json
import urllib.request
import json
import urllib.request
def lambda_handler(event, context):
"""
http://ifconfig.me/
"""
try:
# requestifconfig.me
with urllib.request.urlopen('http://ifconfig.me/', timeout=10) as response:
ip_address = response.read().decode('utf-8').strip()
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Successfully retrieved IP address',
'ip_address': ip_address
})
}
except urllib.error.URLError as e:
# network error
return {
'statusCode': 500,
'body': json.dumps({
'message': 'Error retrieving IP address',
'error': str(e)
})
}
except Exception as e:
# other error
return {
'statusCode': 500,
'body': json.dumps({
'message': 'Unexpected error occurred',
'error': str(e)
})
}
# for local test
if __name__ == '__main__':
# test event
test_event = {}
test_context = {}
result = lambda_handler(test_event, test_context)
print(json.dumps(result, indent=2, ensure_ascii=False))```
{% endraw %}
bash
Response:
{
"statusCode": 200,
"body": "{\"message\": \"Successfully retrieved IP address\", \"ip_address\": \"2600:1f13:94:1a02:ef65:ac4:50ab:5e1\"}"
}
{% raw %}
AWS Service
use_dualstack_endpoint True
https://docs.aws.amazon.com/vpc/latest/userguide/aws-ipv6-support.html
S3
python
my_config = Config(
use_dualstack_endpoint=True,
retries={'max_attempts': 3, 'mode': 'standard'}
)
bucket_name = ''
try:
s3_resource = boto3.resource("s3",config=my_config)
buckets = list(s3_resource.buckets.all())
for bucket in buckets:
bucket_name = bucket.name
logger.info(bucket_name)
except ClientError:
logger.exception("Couldn't get buckets.")
S3 Bucket Get Success
DynamoDB
try:
client = boto3.client('dynamodb',config=my_config)
response = client.get_item(
TableName='testTable',
Key={
'id': {
'S': '1'
},
}
)
print(response['Item'])
S3 Vectors
python
try:
bedrock = boto3.client("bedrock-runtime");
client = boto3.client('s3vectors',config=my_config)
response = bedrock.invoke_model(
modelId="amazon.titan-embed-text-v2:0",
body=json.dumps({"inputText": 'サンプル'})
)
# Extract embedding from response.
model_response = json.loads(response["body"].read())
embedding = model_response["embedding"]
response = client.query_vectors(
vectorBucketName='my-s3-vector-bucket',
indexName='my-s3-vector-index',
topK=3,
queryVector={
'float32': embedding
},
returnMetadata=True,
returnDistance=True
)
print(json.dumps(response["vectors"], indent=2))
except ClientError:
logger.exception("")
Bedrock Runtime
dualstack endpoint no support
ECS
ECS Fargate prioritizes IPv4 by default, so it attempts to connect via IPv4 even in IPv6 dual-stack environments.
bash
ResourceInitializationError: unable to pull secrets or registry auth: The task
cannot pull registry auth from Amazon ECR: There is a connection issue between the
task and Amazon ECR. Check your task network configuration. operation error ECR:
GetAuthorizationToken, exceeded maximum number of attempts, 3, https response error
StatusCode: 0, RequestID: , request send failed, Post
"https://api.ecr.us-west-2.amazonaws.com/": dial tcp 34.223.26.183:443: i/o timeout
https://github.com/aws/containers-roadmap/issues/2641
EKS Kubernetes
The ECR dualstack endpoint exists, so images can be pulled via the Egress out internet Gateway.
Initially, there was an issue where pulling was not possible.
Kubernetes supports IPv4/IPv6 dualstack, but EKS can only be configured as an IPv6-only cluster.
https://docs.aws.amazon.com/eks/latest/best-practices/ipv6.html
Summary
More services are becoming IPv6-capable. However, since some services still cannot use it, the current approach is to use VPC Endpoints alongside them.
Top comments (0)