亚马逊AWS官方博客

云上自动化创建账户实践

背景

关于云计算以及其所提供的优势,已经有许多文章和博客详细阐述。然而,尽管云计算带来了众多好处,但也伴随着一些需要解决的复杂性,其中最为重要的是根据用户场景进行资源分离。

在亚马逊云服务中,通常可以通过解耦的账户结构实现计费、资源限制和权限的隔离,从而限制了潜在的风险影响。为了应对随着工作负载数量增加以及管理和维护环境复杂性上升而带来的挑战,亚马逊云科技专业服务团队提供了名为“Cloud Foundations”的解决方案。该解决方案结合了在云上启动多账户架构的最佳实践。

在与众多企业客户合作的经验中,我们发现企业客户通常会为每个业务部门提供单独的亚马逊云科技账户,以满足企业组织中不同的需求。虽然创建多个账户简化了云上运营,提供了诸如安全性、资源隔离、影响范围缩小和计费简化等诸多优势,但创建、引导和配置基线设置却需要投入大量时间。企业客户迫切需要一种可扩展且高效的方式来管理账户创建和引导过程,以确保新账户按照企业定义的标准基线创建,并设置了必要的治理和防护措施。尤为重要的是,企业客户希望能够实现自动化,以节省时间和资源成本。

在本文中,我们将重点讨论自动化账户创建的逻辑、前端集成以及账户创建后的自动化检查流程。

账户创建逻辑和基线

亚马逊云科技推出的 Control Tower 服务(中国区暂时不支持)中包含 Account Factory 功能,该功能可以实现自动化创建账户;另外亚马逊云科技也提供了基于 Cloudformation 的 Account Vending Machine 开源的自动化创建账户的方案。对上述的两种方案本篇文章中不作赘述。本文中会着重介绍账户创建的逻辑以及企业客户常用的账户基线。

自动化创建账户逻辑

如下图所示,自动化创建账户方案一般涉及三个账户,分别是功能账户(用于部署自动化账户创建方案的主要云上资源)、亚马逊云科技组织管理账户(主要用于创建新账户的动作)、目标账户(新创建的账户)。

我们根据上图标示的四个步骤来为您阐述自动化创建账户的逻辑:

1)终端用户通过用户前端、工单系统或者亚马逊云科技服务目录产品(Service Catalog products)来提交创建新账户的请求。

2)我们可以使用亚马逊云科技无服务器计算(Lambda)或步骤函数(Step Functions)来处理创建账户的逻辑,当收到终端用户的创建账户请求后,后端逻辑通过管理账户的亚马逊云科技组织(Organizations)接口创建新账户;账号创建前,可以按照企业制定的标准(例如账户名称)认证终端用户的输入信息,如果输入有误,可以触发异常并通知对应的团队。

3)当账户创建完成后,通常需要休眠 120 秒以等待账户初始化完成。初始化任务包括亚马逊云科技服务的初始化和预先定义由 Cloudformation StackSet 下发的账户基线;等待初始化完成后,我们会通过由 Stack Set 部署好的目标账户角色来获取该目标账户的权限,再通过亚马逊云科技提供的 SDK 来完成剩余的账户基线。

4)最终,当所有任务完成后,程序会将账户相关信息存入账户 DynamoDB 表格。这个表格可以用来记录自动化账户方案的状态以及和其他方案集成时提供账户相关信息。

企业账户基线

企业常用的账户基线:

基线名称 基线描述 相关链接
更新账户登陆密码策略 设置一个符合企业用户标准的登陆账户密码策略 https://docs.aws.amazon.com/prescriptive-guidance/latest/aws-startup-security-baseline/acct-06.html
创建账户别名 通常表现于账户登陆的 URL 里。 https://docs.aws.amazon.com/IAM/latest/UserGuide/AboutAccountAlias.html
删除默认 VPC 删除区域内默认生成的 VPC,VPC 需要按照企业标准和规划创建。 https://docs.aws.amazon.com/vpc/latest/userguide/delete-vpc.html
创建 IAM 角色 利用Cloudformation Stackset 创建一些预制角色,通常配合解决方案使用,例如自动化账户创建方案就需要部署预制角色。 https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create.html
组织单元配置 根据企业标准或用户输入将新建账户迁移对应的组织单元中。 https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_ous.html
激活账户级别 S3 公网屏蔽 一般企业安全标准中不允许 S3 的公网访问,因此通常我们会在账户级别屏蔽 S3 的公网访问。注意,账户级别的公网控制会优先于 S3 对象对于 S3 公网控制。 https://docs.aws.amazon.com/AmazonS3/latest/userguide/configuring-block-public-access-account.html
账户级别强制 EBS 加密 账户级别强制 EBS 加密,如果使用 CMK 对 EBS 进行强制加密,需要注意的是在中国区 CMK 暂时不支持跨区域部署。 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html
创建并配置 CloudTrail 适用于使用亚马逊云科技 CloudTrail 对云上账户进行风险审核、监管和合规性检查的企业。 https://docs.amazonaws.cn/awscloudtrail/latest/userguide/cloudtrail-user-guide.html
创建并配置 Config 适用于使用亚马逊云科技 Config 规则来评估云上资源的配置的企业。 https://docs.amazonaws.cn/config/latest/developerguide/WhatIsConfig.html
配置账户备用联系人 设置备用联系人,亚马逊云科技根据联系信息通知和联系相关联系人。 https://docs.aws.amazon.com/prescriptive-guidance/latest/aws-startup-security-baseline/acct-01.html
创建账户级别的标签 适用于在亚马逊云科技组织内使用标签策略的企业。 https://docs.amazonaws.cn/organizations/latest/userguide/orgs_tagging.html?icmpid=docs_orgs_console
初始化 CDK 适用于使用亚马逊云科技 CDK 部署资源的企业。 https://docs.aws.amazon.com/cdk/v2/guide/hello_world.html
配置第三方 IDPs 源 适用于使用第三方 IDPs 做认证联入亚马逊云科技的企业客户。 https://docs.amazonaws.cn/IAM/latest/UserGuide/id_roles_providers.html?icmpid=docs_iam_help_panel_list

