使用 CloudWatch Events - AWS Certificate Manager
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 CloudWatch Events

您可以使用 Amazon CloudWatch Events 自动执行您的 AWS 服务并自动响应系统事件,例如应用程序可用性问题或资源更改。AWS 服务中的事件将近乎实时传输到 CloudWatch Events。您可以编写简单规则来指示您关注的事件,并指示要在事件匹配规则时执行的自动化操作。有关更多信息,请参阅创建对事件触发的 CloudWatch Events 规则

CloudWatch Events使用 将 转换为操作。Amazon EventBridge借助 EventBridge,您可以使用事件触发目标,包括 AWS Lambda 函数、AWS Batch 作业、Amazon SNS 主题以及许多其他目标。有关更多信息,请参阅For more information, see 什么是 Amazon EventBridge?

AWS Certificate Manager 已导入证书的 通知

ACM 将自动续订生成的证书,但导入的证书需要在到期ACM之前重新颁发并重新导入 ,以避免中断。以下部分说明如何使用 AWS Lambda 侦听 CloudWatch Events、创建通知并将结果发布到 AWS Security Hub,从而向管理员和安全团队提供更大的可见性。

设置 Lambda 函数

以下示例要求您首先创建和部署 AWS Lambda 函数并配置 AWS Identity and Access Management (IAM) 角色和权限。此安全最佳实践可让您更灵活地指定有权调用 函数的人员,并限制授予该人员的权限。不建议直接在用户账户下运行大多数操作,尤其是在管理员账户下不运行AWS大多数操作。

