Respuesta a un evento con una función Lambda - AWS Certificate Manager

Respuesta a un evento con una función Lambda

En este procedimiento se muestra cómo utilizar AWS Lambda para escuchar en Amazon EventBridge, crear notificaciones con Amazon Simple Notification Service (SNS) y publicar los hallazgos en AWS Security Hub, al proporcionar visibilidad a los administradores y equipos de seguridad.

Para configurar una función Lambda y un rol de IAM
  1. Primero configure un rol de AWS Identity and Access Management (IAM) y defina los permisos necesarios para la función Lambda. Esta práctica recomendada de seguridad brinda flexibilidad a la hora de designar quién tiene autorización para llamar a la función y de limitar los permisos concedidos a esa persona. No se recomienda ejecutar la mayoría de las operaciones de AWS directamente en una cuenta de usuario y, en especial, en una cuenta de administrador.

    Abra la consola de IAM en https://console.aws.amazon.com/iam/.

  2. Utilice el editor de políticas de JSON para crear la política definida en la siguiente plantilla. Proporcione detalles sobre su propia región y cuenta de AWS. Para obtener más información, consulte Creación de políticas en la pestaña de 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. Cree un rol de IAM y adjúntele la política nueva. Para obtener información sobre cómo crear un rol de IAM y adjuntar una política, consulte Creación de un rol para un servicio de AWS (consola).

  4. Abra la consola de AWS Lambda en https://console.aws.amazon.com/lambda/.

  5. Cree la función Lambda. Para obtener más información, consulte Crear una función Lambda con la consola. Realice los pasos siguientes:

    1. En la página Create function (Crear función), elija la opción Author from scratch (Crear desde cero) para crear la función.

    2. Especifique un nombre como “handle-expiring-certificates” en el campo Function name (Nombre de la función).

    3. En la lista Runtime (Tiempo de ejecución), elija Python 3.8.

    4. Expanda Change default execution role (Cambiar rol de ejecución predeterminado) y elija Use an existing role (Utilizar un rol existente).

    5. En la lista Existing role (Rol existente), elija el rol que creó antes.

    6. Elija Crear función.

    7. En Function code (Código de la función), inserte el siguiente código:

      # 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. En Environment variables (Variables de entorno), elija Edit (Editar) y, opcionalmente, agregue las siguientes variables.

      • (Opcional) EXPIRY_DAYS

        Especifica el tiempo de espera, en días, antes de que se envíe el aviso de vencimiento del certificado. La función tiene un valor predeterminado de 45 días, pero puede especificar valores personalizados.

      • (Opcional) SNS_TOPIC_ARN

        Especifica un ARN para un Amazon SNS. Proporcione el ARN completo en el formato arn:aws:sns:<region>:<account-number>:<topic-name>.

      • (Opcional) SECURITY_HUB_REGION

        Especifica una AWS Security Hub en una región diferente. Si no se especifica, se utiliza la región de la función Lambda en ejecución. Si la función se ejecuta en varias regiones, puede ser conveniente que todos los mensajes de certificado vayan a Security Hub en una sola región.

    9. En Basic settings (Configuración básica), establezca el valor Timeout (Tiempo de espera) en 30 segundos.

    10. En la parte superior de la página, elija Deploy (Implementar).

Complete las tareas del siguiente procedimiento para comenzar a utilizar esta solución.

Automatizar un aviso de vencimiento por correo electrónico

En este ejemplo, proporcionamos un solo correo electrónico para cada certificado que vence en el momento en que se genera el evento a través de Amazon EventBridge. De forma predeterminada, cada día ACM genera un evento para un certificado al que le quedan 45 días o menos para vencer. (Este periodo se puede personalizar con la operación PutAccountConfiguration de la API de ACM). Cada uno de estos eventos activa la siguiente cadena de acciones automatizadas:

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. Cree la función Lambda y configure los permisos. (Ya se ha completado, consulte Para configurar una función Lambda y un rol de IAM).

  2. Cree un tema de SNS estándar para la función Lambda que se utilizará a fin de enviar notificaciones. Para obtener más información, consulte Creación de un tema de Amazon SNS.

  3. Suscriba las partes interesadas al tema de SNS nuevo. Para obtener más información, consulte Suscripción a un tema de Amazon SNS.

  4. Crear una regla de Amazon EventBridge para activar una función de Lambda. Para más información, consulte Creating Amazon EventBridge rules that react to events (Creación de reglas de Amazon EventBridge que reaccionan a los eventos).

    En la consola de Amazon EventBridge en https://console.aws.amazon.com/events/, diríjase a la página Events (Eventos) > Rules (Reglas) y elija Create rule (Crear regla). Especifique el Service Name (Nombre del servicio), Event Type (Tipo de evento) y Lambda function (Función Lambda). En el editor Event Pattern preview (Vista previa de patrón de eventos), pegue el siguiente código:

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

    Un evento como el que recibe Lambda se muestra en Show sample event(s) (Mostrar eventos de muestra):

    { "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" } }
Eliminación

Una vez que ya no necesite la configuración de ejemplo, o cualquier configuración, es una práctica recomendada eliminar todos los rastros de esta para evitar problemas de seguridad y cargos futuros inesperados:

  • Política y rol de IAM

  • Lambda function

  • Regla de CloudWatch Events

  • CloudWatch Logs asociado a Lambda

  • Tema de SNS