对于以上账户基线的部署,我们推荐使用 CloudFormation 模版或者在 Lambda 中使用亚马逊云科技提供的 SDK 实现。在这里我们以亚马逊云科技提供的 Boto3 SDK(Python3)为例,在创建新账户时,我们可以利用以下代码删除新账户默认创建的 VPC。

import boto3
import time
from botocore.exceptions import ClientError
def delete_default_vpc(currentregion):
    print("Default VPC deletion in progress in {}".format(currentregion))
    ec2_client = get_client('ec2', currentregion)
    vpc_response = ec2_client.describe_vpcs()
    default_vpcid = None
    for i in range(0,len(vpc_response['Vpcs'])):
        if((vpc_response['Vpcs'][i]['IsDefault']) == True):
            print("Default VPC is", vpc_response['Vpcs'][i]['VpcId'])
            default_vpcid = vpc_response['Vpcs'][i]['VpcId']
    if not default_vpcid:
        print("Default VPC was removed, skipping...")
        return

    subnet_response = ec2_client.describe_subnets()
    subnet_delete_response = []
    default_subnets = []
    for i in range(0,len(subnet_response['Subnets'])):
        if(subnet_response['Subnets'][i]['VpcId'] == default_vpcid):
            default_subnets.append(subnet_response['Subnets'][i]['SubnetId'])
    for i in range(0,len(default_subnets)):
        subnet_delete_response.append(ec2_client.delete_subnet(SubnetId=default_subnets[i],DryRun=False))
    print("Default Subnets" + currentregion + "Deleted.")
    igw_response = ec2_client.describe_internet_gateways()
    for i in range(0,len(igw_response['InternetGateways'])):
        for j in range(0,len(igw_response['InternetGateways'][i]['Attachments'])):
            if(igw_response['InternetGateways'][i]['Attachments'][j]['VpcId'] == default_vpcid):
                default_igw = igw_response['InternetGateways'][i]['InternetGatewayId']
    print(default_igw)
    detach_default_igw_response = ec2_client.detach_internet_gateway(InternetGatewayId=default_igw,VpcId=default_vpcid,DryRun=False)
    delete_internet_gateway_response = ec2_client.delete_internet_gateway(InternetGatewayId=default_igw)
    print("Default IGW " + currentregion + "Deleted.")
    time.sleep(10)
    delete_vpc_response = ec2_client.delete_vpc(VpcId=default_vpcid,DryRun=False)
    print("Deleted Default VPC in {}".format(currentregion))
    return delete_vpc_response
def get_client(service, region):
  client = boto3.client(service, region_name = region)
  return client
def lambda_handler(event, context):
    ec2_client = boto3.client('ec2')
    # delete default vpc in every region
    regions = []
    regions_response = ec2_client.describe_regions()

    for i in range(0,len(regions_response['Regions'])):
        regions.append(regions_response['Regions'][i]['RegionName'])
    for r in regions:
        try:
            delete_vpc_response = delete_default_vpc(r)
        except ClientError as e:
            print("An error occured while deleting Default VPC in {}. Error: {}".format(r,e))

在本章节中,我们讨论了,自动化创建新账户的基本流程和常用账户基线。在下面的章节中,我们会和您一起讨论如何将自动化创建账户方案和企业的用户前端以及工单系统集成。

自动化创建账户前端集成

