背景
亚马逊云科技标签 是以键-值对的形式附加到某一个资源中,成为资源的一个属性,因此通过一致性资源标记(标签策略)来为资源和服务添加一致性的属性字段,可实现成本分析、自动化运维、安全运维、(业务)资产管理等使用场景。
成本分析:分析不同部门的成本,以及在不同资源及服务的成本分布,是非常有效使得成本可见的方案
自动化运维:实现自动化运维,典型场景如服务器自动启停,设置启停时间和执行周期,可以有效降低费用成本
安全运维:实现高效的安全运维管理,典型场景如:
业务视角资产管理:实现资源与服务的业务化描述,帮助您统计和分析不同业务所使用的服务和资源。
通常在资源标签使用场景中,大多依托人工去控制标签的合规性;这样对于未添加标签或者标签不正确的资源很难以监测和管控。基于本文所描述的方案,可以从资源标签的创建、检测、自动修复等多个阶段实现标签的合规;在资源创建方几乎无感的情况下即可完成目标标签的添加。
本文主要介绍了一种实践方法:利用设计的标签内容,实现对资源的访问控制
标签控制概述
对于标签的控制策略来说,整体的思路包含以下几个步骤:
以上持续合规思路应用到标签控制中,可以大幅提高资源标签的准确性和易用性。
指导性控制
在本文示例的场景中,指导性控制策略如下:
- 将企业组织下的全部账号内的 EC2 和 EBS 卷都附加上 {“aws-project”:”migration”} 的标签。
- 保证此标签在任何情况下,都可以自动附加和修复。
预防性控制
预防性控制目的是限制对附加了指定标签的资源的操作权限;在亚马逊云科技平台上,可以使用Amazon Organizations服务的 服务控制策略(SCP) 来实现对附加标签资源的预防性控制,为您组织中的所有账户提供对最大可用权限的集中控制。SCP 可帮助确保您的账户符合组织的访问控制准则。
服务控制策略是一种强制控制策略;除此之外,还可以使用标签策略来规范标签的使用。
本文中设计的预防性控制策略包含:
- 服务控制策略:拒绝未使用一致性标签的 EC2实例的创建。
- 标签策略:规范目标标签的键值 { “aws-project”: “migration”}
控制台配置
创建服务控制策略
- 登录到 Amazon Organizations 控制台
- 在 Service control policies (服务控制策略)页面上,选择 Create policy (创建策略)。
- 在Create new service control policy (创建新的服务控制策略)页面上,输入策略的 Policy name (策略名称) 和可选 Policy description (策略说明)。
- 构建策略,按需添加拒绝或允许访问的语句。如果您使用 Deny 语句,请执行以下操作:
- 在右侧 Edit statement (编辑语句) 窗格的 Add actions (添加操作) 下方,选择服务(Amazon EC2)。当您选择右侧的选项时,JSON 编辑器会更新,以在左侧显示相应的 JSON 策略。
- 选择服务后,将打开一个列表,其中包含该服务的可用操作。选择要拒绝的操作
ec2:CreateVolume、ec2:RunInstances
,左侧的 JSON 将更新,以包含您选择的操作。
- 指定要包含在语句中的资源,
arn:aws:ec2:*:*:instance/*", "arn:aws:ec2:*:*:volume/*"
,即所有EC2实例及储存卷
- 添加语句判断条件,以标签判断为例,更多语法参考官方文档
- Condition Key(判断条件字段名),选择aws:RequestTag
- Tag key(标签名称),输入aws-project
- Qualifier(匹配方式),选择默认
- Operater(操作符),即判断方式,选择Null,即判断指定的标签的健值不为空
- 在Value(值)中输入要指定的标签值
- 添加完语句后,选择 Create policy (创建策略) 以保存已完成的 SCP。
服务控制策略示例代码如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2Tags",
"Effect": "Deny",
"Action": [
"ec2:CreateVolume",
"ec2:RunInstances"
],
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"Null": {
"aws:RequestTag/aws-project": "migration"
}
}
}
]
}
创建标签策略
- 登录到 Amazon Organizations 控制台
- 选择策略 -> 标签策略,将以下 JSON 文本复制到对应的策略中
{
"tags": {
"aws-project": {
"tag_key": {
"@@assign": "aws-project"
},
"tag_value": {
"@@assign": [
"migration"
]
},
"enforced_for": {
"@@assign": [
"ec2:instance",
"ec2:volume"
]
}
}
}
}
以上策略的含义是如果在请求创建EC2或者EBS的过程中添加了标签键为 ”aws-project”,则其值必须为 “migration”;否则标签就会被拒绝创建。
注意标签策略目前仅支持亚马逊云科技国际区,中国区需要使用替代方案:亚马逊云科技标签策略中国区替代方案 。
检测性控制
检测性控制目的是自动发现非预期的资源标签;在亚马逊云科技的平台上,可以使用 Amazon Config 这个服务来实现对于资源标签的检测。
控制台配置
(1)进入Amazon Config 控制台,确认 Amazon Config 已启用(启用方法参见: https://docs.aws.amazon.com/zh_cn/config/latest/developerguide/gs-console.html)。
(2)在Rules 页面,选择 Add rule进入 Add rule 页面,默认选择 AWS managed rule。搜索选择 required-tags
;完成以下主要配置:
- Name (名称):输入自定义规则名称。
- Scope of changes (更改范围):选择 Resources。本文这里选择 EC2:Instance 和 EC2:Volume。
- Rule parameters (规则参数):
- 在本条规则下,最多可以一次性检测 6 个资源标签是否符合预期。
- 在本文的案例中,配置 tag1Key:aws-project ; tag1Value:migration。
- Remediation action (修正操作) 我们在后续响应式控制中配置。
- 完成上述配置后点击 Save。Amazon Config 会自动监听资源的标签配置,告警不符合预期的 EC2 实例和 EBS 卷的标签。
CloudFormation 代码示例
通过 CloudFormation 可以快速创建以上Amazon Config 规则,并通过 CloudFormation 堆栈集的特性,可以将CloudFormation 堆栈一键部署到企业组织多账号体系下。
本文的示例代码如下:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS Config Rules'
Resources:
ConfigRuleForTags:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: aws-required-tags-01
Description: 'Checks whether your resources have the tags that you specify.'
InputParameters: |
{"tag1Key": "aws-project",
"tag1Value": "migration"}
Scope:
ComplianceResourceTypes:
- "AWS::EC2::Volume"
- "AWS::EC2::Instance"
Source:
Owner: AWS
SourceIdentifier: REQUIRED_TAGS
CloudFormation 的部署可以参考亚马逊云科技的官方文档。
响应式控制
响应式控制目的是在检测出非预期的资源标签后,自动修复打上预期的标签。本文的响应式控制策略结合 Amazon Config 修正操作特性和自定义 Amazon Systems Manager 自动化文档,实现预期资源标签的自动添加。
控制台配置
配置 System Manager 自动化文档
- 进入AWS Systems Manager控制台,点击左侧导航栏Documents,在Documents页面点击 Create Automation。
- 在 Create Automation页面,主要配置如下:
- Name:输入名称 (config-remediation-for-ec2-tags)
- 以Editor方式进行配置,在Editor处输入以下代码后点击创建。
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
outputs:
- SetRequiredTags.SuccessfulResources
- SetRequiredTags.FailedResources
parameters:
RequiredTags:
type: StringMap
description: (Required) The tags to add to the resources.
displayType: textarea
ResourceIDs:
type: StringList
description: (Required) The IDs of the resources to add the tags to.
minItems: 1
displayType: textarea
AutomationAssumeRole:
type: String
description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
default: ''
allowedPattern: '^arn:aws(-cn|-us-gov)?:iam::\d{12}:role\/[\w+=,.@_\/-]+|^$'
mainSteps:
- name: SetRequiredTags
action: 'aws:executeScript'
onFailure: Abort
isCritical: true
timeoutSeconds: 600
isEnd: true
inputs:
Runtime: python3.7
Handler: set_required_tags_handler
InputPayload:
RequiredTags: '{{RequiredTags}}'
ResourceIDs: '{{ResourceIDs}}'
Script: |
import json
import boto3
import os
def get_account_id():
client = boto3.client("sts")
return client.get_caller_identity()["Account"]
def set_required_tags_handler(event, context):
client = boto3.client('resourcegroupstaggingapi')
successesResources = []
FailedResources = []
ResourceIDs = event["ResourceIDs"]
tags = event["RequiredTags"]
current_account_id = get_account_id()
runtime_region = os.environ['AWS_REGION']
for arn in ResourceIDs:
try:
if(arn.split("-")[0] == "i"):
arn = "arn:aws:ec2:{0}:{1}:instance/{2}".format(runtime_region,current_account_id,arn)
response = client.tag_resources(ResourceARNList = [arn], Tags = tags)
if(arn.split("-")[0] == "vol"):
arn = "arn:aws:ec2:{0}:{1}:volume/{2}".format(runtime_region,current_account_id,arn)
response = client.tag_resources(ResourceARNList = [arn], Tags = tags)
successesResources.append(arn)
except Exception as e:
errorMsg = str(e)
FailedResources.append({'ResourceIDs': arn , "error": errorMsg})
out = {
"SuccessesResources": successesResources,
"FailedResources": FailedResources
}
return out
outputs:
- Name: SuccessfulResources
Selector: $.Payload.SuccessesResources
Type: StringList
- Name: FailedResources
Selector: $.Payload.FailedResources
Type: MapList
配置 System Manager 服务角色
- 进入 IAM 控制台,创建 IAM 角色,本文配置角色名为:AmazonSSMDocumentAutomationRole
- 添加亚马逊云科技托管权限策略:AmazonSSMAutomationRole 和ResourceGroupsandTagEditorFullAccess
配置 Amazon Config
- 进入 Amazon Config 控制台,点击左侧导航栏的Rules进入Rule页面,选择编辑在检测性控制创建的 Config 规则 (默认是 aws-required-tags-01)。
- 在 Choose remediation action部分,完成以下主要配置:
- Remediation action:选择在前一步骤中建立的Amazon Systems Manager 自动化文档名称;本文默认是config-remediation-for-ec2-tags
- Auto remediation:若您希望自动修正,选择Yes;若希望以点击修复按钮的形式进行修正,则选择No(建议在启用自动修正前先进行测试)
- Resource ID parameter:选择 ResourceIDs
- Parameters:
- AutomationAssumeRole:输入在前一步中为Amazon Systems Manager 自动化文档所创建的角色对应的ARN值。
- RequiredTags: 输入自动修正后的 Tags。 本文默认为 {“aws-project”:”migration”}
CloudFormation 代码示例
响应式控制 CloudFormation 代码示例如下,可以配置 CloudFormation 堆栈集将此一键部署到整个企业组织下。
AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS Config Rules'
Resources:
SSMDocumentForTags:
Type: AWS::SSM::Document
Properties:
Content:
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
outputs:
- SetRequiredTags.SuccessfulResources
- SetRequiredTags.FailedResources
parameters:
RequiredTags:
type: StringMap
description: (Required) The tags to add to the resources.
displayType: textarea
ResourceIDs:
type: StringList
description: (Required) The IDs of the resources to add the tags to.
minItems: 1
displayType: textarea
AutomationAssumeRole:
type: String
description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
default: ''
allowedPattern: '^arn:aws(-cn|-us-gov)?:iam::\d{12}:role\/[\w+=,.@_\/-]+|^$'
mainSteps:
- name: SetRequiredTags
action: 'aws:executeScript'
onFailure: Abort
isCritical: true
timeoutSeconds: 600
isEnd: true
inputs:
Runtime: python3.7
Handler: set_required_tags_handler
InputPayload:
RequiredTags: '{{RequiredTags}}'
ResourceIDs: '{{ResourceIDs}}'
Script: |
import json
import boto3
import os
def get_account_id():
client = boto3.client("sts")
return client.get_caller_identity()["Account"]
def get_partition(runtime_region):
client = boto3.Session()
partition = client.get_partition_for_region(runtime_region)
return partition
def set_required_tags_handler(event, context):
client = boto3.client('resourcegroupstaggingapi')
successesResources = []
FailedResources = []
ResourceIDs = event["ResourceIDs"]
tags = event["RequiredTags"]
current_account_id = get_account_id()
runtime_region = os.environ['AWS_REGION']
partition = get_partition(runtime_region)
for arn in ResourceIDs:
try:
if(arn.split("-")[0] == "i"):
arn = "arn:{0}:ec2:{1}:{2}:instance/{3}".format(partition,runtime_region,current_account_id,arn)
response = client.tag_resources(ResourceARNList = [arn], Tags = tags)
if(arn.split("-")[0] == "vol"):
arn = "arn:{0}:ec2:{1}:{2}:volume/{3}".format(partition,runtime_region,current_account_id,arn)
response = client.tag_resources(ResourceARNList = [arn], Tags = tags)
successesResources.append(arn)
except Exception as e:
errorMsg = str(e)
FailedResources.append({'ResourceIDs': arn , "error": errorMsg})
out = {
"SuccessesResources": successesResources,
"FailedResources": FailedResources
}
return out
outputs:
- Name: SuccessfulResources
Selector: $.Payload.SuccessesResources
Type: StringList
- Name: FailedResources
Selector: $.Payload.FailedResources
Type: MapList
DocumentFormat: YAML
DocumentType: Automation
Name: config-remediation-for-ec2-tags
Tags:
- Key: aws-project
Value: landing-zone
SSMAutomationAssumeRole:
Type: AWS::IAM::Role
Properties:
Description: "Role for SSM Automation Actions"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole"
- !Sub "arn:${AWS::Partition}:iam::aws:policy/ResourceGroupsandTagEditorFullAccess"
RoleName: AmazonSSMDocumentAutomationRole
EC2TagsRemediationConfiguration:
Type: "AWS::Config::RemediationConfiguration"
Properties:
Automatic: TRUE
MaximumAutomaticAttempts: 3
RetryAttemptSeconds: 120
ConfigRuleName: !Ref ConfigRuleForTags
Parameters:
AutomationAssumeRole:
StaticValue:
Values:
- !GetAtt SSMAutomationAssumeRole.Arn
ResourceIDs:
ResourceValue:
Value: RESOURCE_ID
RequiredTags:
StaticValue:
Values:
- '{"aws-project":"migration"}'
TargetId: !Ref SSMDocumentForTags
TargetType: "SSM_DOCUMENT"
结合检测性控制的 CloudFormation 模板,可以实现一键部署实现对于资源标签的监测和自动修复。
总结
本文以亚马逊云科技持续合规的思想为基石,将资源标签的配置转换为安全策略配置、将标签检测和修正响应转换为相应自动化检测和自动修正响应;实现了合规即代码(Compliance as Code)的合规控制,一定程度上满足终端用户无感知的资源标签控制。
对于非 EC2 的资源,大部分也可以使用 Tagging API 的TagResources 方法来进行标签附加操作;读者可以参考上述响应式控制代码进行二次开发。但请注意,不是所有的资源都支持标签,相关说明参考亚马逊云科技官方文档。
本篇作者