在 AWS 上部署容器化应用时,使用 ECS Fargate 是一个很好的选择。Fargate 是一种无需管理底层基础设施的容器化计算引擎,可以轻松运行容器,而无需预置或管理 EC2 实例。

然而,默认情况下,Fargate 任务使用公共子网,这意味着容器可以直接访问互联网。虽然这对某些应用程序来说是必需的,但从安全角度来看,最好将容器与互联网隔离开来,只允许出站流量。

在本文中,我们将介绍如何使用 Python 脚本批量禁止 ECS Fargate 服务的公网访问,并使用 NAT 网关解决镜像拉取问题。

先决条件

  • AWS 账户和适当的 IAM 权限
  • Python 3.x 及其 boto3 库
  • 已创建的 ECS 集群和服务

脚本概述

我们将编写一个 Python 脚本,该脚本可以:

  1. 列出指定 ECS 集群中的所有服务
  2. 检查每个服务的网络配置
  3. 如果服务使用公共子网,则更新其配置以使用私有子网和 NAT 网关
  4. 在更新配置后,等待一段时间再处理下一个服务,以避免频繁更新导致的问题

脚本代码

import boto3
import time

ecs_client = boto3.client('ecs', region_name='us-east-1')
ec2_client = boto3.client('ec2', region_name='us-east-1')

# 定义睡眠时间(以秒为单位)
sleep_time = 60

# 定义 VPC 对应的私有子网和安全组配置
vpc_config = {
    'vpc-97f8a9f1': {
        'subnets': ['subnet-03271df83933bc34c', 'subnet-0ba1ee5da9fa8a5fe'],
        'security_groups': ['sg-023218fbc132324f0']
    },
    'vpc-01bc326c162920649': {
        'subnets': ['subnet-0406036d337ad20aa', 'subnet-01f8076d8bb161937', 'subnet-0b7927873dfe98061'],
        'security_groups': ['sg-011d6a642cf4adddb']
    }
}

def get_cluster_services(cluster):
    services = []
    next_token = None

    while True:
        if next_token:
            response = ecs_client.list_services(cluster=cluster, nextToken=next_token)
        else:
            response = ecs_client.list_services(cluster=cluster)

        services.extend(response['serviceArns'])

        if 'nextToken' in response:
            next_token = response['nextToken']
        else:
            break

    return services

def get_vpc_id_from_subnet(subnet_id):
    response = ec2_client.describe_subnets(SubnetIds=[subnet_id])
    vpc_id = response['Subnets'][0]['VpcId']
    return vpc_id

def update_service_subnet(cluster, service):
    # 获取服务信息
    response = ecs_client.describe_services(
        cluster=cluster,
        services=[service]
    )
    service_info = response['services'][0]

    # 获取服务所在的子网
    subnet_ids = service_info.get('networkConfiguration', {}).get('awsvpcConfiguration', {}).get('subnets', [])

    # 如果服务关联了子网
    if subnet_ids:
        # 获取 VPC ID
        vpc_id = get_vpc_id_from_subnet(subnet_ids[0])

        # 如果服务所在的 VPC ID 在配置中存在
        if vpc_id in vpc_config:
            vpc_config_data = vpc_config[vpc_id]
            subnets = vpc_config_data['subnets']
            security_groups = vpc_config_data['security_groups']

            # 更新服务的子网和安全组配置
            response = ecs_client.update_service(
                cluster=cluster,
                service=service,
                networkConfiguration={
                    'awsvpcConfiguration': {
                        'subnets': subnets,
                        'securityGroups': security_groups,
                        'assignPublicIp': 'DISABLED'
                    }
                }
            )
            print(f"Updated service {service} subnet and security groups for cluster {cluster}")
            time.sleep(sleep_time)
        else:
            print(f"VPC ID {vpc_id} not found in the configuration")
    else:
        print(f"Service {service} is not associated with any subnet")

# 示例用法
clusters = ['cluster1','cluster2','cluster3']

for cluster in clusters:
    services = get_cluster_services(cluster)
    for service in services:
        update_service_subnet(cluster, service)

脚本解释

  1. 首先,我们导入必要的 AWS SDK 库并初始化 ECS 和 EC2 客户端。
  2. 定义一个睡眠时间变量,用于在更新每个服务后等待一段时间,避免频繁更新导致的问题。
  3. 定义一个 vpc_config 字典,用于存储每个 VPC 对应的私有子网和安全组配置。你需要根据实际情况填写正确的子网 ID 和安全组 ID。
  4. get_cluster_services 函数用于列出指定 ECS 集群中的所有服务 ARN。
  5. get_vpc_id_from_subnet 函数用于从子网 ID 获取 VPC ID。
  6. update_service_subnet 函数是主要逻辑所在。它会获取服务的网络配置,如果服务使用公共子网,则根据 vpc_config 中的配置更新服务的子网和安全组,并禁用公网 IP。更新后,它会等待指定的睡眠时间。
  7. 最后,我们定义了一个示例集群列表,并遍历每个集群和服务,调用 update_service_subnet 函数进行更新。

使用说明

  1. 将上述脚本保存为 Python 文件,例如 update_ecs_service_subnets.py
  2. 根据实际情况,在 vpc_config 字典中填写正确的私有子网 ID 和安全组 ID。
  3. 根据需要,修改 clusters 列表中的集群名称。
  4. 确保您的 AWS 凭证已正确配置,或者使用适当的角色或实例配置文件。
  5. 运行脚本:
python update_ecs_service_subnets.py
  1. 脚本将逐个处理每个集群中的服务,如果服务使用公共子网,则更新其配置以使用私有子网和 NAT 网关。
  2. 更新完成后,您的 ECS Fargate 服务将无法直接访问互联网,但可以通过 NAT 网关拉取 Docker 镜像和访问其他出站资源。

通过执行此脚本,您可以批量禁止 ECS Fargate 服务的公网访问,提高应用程序的安全性,同时使用 NAT 网关解决镜像拉取问题。请注意,根据您的服务数量和集群规模,此过程可能需要一些时间。