通常我们会向企业客户推荐直接使用亚马逊云科技服务目录产品作为自动化创建账户的用户界面,这样做的好处是,该服务可以和亚马逊云科技的其他服务做好很好的集成,例如,亚马逊云科技身份和访问管理(IAM)和 Lambda 等。

然而,在和企业客户合作过程中,我们发现许多企业客户通常希望将自动化创建账户解决方案和自己的用户前端页面或者工单系统(例如 ServiceNow)做集成。

这一章节,我们会着重讨论如何将自动化创建账户解决方案和用户前端页面以及用户工单系统做集成。

用户前端页面集成

我们推荐使用亚马逊云科技推出的无服务应用架构Chalice将自动化创建账户解决方案和用户前端页面做集成。在这个集成方案中,亚马逊云科技 API 网关提供接口(例如账户创建,账户更新和账户删除),用户前端只需将这些接口集成到用户页面中即可。

无服务应用架构 Chalice

亚马逊云科技 Chalice 是一个由亚马逊云科技推出的开源 Python 框架,专门用于简化构建、部署和管理在 Amazon Lambda 和 Amazon API Gateway 上运行的无服务器应用程序。它旨在使开发人员能够更轻松地构建具有自动扩展能力和低运营成本的应用程序,而无需关注基础架构的细节。

Chalice 的安装部署比较简单,可以参考代码库中的示例;但需要注意的是,由于在亚马逊云科技中国区不支持 API Gateway 的 Edge 模式,我们需要在 Chalice 的配置中(.chalice/config.json)的 api_gateway_endpoint_type 设置为 REGIONAL:

{
  "version": "2.0",
  "app_name": "quickstart",
  "api_gateway_endpoint_type": "REGIONAL",
  "stages": {
    "dev": {
      "api_gateway_stage": "api"
    }
  }
}

另外,我们还可以使用亚马逊云科技 CDK(Cloud Development Kit)来部署 Chalice, 详细部署方法可以参考使用 CDK 部署 Chalice 应用的博客。

讲完了 Chalice 的部署,我们再讨论一下 API 的认证如何在 Chalice 中实现。Chalice 支持自定义的认证,以下为代码示例:

from chalice import Chalice, AuthResponse
app = Chalice(app_name='demoauth1')
@app.authorizer()
def demo_auth(auth_request):
    token = auth_request.token
    # This is just for demo purposes as shown in the API Gateway docs.
    # Normally you'd call an oauth provider, validate the
    # jwt token, etc.
    # In this example, the token is treated as the status for demo
    # purposes.
    if token == 'allow':
        return AuthResponse(routes=['/'], principal_id='user')
    else:
        # By specifying an empty list of routes,
        # we're saying this user is not authorized
        # for any URLs, which will result in an
        # Unauthorized response.
        return AuthResponse(routes=[], principal_id='user')


@app.route('/', authorizer=demo_auth)
def index():
    return {'context': app.current_request.context}

在以上示例中,我们可以在 demo_auth 方法中自定义认证逻辑。比如企业客户使用 KeyClock 作为 IDP,我们就可以使用 jwt 来处理用户请求中的 KeyClock Token, 前端可以使用 PostMan 来做测试;具体处理逻辑如下:

代码示例如下:

@app.authorizer()
def jwt_auth(auth_request):
    token = auth_request.token
    keycloak_endpoint, keycloak_realm = get_keycloak_url_and_realm()
    cert_url = "{0}realms/{1}/protocol/openid-connect/certs".format(keycloak_endpoint,keycloak_realm)
    options = {"verify_signature": True, "verify_aud": False, "exp": True}
    principal_id = 'NoneAuthUser'
    try:
        jwks_client = PyJWKClient(cert_url)
        signing_key = jwks_client.get_signing_key_from_jwt(token)
        user_info = jwt.decode(token, signing_key.key, algorithms=["RS256"],options=options)
        # Add auth logic 
        if 'keycloak-test' in user_info['resource_access']:
                principal_id = user_info ['sub']
                context = {'user_role': user_role,'user_email': user_email}
                return AuthResponse(routes=['*'], principal_id=principal_id,context=context)
    except Exception as e:
        logger.exception("Received an exception")
    return AuthResponse(routes=[], principal_id=principal_id)

通过对 Chalice 的配置可以实现和客户前端的集成和认证,然而有一些企业客户更希望自动化创建账户的方案和其工单系统集成,下面的章节我们会讨论如何将自动化创建账户方案和工单系统集成。

工单系统集成

在一些企业客户的使用场景中,自动化账户创建需要和企业的工单系统(例如 ServiceNow, 缩写为 SNOW)做集成。正常情况下,我们还是推荐使用 Chalice 无服务架构与工单系统集成,即工单系统后端直接调用亚马逊云科技 API 网关提供的接口完成自动化账户创建流程。但是特殊情况下,企业工单系统无法直接调用 API 网关提供的接口。这种情况下,我们也可以使用亚马逊云科技 Lambda 来定时获取工单系统中的工单并触发自动账户创建流程(不推荐)。

