Lambda 함수를 사용하여 이벤트에 응답 - AWS Certificate Manager

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Lambda 함수를 사용하여 이벤트에 응답

이 절차는 Amazon에서 수신 AWS Lambda 대기하고 EventBridge, Amazon Simple Notification Service (SNS) 로 알림을 생성하고, 결과를 게시하여 AWS Security Hub관리자와 보안 팀에 가시성을 제공하는 방법을 보여줍니다.

Lambda 함수 및 IAM 역할을 설정하려면
  1. 먼저 AWS Identity and Access Management (IAM) 역할을 구성하고 Lambda 함수에 필요한 권한을 정의합니다. 이 보안 모범 사례를 통해 유연하게 함수를 호출할 권한이 있는 사용자를 지정하고 해당 사용자에게 부여된 권한을 제한할 수 있습니다. 대부분의 AWS 작업을 사용자 계정으로 직접 실행하는 것은 권장되지 않으며, 특히 관리자 계정으로는 실행하지 않는 것이 좋습니다.

    https://console.aws.amazon.com/iam/에서 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. https://console.aws.amazon.com/lambda/ 에서 AWS Lambda 콘솔을 엽니다.

  5. Lambda 함수를 생성합니다. 자세한 내용은 콘솔로 Lambda 함수 생성을 참조하세요. 다음 단계를 완료합니다.

    1. [함수 생성(Create function)] 페이지에서 [새로 작성(Author from scratch)] 옵션을 선택하여 함수를 생성합니다.

    2. 함수 이름 필드에 handle-expiring-certificates "“와 같은 이름을 지정합니다.

    3. [런타임(Runtime)] 목록에서 Python 3.8을 선택합니다.

    4. [기본 실행 역할 변경(Change default execution role)]을 선택하고 [기존 역할 사용(Use an existing role)]을 선택합니다.

    5. [기존 역할(Existing role)] 목록에서 앞서 생성한 역할을 선택합니다.

    6. 함수 생성을 선택합니다.

    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 SNS의 ARN을 지정합니다. arn:aws:sns:<region>:<account-number>:<topic-name> 형식으로 전체 ARN을 제공합니다.

      • (선택 사항) SECURITY_HUB_REGION

        다른 AWS Security Hub 지역의 a를 지정합니다. 이 값을 지정하지 않으면 실행 중인 Lambda 함수의 리전이 사용됩니다. 함수가 여러 리전에서 실행되는 경우 모든 인증서 메시지가 단일 리전의 Security Hub로 전송되도록 하는 것이 좋습니다.

    9. [기본 설정(Basic settings)]에서 [제한 시간(Timeout)]을 30초로 설정합니다.

    10. 페이지 상단에서 [배포(Deploy)]를 선택합니다.

다음 절차의 작업을 완료하여 이 솔루션의 사용을 시작합니다.

만료 이메일 알림을 자동화하려면

이 예시에서는 EventBridge Amazon을 통해 이벤트가 발생하는 시점에 만료 예정인 각 인증서에 대해 단일 이메일을 제공합니다. 기본적으로 ACM은 만료일이 45일 이하로 남은 인증서에 대해 매일 이벤트를 발생시킵니다. (이 기간은 ACM API PutAccountConfiguration작업을 사용하여 사용자 지정할 수 있습니다.) 이러한 각 이벤트는 다음과 같은 일련의 자동화된 작업을 트리거합니다.

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
  1. Lambda 함수를 생성하고 권한을 구성합니다. (이미 완료됨 - Lambda 함수 및 IAM 역할을 설정하려면 참조).

  2. 알림을 보내는 데 사용할 Lambda 함수의 표준 SNS 주제입니다. 자세한 내용은 Amazon SNS 주제 생성을 참조하세요.

  3. 관심있는 당사자가 새로운 SNS 주제를 구독하도록 설정합니다. 자세한 내용은 Amazon SNS 주제에 구독 설정을 참조하세요.

  4. Amazon EventBridge 규칙을 생성하여 Lambda 함수를 트리거합니다. 자세한 내용은 이벤트에 반응하는 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 주제