Répondre à un événement avec une fonction Lambda
Cette procédure montre comment procéder AWS Lambda pour écouter Amazon EventBridge, créer des notifications avec Amazon Simple Notification Service (SNS) et publier les résultats sur AWS Security Hub, ce qui permet aux administrateurs et aux équipes de sécurité d'avoir une meilleure visibilité.
Pour configurer une fonction Lambda et un rôle IAM
-
Tout d'abord, configurez un rôle AWS Identity and Access Management (IAM) et définissez les autorisations nécessaires à 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 est déconseillé d'exécuter la plupart des opérations AWS directement sous un compte utilisateur, et encore plus sous un compte administrateur.
Ouvrez la console IAM à l'adresse https://console.aws.amazon.com/iam/
. -
Utilisez l'éditeur de stratégie JSON pour créer la stratégie définie dans le modèle ci-dessous. Entrez votre région et les informations de votre compte AWS. 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":"*" } ] } -
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'attribution d'une stratégie, consultez Création d'un rôle pour un service AWS (console).
Ouvrez la console AWS Lambda à l'adresse https://console.aws.amazon.com/lambda/
. -
Créez la fonction Lambda. Pour plus d'informations, consultez Créer une fonction Lambda à l'aide de la console. Procédez comme suit :
-
Sur la page Créer une fonction, choisissez l'option Créer de bout en bout afin de créer la fonction.
-
Indiquez un nom tel que « gérer-certificats-expirés » dans le champ Nom de la fonction.
-
Dans la liste Environnement d'exécution, choisissez Python 3.8.
-
Développez Modifier le rôle d'exécution par défaut et choisissez Utiliser un rôle existant.
-
Dans la liste Rôle existant, choisissez le rôle que vous avez précédemment créé.
-
Choisissez Créer une fonction.
-
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:]
-
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
Indique 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.
-
-
Sous Paramètres de base, définissez Expiration sur 30 secondes.
-
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 seul e-mail pour chaque certificat arrivant à expiration au moment où l'événement est signalé par 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 de l'opération PutAccountConfiguration
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
-
Créez la fonction Lambda et configurez les autorisations. (Déjà terminé – voir Pour configurer une fonction Lambda et un rôle IAM).
-
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.
-
Abonnez toutes les parties intéressées à la nouvelle rubrique SNS. Pour plus d'informations, consultez Abonnement à une rubrique Amazon SNS.
-
Créez une règle Amazon EventBridge pour déclencher la fonction Lambda. Pour plus d'informations, consultez Création de règles Amazon EventBridge qui réagit aux événements.
Dans la console Amazon EventBridge, à l'adresse https://console.aws.amazon.com/events/
, naviguer vers Événements > Règles Page et choisissez Créer une règle. 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 :
-
Stratégie et rôle IAM
-
Fonction Lambda
-
Règle CloudWatch Events
-
Journaux CloudWatch Logs associés à Lambda
-
Rubrique SNS