You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 07:19:58