기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
이 절차는 AWS Lambda 를 사용하여 Amazon 에서 수신 대기하고 EventBridge, Amazon Simple Notification Service(SNS)를 사용하여 알림을 생성하고 AWS Security Hub, 결과를 에 게시하여 관리자와 보안 팀에 가시성을 제공하는 방법을 보여줍니다.
Lambda 함수 및 IAM 역할을 설정하려면
-
먼저 AWS Identity and Access Management (IAM) 역할을 구성하고 Lambda 함수에 필요한 권한을 정의합니다. 이 보안 모범 사례를 통해 유연하게 함수를 호출할 권한이 있는 사용자를 지정하고 해당 사용자에게 부여된 권한을 제한할 수 있습니다. 대부분의 AWS 작업은 사용자 계정에서 직접 실행하는 것이 권장되지 않으며, 특히 관리자 계정에서는 실행하지 않는 것이 좋습니다.
에서 IAM 콘솔을 엽니다https://console.aws.amazon.com/iam/
. -
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":"*" } ] } -
IAM 역할을 생성하고 새 정책을 연결합니다. IAM 역할 생성 및 정책 연결에 대한 자세한 내용은 AWS 서비스에 대한 역할 생성(콘솔)을 참조하세요.
에서 AWS Lambda 콘솔을 엽니다https://console.aws.amazon.com/lambda/
. -
Lambda 함수를 생성합니다. 자세한 내용은 콘솔로 Lambda 함수 생성을 참조하세요. 다음 단계를 완료합니다.
-
[함수 생성(Create function)] 페이지에서 [새로 작성(Author from scratch)] 옵션을 선택하여 함수를 생성합니다.
-
함수 이름 필드에 “handle-expiring-certificates”와 같은 이름을 지정합니다.
-
[런타임(Runtime)] 목록에서 Python 3.8을 선택합니다.
-
[기본 실행 역할 변경(Change default execution role)]을 선택하고 [기존 역할 사용(Use an existing role)]을 선택합니다.
-
[기존 역할(Existing role)] 목록에서 앞서 생성한 역할을 선택합니다.
-
함수 생성(Create function)을 선택합니다.
-
[함수 코드(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:]
-
[환경 변수(Environment variables)]에서 [편집(Edit)]을 선택하고 선택적으로 다음 변수를 추가합니다.
-
(선택 사항) EXPIRY_DAYS
인증서 만료 알림을 보내기 전의 리드 타임(일)을 지정합니다. 이 함수의 기본값은 45일이지만 사용자 지정 값을 지정할 수 있습니다.
-
(선택 사항) SNS_TOPIC_ARN
Amazon 에 ARN 대한 를 지정합니다SNS. arn:aws:sns 형식으로 전체 ARN를 제공합니다.
<region>
:<account-number>
:<topic-name>
. -
(선택 사항) SECURITY_HUB_REGION
다른 리전 AWS Security Hub 에서 을 지정합니다. 이 값을 지정하지 않으면 실행 중인 Lambda 함수의 리전이 사용됩니다. 함수가 여러 리전에서 실행되는 경우 모든 인증서 메시지가 단일 리전의 Security Hub로 전송되도록 하는 것이 좋습니다.
-
-
[기본 설정(Basic settings)]에서 [제한 시간(Timeout)]을 30초로 설정합니다.
-
페이지 상단에서 [배포(Deploy)]를 선택합니다.
-
다음 절차의 작업을 완료하여 이 솔루션의 사용을 시작합니다.
만료 이메일 알림을 자동화하려면
이 예제에서는 Amazon 를 통해 이벤트가 제기되는 시점에 만료되는 각 인증서에 대해 단일 이메일을 제공합니다 EventBridge. 기본적으로 는 만료 후 45일 이하의 인증서에 대해 매일 이벤트를 ACM 생성합니다. (이 기간은 ACM 의 PutAccountConfiguration 작업을 사용하여 사용자 지정할 수 있습니다API.) 이러한 각 이벤트는 다음과 같은 일련의 자동화된 작업을 트리거합니다.
ACM raises Amazon EventBridge event →
>>>>>>> events
Event matches Amazon EventBridge rule →
Rule calls Lambda function →
Function sends SNS email and logs a Finding in Security Hub
-
Lambda 함수를 생성하고 권한을 구성합니다. (이미 완료됨 - Lambda 함수 및 IAM 역할을 설정하려면 참조).
-
알림을 보내는 데 사용할 Lambda 함수의 표준 SNS 주제를 생성합니다. 자세한 내용은 Amazon SNS 주제 생성을 참조하세요.
-
관심 있는 사람이 새 SNS 주제를 구독합니다. 자세한 내용은 Amazon SNS 주제 구독을 참조하세요.
-
Lambda 함수를 트리거하는 Amazon EventBridge 규칙을 생성합니다. 자세한 내용은 이벤트에 대응하는 Amazon EventBridge 규칙 생성을 참조하세요.
의 Amazon EventBridge 콘솔에서 이벤트 > 규칙 페이지로 https://console.aws.amazon.com/events/
이동하여 규칙 생성을 선택합니다. [서비스 이름(Service Name)], [이벤트 유형(Event Type)] 및 [Lambda 함수(Lambda function)]를 지정합니다. [이벤트 패턴 미리 보기(Event Pattern preview)] 편집기에서 다음 코드를 붙여 넣습니다. { "source": [ "aws.acm" ], "detail-type": [ "ACM Certificate Approaching Expiration" ] }
Lambda 수신과 같은 이벤트가 [샘플 이벤트 표시(Show sample event(s))] 아래에 표시됩니다.
{ "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" } }
정리하려면
구성 예 또는 다른 어떤 구성이 더 이상 필요하지 않은 경우, 보안 문제 및 예기치 않은 향후 요금 발생을 방지하기 위해 모든 트레이스를 제거하는 것이 좋습니다.
-
IAM 정책 및 역할
-
Lambda 함수
-
CloudWatch 이벤트 규칙
-
CloudWatch Lambda와 연결된 로그
-
SNS 주제