使用Terraform无法通过VPC网关端点从私有子网连接到S3
问题:私有子网中的EC2实例无法通过VPC网关端点连接S3?
我正在用Terraform配置AWS三层架构,想给私有子网里的第二层实例设置每日自动备份到S3,不想走公网所以没开NAT Gateway,打算用VPC网关端点转发流量,但一直失败。从实例执行AWS命令都超时:
aws s3 cp /backup-data/hello.txt s3://mpp-bakcup-bucket/ec2/ Connect timeout on endpoint URL: "https://ec2.eu-central-1.amazonaws.com/"
或者:
aws ec2 describe-vpc-endpoints Connect timeout on endpoint URL: "https://ec2.eu-central-1.amazonaws.com/"
已做的排查:
- 架构能成功部署到AWS
- 可以通过堡垒主机SSH连接到私有实例
- 实例里已正确配置backup-user和定时任务,但执行以下命令都超时:
aws --region eu-central-1 s3 ls aws --region eu-central-1 ec2 describe-vpc-endpoints
Connect timeout on endpoint URL: "https://ec2.eu-central-1.amazonaws.com/"
相关Terraform代码:
- VPC、IGW、1个公有子网+2个私有子网
- 私有子网路由表:
resource "aws_route_table" "private_app_route_table_az1" { vpc_id = aws_vpc.vpc.id tags = { Name = "Private App Route Table az 1" } }
- S3网关端点(已关联路由表):
resource "aws_vpc_endpoint" "s3_endpoint_az1" { vpc_id = aws_vpc.vpc.id service_name = "com.amazonaws.${var.region}.s3" vpc_endpoint_type = "Gateway" tags = { Name = "my-s3-endpoint az 1" } } resource "aws_vpc_endpoint_route_table_association" "s3_endpoint_az1_rt_association" { route_table_id = aws_route_table.private_app_route_table_az1.id vpc_endpoint_id = aws_vpc_endpoint.s3_endpoint_az1.id }
- S3存储桶:
resource "aws_s3_bucket" "backup_bucket" { bucket = "mpp-bakcup-bucket" } resource "aws_s3_object" "ec2_directory" { bucket = aws_s3_bucket.backup_bucket.bucket key = "backups/ec2/" }
- 用户数据脚本配置:
data "template_file" "user_data" { template = file("../modules/vpc/user-data.tpl") vars = { ACCESS_KEY = aws_iam_access_key.backup_user_access_key.id SECRET_KEY = aws_iam_access_key.backup_user_access_key.secret REGION = var.region } } locals { ec2_user_data_script = data.template_file.user_data.rendered }
模板文件:
#!/bin/bash apt-get update mkdir /backup-data chown -R backup-user:backup-user /backup-data echo "hello world!" > /backup-data/hello.txt sudo -u backup-user bash -c 'echo -e "${ACCESS_KEY}\n${SECRET_KEY}\n${REGION}\n\n" | aws configure' sudo -u backup-user crontab -l | { cat; echo '0 1 * * * aws s3 cp /backup-data/hello.txt s3://mpp-bakcup-bucket/ec2/'; } | sudo -u backup-user crontab - service cron restart
- IAM用户及策略:
resource "aws_iam_policy" "s3_access_policy" { name = "s3-access-policy" description = "Policy for granting S3 access to backup user" policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::mpp-bakcup-bucket", "arn:aws:s3:::mpp-bakcup-bucket/*" ] } ] } EOF }
resource "aws_iam_user" "backup_user" { name = "backup-user" } resource "aws_iam_access_key" "backup_user_access_key" { user = aws_iam_user.backup_user.name } resource "aws_iam_user_policy_attachment" "backup_user_policy_attachment" { user = aws_iam_user.backup_user.name policy_arn = aws_iam_policy.s3_access_policy.arn }
问题原因及修复方案
1. 私有子网未关联到配置了S3端点的路由表
你创建了私有子网的路由表,但没有将该路由表与私有子网绑定。这意味着私有子网的流量仍使用默认路由表,而默认路由表没有指向S3网关端点的规则,导致S3流量无法通过端点转发。
修复代码:
添加路由表与私有子网的关联:
resource "aws_route_table_association" "private_app_subnet_az1_rt_assoc" { subnet_id = aws_subnet.private_app_subnet_az1.id # 替换为你的私有子网资源ID route_table_id = aws_route_table.private_app_route_table_az1.id }
2. 执行aws ec2命令需要EC2接口端点
S3网关端点仅支持S3服务的流量转发,而aws ec2 describe-vpc-endpoints属于EC2服务API调用,需要创建EC2接口端点才能在私有子网内访问,否则会因无法连接公网EC2端点而超时。
修复代码:
创建EC2接口端点并关联私有子网路由表:
# 创建端点安全组:允许私有子网实例的HTTPS访问 resource "aws_security_group" "vpc_endpoint_sg" { name = "vpc-endpoint-sg" description = "Allow HTTPS access to VPC endpoints from private subnet" vpc_id = aws_vpc.vpc.id ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.vpc.cidr_block] # 允许VPC内实例访问 } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # 创建EC2接口端点 resource "aws_vpc_endpoint" "ec2_endpoint" { vpc_id = aws_vpc.vpc.id service_name = "com.amazonaws.${var.region}.ec2" vpc_endpoint_type = "Interface" security_group_ids = [aws_security_group.vpc_endpoint_sg.id] subnet_ids = [aws_subnet.private_app_subnet_az1.id] # 你的私有子网ID private_dns_enabled = true # 启用私有DNS,让AWS CLI自动解析到VPC内端点 } # 关联EC2端点到私有路由表 resource "aws_vpc_endpoint_route_table_association" "ec2_endpoint_rt_assoc" { route_table_id = aws_route_table.private_app_route_table_az1.id vpc_endpoint_id = aws_vpc_endpoint.ec2_endpoint.id }
3. 验证S3网关端点的路由是否自动创建
创建S3网关端点后,AWS会自动在关联的路由表中添加一条目标为S3前缀列表、下一跳为S3端点的路由。如果控制台中未看到该路由,可手动添加:
resource "aws_route" "s3_route" { route_table_id = aws_route_table.private_app_route_table_az1.id destination_prefix_list_id = aws_vpc_endpoint.s3_endpoint_az1.prefix_list_id gateway_id = aws_vpc_endpoint.s3_endpoint_az1.id }
4. (可选)用IAM实例角色替代硬编码Access Key
硬编码Access Key到用户数据存在安全风险,建议给EC2实例附加IAM角色,让实例自动获取权限:
# 创建实例角色 resource "aws_iam_role" "ec2_backup_role" { name = "ec2-backup-role" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF } # 关联S3访问策略到角色 resource "aws_iam_role_policy_attachment" "ec2_backup_role_s3_policy" { role = aws_iam_role.ec2_backup_role.name policy_arn = aws_iam_policy.s3_access_policy.arn } # 创建实例配置文件 resource "aws_iam_instance_profile" "ec2_backup_instance_profile" { name = "ec2-backup-instance-profile" role = aws_iam_role.ec2_backup_role.name } # 在EC2实例中添加配置文件 resource "aws_instance" "private_app_instance" { # ... 其他配置 iam_instance_profile = aws_iam_instance_profile.ec2_backup_instance_profile.name }
修改用户数据脚本,移除AWS配置步骤:
#!/bin/bash apt-get update mkdir /backup-data chown -R backup-user:backup-user /backup-data echo "hello world!" > /backup-data/hello.txt sudo -u backup-user crontab -l | { cat; echo '0 1 * * * aws s3 cp /backup-data/hello.txt s3://mpp-bakcup-bucket/ec2/'; } | sudo -u backup-user crontab - service cron restart
内容的提问来源于stack exchange,提问作者msmilkov
相关产品推荐
相关产品推荐

