亚马逊AWS官方博客

使用 AWS Systems Manager 和 AWS Lambda 实现跨账户的共享服务调用

本文关键词:AWS Systems Manager, AWS Lambda, bootstrapping, IAM role, cross-account, multiple accounts, shared services

多账号架构下的共享服务

在大型企业的云环境中,为了满足 IT 系统的合规性要求和安全性要求,通常需要采用多账户的架构以实现不同业务部门之间的资源隔离和权限控制。下图是一个常见的企业多账户架构,通常包含两类账户群:一类是中央共享服务账户群(桔色背景框),包含 AD 服务、日志服务、VPN 服务、跳板机等共享服务;另一类是业务应用账户群(蓝色背景框),通常情况下生产和非生产的资源会分别放置到不同的账户中。

图一  企业多账户架构示例

业务应用和共享服务的通信,通常可以通过以下两种方式实现:

(1)通过 VPC Peering 的方式打通共享服务所在的 VPC 和业务应用所在的 VPC 的网络连接,同时配合安全组进行网络流量的控制。目前 AWS 的中国区域可以支持这个方式,但需要注意 VPC Peering 会有连接数量的限制

(2)业务应用可以通过 Interface VPC Endpoint(基于 AWS PrivateLink 技术)访问到共享服务的 VPC,同时配合安全组进行网络流量的控制。目前(2018年5月)AWS 中国区域尚不支持 Interface VPC Endpoints。

除了以上两种基于私有网络打通的方案之外,本文描述了某 AWS 客户在项目实施中设计的第三种跨账户共享服务访问方式:基于 API 的跨账户共享服务访问方式。

基于 API 的跨账户共享服务访问方式概述

在项目实施中,为了避免共享服务和业务应用的直接网络连通,实施团队设计了使用业务应用账户下的 Lambda 程序调用共享服务账户下 AWS Systems Manager 服务 API 的方式来完成共享服务的调用。

本文将以 EC2 服务器的 DNS 自动注册为例,展示跨账户共享服务调用的实现机理。如图二所示,当 Auto Scaling 组发生水平扩展时,新增的 EC2 实例经常需要执行一些定制的初始化工作,DNS 域名的注册就是其中的一个环节。Auto Scaling 的水平扩展由 CloudWatch Event 的规则捕捉后,会触发 Lambda 程序通过跨账户的 API 调用完成 DNS 的域名注册。

图二  方案架构

下面将为您介绍方案的实现细节。

共享服务的 API 化

本方案通过 AWS Systems Manager 实现了共享服务的 API 化,从而把基于私有网络的服务调用转变为基于公有网络的 AWS API 调用。

例如,为了实现在 AD 中执行 DNS 域名的注册,可以采用 AWS Manager 预设的 AWS-RunPowerShellScript Document,通过在 AD 所在的 Windows 操作系统中执行 PowerShell 的脚本的方式完成 DNS 域名的注册。

图三  Documents in AWS Systems Manager

以下 Python 代码片段通过调用 AWS Manager 的 SendCommand API,实现了 DNS 域名注册共享服务的调用:

ssmCommand = client.send_command(
		InstanceIds = [
			'<instance-id>'
		],
		DocumentName = 'AWS-RunPowerShellScript',
		Parameters = {
		'commands': [
			'Add-DnsServerResourceRecordA -Name "<host-name>" -ZoneName "<zone-name>" -AllowUpdateAny -IPv4Address "<ip-address>" -TimeToLive 01:00:00'
		]
		},
	)

 

具体 Python SDK 函数的参数可以参见文档

共享服务调用的触发

共享服务调用通常由 AWS 资源的状态变更触发。本项目的需求为,当 Auto Scaling 组根据 Scaling Policy 启动 EC2 实例时,需要完成一些运维的自动化操作,而这些操作可以通过 Lambda 程序调用相应的 AWS API 来完成。例如:

  • 通过 AWS Manager 实现的共享服务 API:参见上文描述。
  • 通过 EC2 API 实现 AWS 平台层面的操作:例如,修改 AWS EC2 实例的属性 。

这些自动化操作由 CloudWatch Event 的规则(rule)来触发。Rule Pattern 如下文所示:

{
  "source": [
    "aws.autoscaling"
  ],
  "detail-type": [
    "EC2 Instance Launch Successful"
  ],
  "detail": {
    "AutoScalingGroupName": [
      "my-asg-name"
    ]
  }
}

这个 CloudWatch Event 规则的设立可以通过 AWS 控制台的向导来完成。

图四  CloudWatch Event Rule 的设立

为实现配置过程的自动化,上述 CloudWatch Event 规则的设立也可以通过以下 CloudFormation 模板完成。

(1)设置触发条件和触发对象

