Répondre à un événement avec une fonction Lambda - AWS Certificate Manager

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Répondre à un événement avec une fonction Lambda

Cette procédure explique comment AWS Lambda écouter sur Amazon EventBridge, créer des notifications avec Amazon Simple Notification Service (SNS) et publier des résultats sur Amazon AWS Security Hub, offrant ainsi une visibilité aux administrateurs et aux équipes de sécurité.

Pour configurer une fonction Lambda et un rôle IAM
  1. Configurez d'abord un rôle AWS Identity and Access Management (IAM) et définissez les autorisations requises par la fonction Lambda. Cette bonne pratique en matière de sécurité vous offre une certaine souplesse pour désigner la personne autorisée à appeler la fonction, et pour limiter les autorisations accordées à cette personne. Il n'est pas recommandé d'exécuter la plupart AWS des opérations directement sous un compte utilisateur et surtout pas sous un compte administrateur.

    Ouvrez la console IAM à l’adresse https://console.aws.amazon.com/iam/.

  2. Utilisez l'éditeur de stratégie JSON pour créer la stratégie définie dans le modèle ci-dessous. Indiquez votre région et les détails de votre AWS compte. Pour plus d'informations, consultez Création de stratégies sous l'onglet 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. Créez un rôle IAM et attachez la nouvelle stratégie à celui-ci. Pour plus d'informations sur la création d'un rôle IAM et l'attachement d'une politique, consultez la section Création d'un rôle pour un AWS service (console).

  4. Ouvrez la AWS Lambda console à l'adresse https://console.aws.amazon.com/lambda/.

  5. Créez la fonction Lambda. Pour plus d'informations, consultez Créer une fonction Lambda à l'aide de la console. Procédez comme suit :

    1. Sur la page Créer une fonction, choisissez l'option Créer de bout en bout afin de créer la fonction.

    2. Spécifiez un nom tel que « handle-expiring-certificates » dans le champ Nom de la fonction.

    3. Dans la liste Environnement d'exécution, choisissez Python 3.8.

    4. Développez Modifier le rôle d'exécution par défaut et choisissez Utiliser un rôle existant.

    5. Dans la liste Rôle existant, choisissez le rôle que vous avez précédemment créé.

    6. Choisissez Créer une fonction.

    7. Sous Code de fonction, insérez le code suivant :

      # 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. Sous Variables d'environnement, choisissez Modifier et ajoutez éventuellement les variables suivantes.

      • (Facultatif) EXPIRY_DAYS

        Indique le délai, en jours, avant l'envoi de l'avis d'expiration du certificat. La fonction est définie par défaut sur 45 jours, mais vous pouvez spécifier des valeurs personnalisées.

      • (Facultatif) SNS_TOPIC_ARN

        Indique un ARN pour un service Amazon SNS. Entrez l'ARN complet au format arn:aws:sns:<région>:<numéro-de-compte>:<nom-de-la-rubrique>.

      • (Facultatif) SECURITY_HUB_REGION

        Spécifie un AWS Security Hub dans une autre région. Si cela n'est pas indiqué, la région de la fonction Lambda en cours d'exécution est utilisée. Si la fonction est exécutée dans plusieurs régions, il est préférable que tous les messages de certificat soient acheminés vers le Security Hub d'une même région.

    9. Sous Paramètres de base, définissez Expiration sur 30 secondes.

    10. En haut de la page, choisissez Déployer.

Effectuez les tâches de la procédure suivante pour commencer à utiliser cette solution.

Pour automatiser l'envoi d'un avis d'expiration par e-mail

Dans cet exemple, nous fournissons un e-mail unique pour chaque certificat expirant au moment où l'événement est déclenché via Amazon EventBridge. Par défaut, ACM déclenche un événement par jour au cours des 45 jours qui précèdent l'expiration d'un certificat. (Cette période peut être personnalisée à l'aide du PutAccountConfigurationfonctionnement de l'API ACM.) Chacun de ces événements déclenche la cascade d'actions automatisées suivante :

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. Créez la fonction Lambda et configurez les autorisations. (Déjà terminé – voir Pour configurer une fonction Lambda et un rôle IAM).

  2. Créez une rubrique SNS standard à utiliser par la fonction Lambda pour envoyer des notifications. Pour plus d'informations, consultez Création d'une rubrique Amazon SNS.

  3. Abonnez toutes les parties intéressées à la nouvelle rubrique SNS. Pour plus d'informations, consultez Abonnement à une rubrique Amazon SNS.

  4. Créez une EventBridge règle Amazon pour déclencher la fonction Lambda. Pour plus d'informations, consultez la section Création de EventBridge règles Amazon qui réagissent aux événements.

    Dans la EventBridge console Amazon à l'adresse https://console.aws.amazon.com/events/, accédez à la page Événements > Règles et choisissez Create rule. Complétez les champs Nom du service, Type d'événement et Fonction Lambda. Dans l'éditeur Aperçu du modèle d'événement, collez le code suivant :

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

    Un événement tel que Lambda reçoit apparaît sous Afficher les exemples d'événements :

    { "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" } }
Pour nettoyer

Une fois que vous n'avez plus besoin de l'exemple de configuration, ou de toute autre configuration, il est préférable d'en supprimer toute trace pour éviter les problèmes de sécurité et les frais imprévus :

  • Politique IAM et rôle

  • Fonction Lambda

  • CloudWatch Règle des événements

  • CloudWatch Logs associés à Lambda

  • Rubrique SNS