如上图所示,我们可以将 API 的 Token 存放在亚马逊云科技凭证管理器(Secrets Manager)里,Lambda 每 10 分钟获取一次工单,并将新的工单信息存入亚马逊云科技 DynamoDB 表中。新的表内信息注入动作触发自动化账户创建流程。

该章节中,我们着重讨论使用亚马逊云科技推出的 Chalice 无服务框架和用户前端集成,另外,讨论了在无法使用工单系统后端调用亚马逊云科技的 API 接口的情况下,我们也可以反向调用工单系统接口以达到和工单系统集成的目的。这里需要注意的是在中国区,API 域名需要备案,否则不管公有或私有 API 都无法正常使用。

账户创建后自动化检查

当账户创建完成后,我们需要有一个方法来检查账户的内容是否符合企业要求,即账户基线是否已经配置成功。检查完成后,可以为相关企业部门提供报告。此类检查实现的方式很多,在本篇文章中,我们会着重向您介绍利用亚马逊云科技 DynamoDB 表和 Lambda 实现账户检查。

DynamoDB 表:

表格名称 表格主键 表格描述
账户参数表 AccountId (Str) 自动化账户创建完成后,账户信息(例如账户 ID,OU 信息等)会存入此表格。
检查报告构建表 id (Str) 构建报告以及调用检查内容定义表中的检查项目。
检查内容定义表 id (Str) 检查内容的期待值,例如按照企业要求 S3 公网访问是否开启。

我们以 S3 公网访问的检查为例,可以将自动化账户检查分为 4 个步骤:

1) 从账户参数表里获取账户信息例如账户别名、账户 Email 以及账户组织里的组织单位(OU)等;利用账户号码以及提前创建好的目标账户的角色获取目标账户的临时权限。

2)在检查报告构建表里查找到 S3 相关需要的检查项目如下,identifier 是在检查内容定义表的定位符,items 是报告中的检查条目,settings 是在报告中的检查大项。

{
    "id":"s3",
    "checkitems":[
    {
    "identifier":"BlockPublicAcls",
    "items":"Block Public Acls - Enabled",
    "settings":"S3 Block Public Access"
    },
    {
    "identifier":"BlockPublicPolicy",
    "items":"Block Public Policy - Enabled",
    "settings":"S3 Block Public Access"
    },
    {
    "identifier":"IgnorePublicAcls",
    "items":"Ignore Public Acls - Enabled",
    "settings":"S3 Block Public Access"
    },
    {
    "identifier":"RestrictPublicBuckets",
    "items":"Restrict Public Buckets - Enabled",
    "settings":"S3 Block Public Access"
    }
    ]
}

3)根据检查报告构建表里的 identifier 定位符,在检查内容定义表里定位到检查项的期待值,例如 BlockPublicAcls 在检查内容定义表里的期待值为 true,那么 Lambda 就会在目标账户里通过 SDK 来检查配置的实际情况,如果也是 true,就会认为该检查项通过。

{
 "id": "s3",
 "source": {
  "publicAccessBlockConfig": {
   "BlockPublicAcls": true,
   "BlockPublicPolicy": true,
   "IgnorePublicAcls": true,
   "RestrictPublicBuckets": true
  }
 }
}

4)根据检查的情况生成一份 PDF 报告。

该账户检查方案不仅可以在账户创建完成后为目标账户提供检查以证明该账户符合企业云上账户标准,而且可以在账户使用过程中,定时对账户进行检查以确保账户一直符合企业的云上账户标准。

总结

本篇文章我们重点讨论了如何在亚马逊云上实现自动化创建账户的最佳实践。通过自动化的方式,企业可以更高效地管理多个亚马逊云账户,实现资源隔离、权限管理和成本控制等目标。我们介绍了账户创建的逻辑和基线配置,以及如何将自动化创建账户方案与用户前端页面和工单系统集成,最后,我们还讨论了账户创建后的自动化检查方法。

希望本文能对正在考虑实施自动化账户创建的企业或个人提供有益的指导和启发。在实际应用中,根据具体需求和情况,可能需要进行更多的定制和调整。通过合理的规划和设计,自动化账户创建可以为企业带来更高效的云资源管理和操作体验。

参考链接

本篇作者

谢丹

亚马逊云科技专业服务团队云架构顾问,拥有多年丰富的云架构和客户服务经验,专注于为企业提供全方位的云上基础设施架构设计、迁移方案设计、最佳实践指导以及落地实施服务。

叶文军

专业服务团队云架构咨询顾问。负责企业客户的云架构设计、迁移、安全和优化,云上自动化运维,容器等相关咨询服务。在云原生技术、DevOps 及私有云部署运维等方面有着丰富的经验。