设置 Lambda 函数

  1. 作为最佳安全实践,首先

    通过以下网址打开 IAM 控制台:https://console.amazonaws.cn/iam/

  2. 使用 JSON 策略编辑器创建以下模板中定义的策略。提供您自己的区域和AWS账户详细信息。有关更多信息,请参阅 JSON 选项卡上的创建策略。

    { "Version":"2012-10-17", "Statement":[ { "Sid":"LambdaCertificateExpiryPolicy1", "Effect":"Allow", "Action":"logs:CreateLogGroup", "Resource":"arn:aws:logs:<region>:<AWS-ACCT-NUMBER>:*" }, { "Sid":"LambdaCertificateExpiryPolicy2", "Effect":"Allow", "Action":[ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource":[ "arn:aws:logs:<region>:<AWS-ACCT-NUMBER>:log-group:/aws/lambda/handle-expiring-certificates:*" ] }, { "Sid":"LambdaCertificateExpiryPolicy3", "Effect":"Allow", "Action":[ "acm:DescribeCertificate", "acm:GetCertificate", "acm:ListCertificates", "acm:ListTagsForCertificate" ], "Resource":"*" }, { "Sid":"LambdaCertificateExpiryPolicy4", "Effect":"Allow", "Action":"SNS:Publish", "Resource":"*" }, { "Sid":"LambdaCertificateExpiryPolicy5", "Effect":"Allow", "Action":[ "SecurityHub:BatchImportFindings", "SecurityHub:BatchUpdateFindings", "SecurityHub:DescribeHub" ], "Resource":"*" }, { "Sid":"LambdaCertificateExpiryPolicy6", "Effect":"Allow", "Action":"cloudwatch:ListMetrics", "Resource":"*" } ] }
  3. 创建一个 IAM 角色并将新策略附加到该角色。有关创建 IAM 角色并附加策略的信息,请参阅为 AWS 服务创建角色(控制台)。

  4. 通过以下网址打开 AWS Lambda 控制台:https://console.amazonaws.cn/lambda/

  5. 创建 Lambda 函数。有关更多信息,请参阅使用 控制台https://docs.amazonaws.cn/lambda/latest/dg/getting-started-create-function.html创建 Lambda 函数。完成以下步骤:

    1. Create function (创建函数) 页面上,选择 Author from scratch (从头开始创作) 选项以创建函数。

    2. Function name (函数名称) 字段中指定名称,例如“handle-expiring-certificates”。

    3. Runtime (运行时) 列表中选择 Python 3.8。

    4. 展开 Change default execution role (更改默认执行角色),然后选择 Use an existing role (使用现有角色)。

    5. Existing role (现有角色) 列表中选择您之前创建的角色

    6. 选择 Create function

    7. Function code (函数代码) 下,插入以下代码:

      # Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import json import boto3 import os from datetime import datetime, timedelta, timezone # ------------------------------------------- # setup global data # ------------------------------------------- utc = timezone.utc # make today timezone aware today = datetime.now().replace(tzinfo=utc) # set up time window for alert - default to 45 if its missing if os.environ.get('EXPIRY_DAYS') is None: expiry_days = 45 else: expiry_days = int(os.environ['EXPIRY_DAYS']) expiry_window = today + timedelta(days = expiry_days) def lambda_handler(event, context): # if this is coming from the ACM event, its for a single certificate if (event['detail-type'] == "ACM Certificate Approaching Expiration"): response = handle_single_cert(event, context.invoked_function_arn) return { 'statusCode': 200, 'body': response } def handle_single_cert(event, context_arn): cert_client = boto3.client('acm') cert_details = cert_client.describe_certificate(CertificateArn=event['resources'][0]) result = 'The following certificate is expiring within ' + str(expiry_days) + ' days: ' + cert_details['Certificate']['DomainName'] # check the expiry window before logging to Security Hub and sending an SNS if cert_details['Certificate']['NotAfter'] < expiry_window: # This call is the text going into the SNS notification result = result + ' (' + cert_details['Certificate']['CertificateArn'] + ') ' # this call is publishing to SH result = result + ' - ' + log_finding_to_sh(event, cert_details, context_arn) # if there's an SNS topic, publish a notification to it if os.environ.get('SNS_TOPIC_ARN') is None: response = result else: sns_client = boto3.client('sns') response = sns_client.publish(TopicArn=os.environ['SNS_TOPIC_ARN'], Message=result, Subject='Certificate Expiration Notification') return result def log_finding_to_sh(event, cert_details, context_arn): # setup for security hub sh_region = get_sh_region(event['region']) sh_hub_arn = "arn:aws:securityhub:{0}:{1}:hub/default".format(sh_region, event['account']) sh_product_arn = "arn:aws:securityhub:{0}:{1}:product/{1}/default".format(sh_region, event['account']) # check if security hub is enabled, and if the hub arn exists sh_client = boto3.client('securityhub', region_name = sh_region) try: sh_enabled = sh_client.describe_hub(HubArn = sh_hub_arn) # the previous command throws an error indicating the hub doesn't exist or lambda doesn't have rights to it so we'll stop attempting to use it except Exception as error: sh_enabled = None print ('Default Security Hub product doesn\'t exist') response = 'Security Hub disabled' # This is used to generate the URL to the cert in the Security Hub Findings to link directly to it cert_id = right(cert_details['Certificate']['CertificateArn'], 36) if sh_enabled: # set up a new findings list new_findings = [] # add expiring certificate to the new findings list new_findings.append({ "SchemaVersion": "2018-10-08", "Id": cert_id, "ProductArn": sh_product_arn, "GeneratorId": context_arn, "AwsAccountId": event['account'], "Types": [ "Software and Configuration Checks/AWS Config Analysis" ], "CreatedAt": event['time'], "UpdatedAt": event['time'], "Severity": { "Original": '89.0', "Label": 'HIGH' }, "Title": 'Certificate expiration', "Description": 'cert expiry', 'Remediation': { 'Recommendation': { 'Text': 'A new certificate for ' + cert_details['Certificate']['DomainName'] + ' should be imported to replace the existing imported certificate before expiration', 'Url': "https://console.aws.amazon.com/acm/home?region=" + event['region'] + "#/?id=" + cert_id } }, 'Resources': [ { 'Id': event['id'], 'Type': 'ACM Certificate', 'Partition': 'aws', 'Region': event['region'] } ], 'Compliance': {'Status': 'WARNING'} }) # push any new findings to security hub if new_findings: try: response = sh_client.batch_import_findings(Findings=new_findings) if response['FailedCount'] > 0: print("Failed to import {} findings".format(response['FailedCount'])) except Exception as error: print("Error: ", error) raise return json.dumps(response) # function to setup the sh region def get_sh_region(event_region): # security hub findings may need to go to a different region so set that here if os.environ.get('SECURITY_HUB_REGION') is None: sh_region_local = event_region else: sh_region_local = os.environ['SECURITY_HUB_REGION'] return sh_region_local # quick function to trim off right side of a string def right(value, count): # To get right part of string, use negative first index in slice. return value[-count:]
    8. Environment variables (环境变量) 下,选择 Edit (编辑) 并(可选)添加以下变量。

      • (可选) EXPIRY_DAYS

        指定在发送证书到期通知之前经过多少天的准备时间。函数默认为 45 天,但您可以指定自定义值。

      • (可选) SNS_TOPIC_ARN

        指定 的 Amazon SNSARN。以 arn:aws:sns 的格式提供完整 ARN:<region>:<account-number>:<topic-name>.

      • (可选) SECURITY_HUB_REGION

        指定不同AWS Security Hub区域中的 。如果未指定,则使用正在运行的 Lambda 函数的区域。如果 函数在多个区域中运行,则可能需要将所有证书消息转到单个区域中Security Hub的 。

    9. Basic settings (基本设置) 下,将 Timeout (超时) 设置为 30 秒。

    10. 在页面顶部,选择 Deploy (部署)。

自动执行到期电子邮件通知

此示例演示如何在通过 引发事件时为每个过期证书提供一封电子邮件CloudWatch Events。默认情况下, 每天为距到期还有 45 天或更短的证书ACM引发一个事件。(可以使用 API 的 PutAccountConfigurationACM 操作自定义此周期。) 这些事件中的每一个都触发以下自动操作的级联:

ACM raises CloudWatch event → Event matches CloudWatch rule → Rule calls Lambda function → Function sends SNS email and logs a Finding in Security Hub

::

完成以下过程中的任务以开始使用此解决方案。

自动执行过期通知

  1. 创建 Lambda 函数并配置权限。(已完成 – 请参阅设置 Lambda 函数)。

  2. 函数创建标准Lambda SNS 主题以用来发送通知。有关更多信息,请参阅创建 Amazon SNS 主题

  3. 为任何相关方订阅新的 SNS 主题。有关更多信息,请参阅订阅 Amazon SNS 主题

  4. 创建 CloudWatch Events 规则以触发 Lambda 函数。有关更多信息,请参阅创建对事件触发的 CloudWatch Events 规则

    在 CloudWatch 控制台的 中https://console.amazonaws.cn/cloudwatch/,导航到 Rules (规则) 页面,然后选择 Create rule (创建规则)。指定服务名称事件类型和Lambda函数。在事件模式预览编辑器中,粘贴以下代码:

    { "source": [ "aws.acm" ], "detail-type": [ "ACM Certificate Approaching Expiration" ] }

    Show sample event(s) Lambda(显示示例事件) 下会显示一个事件,例如 receives (接收):

    { "version": "0", "id": "9c95e8e4-96a4-ef3f-b739-b6aa5b193afb", "detail-type": "ACM Certificate Approaching Expiration", "source": "aws.acm", "account": "123456789012", "time": "2020-09-30T06:51:08Z", "region": "us-east-1", "resources": [ "arn:aws:acm:us-east-1:123456789012:certificate/61f50cd4-45b9-4259-b049-d0a53682fa4b" ], "detail": { "DaysToExpiry": 31, "CommonName": "My Awesome Service" } }

cleanup

当您不再需要示例配置或任何配置时,最佳实践是删除其所有跟踪,以避免安全问题和意外的未来费用:

  • IAM策略和角色

  • Lambda 函数

  • CloudWatch Events 规则

  • CloudWatch Logs 与 Lambda 关联

  • SNS 主题