EventRule: 
  Type: "AWS::Events::Rule"
  Properties: 
    Description: "EventRule"
    EventPattern: 
      source: 
        - "aws.autoscaling"
      detail-type: 
        - "EC2 Instance Launch Successful"
      detail: 
        AutoScalingGroupName: 
          - "<asg-name>"
    State: "ENABLED"
    Targets: 
      - 
        Arn: <lambda-arn>
        Id: "TargetFunction01"

其中:

  • <asg-name>是 Auto Scaling Group 的名字。
  • <lambda-arn>是需要触发的 Lambda 程序的 ARN。

(2)设置调用权限,即赋予 CloudWatch Event 调用 Lambda 程序的权限

PermissionForEventsToInvokeLambda: 
  Type: "AWS::Lambda::Permission"
  Properties: 
    FunctionName: <lambda-arn>
      Ref: "LambdaFunction"
    Action: "lambda:InvokeFunction"
    Principal: "events.amazonaws.com"
    SourceArn: <event-rule-arn>

其中,

  • <lambda-arn>是 Lambda 程序的 ARN。
  • <event-rule-arn>是 CloudWatch Event 规则的 ARN

共享服务调用的权限配置

所有 AWS 的操作都需要授权才可以完成。跨账号的 API 调用的授权需要以下几个步骤。

(1)在共享服务账户下,创建角色(role),命名为 ssm-role-for-lambda。这个角色将被 Lambda 程序继承,用来获取执行 AWS Systems Manager 所需的权限。这个角色绑定的权限可采用以下 AWS Managed Policy。

arn:aws-cn:iam::aws:policy/AmazonSSMFullAccess

(2)在共享服务账户下,为第一步创建的 ssm-role-for-lambda 角色添加 Trusted Entity,从而允许业务账户中的 Lambda 程序继承这个角色。

{
  "Version": "2012-10-17",
  "Statement": [
   {
     "Effect": "Allow",
     "Principal": {
       "AWS": "arn:aws-cn:iam::<app-account-id>:root"
     },
     "Action": "sts:AssumeRole"
   }
  ]
}

其中,<app-account-id>是业务应用账户的 ID。

(3)在业务应用账户下,设置 Lambda 的执行权限,需包括以下权限,从而允许 Lambda 程序来继承 ssm-role-for-lambda角色:

{
   "Sid": "VisualEditor0",
   "Effect": "Allow",
   "Action": "sts:AssumeRole",
   "Resource": "arn:aws-cn:iam::<shared-service-account-id>:role/ssm-role-for-lambda"
}

其中,<shared-service-account-id>是共享服务账户的 ID。

(4)在业务应用账户下,Lambda 程序中通过 STS 服务的 AssumeRole API 继承这个角色。此 API 调用可以通过以下 Python 代码片段实现:

lambda_sts_client = boto3.client('sts',
		region_name="cn-north-1"
		)
response = lambda_sts_client.assume_role(
		RoleArn="arn:aws-cn:iam::<share-service-account-id>:role/ssm-role-for-lambda",
		RoleSessionName="remotessm"
		)

其中,<shared-service-account-id>是共享服务账户的 ID。

以上代码片段中的 response 变量将返回已继承 ssm-role-for-lambda 角色一套动态授权信息,具体包括:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_SESSION_TOKEN

(5)在业务应用账户下,基于第四步获取的动态授权,Lambda 程序即可调用 AWS Systems Manager 的 API 完成共享服务的调用。例如,以下 Python 代码片段实现了调用 AWS Systems Manager 的 API 的 ssm_client 句柄的获取。

ssm_session = boto3.Session(
		aws_access_key_id=response['Credentials']['AccessKeyId'],
aws_secret_access_key=response['Credentials']['SecretAccessKey'],
		aws_session_token=response['Credentials']['SessionToken'],
		region_name="cn-north-1"
		)
ssm_client = ssm_session.client('ssm')

基于 API 的跨账户共享服务访问方式的优势

本文介绍了基于 AWS Systems Manager 和 Lambda 的跨账户共享服务访问方式。相比前文中提到的另外两种方式,本方案有以下优势:

  • 无需配置共享服务 VPC 和业务应用 VPC 的私有网络连接。对于没有 Private Link 功能的中国区域,避免了触及 VPC Peering 数量上限的风险。
  • 把需要客户自行实现的私有网络接口暴露、操作系统内程序执行权限管控等高风险功能,转化为 AWS 平台基于 IAM 服务的 API 访问控制,从而提高了系统的安全性。
  • 实现了共享服务的 API 化,为下一步的共享服务的标准化和自动化奠定基础。

 

寇欣

亚马逊 AWS 中国区专业服务部咨询顾问。加入 AWS 之前,在 IT 和云计算行业积累了丰富的云服务架构和应用软件架构的经验。历任 Oracle 电信事业部专业服务部门资深顾问、中国移动研究院云计算研究员、Lucent Tech 研发中心系统架构师。