Cómo automatizar respuestas para fallas de validación de integridad

Obtén más información sobre cómo utilizar un activador de Cloud Functions para actuar de forma automática ante los eventos de supervisión de la integridad de una VM protegida.

Resumen

La supervisión de la integridad recolecta mediciones desde las instancias de VM protegidas y las muestra en Stackdriver Logging. Si las mediciones de integridad cambian durante los inicios de una instancia de VM protegida, la validación de integridad fallará. Este error se captura como un evento registrado y se genera también en Stackdriver Monitoring.

A veces, las mediciones de la integridad de las VM protegidas cambian por una razón legítima. Por ejemplo, una actualización del sistema puede ocasionar cambios esperados en el kernel del sistema operativo. Debido a esto, la supervisión de la integridad permite que una instancia de VM protegida aprenda un modelo de referencia de política de integridad nuevo cuando surja una falla esperada en la validación de integridad.

En este instructivo, primero crearás un sistema automatizado simple para cerrar las instancias de VM protegidas que presenten fallas en la validación de integridad:

  1. Exporta todos los eventos de supervisión de la integridad a un tema de Cloud Pub/Sub.
  2. Crea un activador de Cloud Functions que utilice los eventos de ese tema para identificar y cerrar las instancias de VM protegidas que presenten fallas en la validación de integridad.

Luego, de manera opcional, puedes expandir el sistema para que las instancias de VM protegidas que fallen en la validación de integridad aprendan el modelo de referencia nuevo en caso de que coincida con una medida buena, o se cierren en caso contrario.

  1. Crea una base de datos de Cloud Firestore para mantener un conjunto de medidas buenas del modelo de referencia de integridad.
  2. Actualiza el activador de Cloud Functions para que las instancias de VM protegidas que fallen en la validación de integridad aprendan el modelo de referencia nuevo si es que existe en la base de datos, o que se cierren de lo contrario.

Si eliges implementar la solución expandida, hazlo de la siguiente manera:

  1. Cada vez que haya una actualización que pueda causar una falla en la validación por una razón legítima, ejecuta esa actualización en una única instancia de VM protegida en el grupo de instancias.
  2. Utiliza el evento de inicio retardado de la instancia de VM actualizada como origen y agrega las medidas nuevas del modelo de referencia de la política a la base de datos a través de la creación de un documento nuevo en la colección known_good_measurements. Consulta Cómo crear una base de datos de medidas buenas del modelo de referencia para obtener más información.
  3. Actualiza las instancias de VM protegida restantes. El activador hace que las instancias restantes aprendan el nuevo modelo de referencia porque puede verificarse como bueno.

Requisitos previos

  • Utiliza un proyecto que tenga seleccionado Cloud Firestore en modo nativo como el servicio de base de datos. Debes realizar esta selección cuando creas el proyecto y no podrás cambiarla. Si tu proyecto no utiliza Cloud Firebase en modo nativo, verás el mensaje "Este proyecto utiliza otro servicio de base de datos" cuando abras la consola de Cloud Firestore.
  • Ten una instancia de VM protegida de Compute Engine en ese proyecto, que funcione como la fuente de las medidas del modelo de referencia de integridad. Las instancias de VM protegida deben reiniciarse al menos una vez.
  • Ten instalada la herramienta de línea de comandos de gcloud.
  • Habilita las API de Stackdriver Logging y de Cloud Functions a través de los siguientes pasos:

    1. Ve a API y servicios.
    2. Verifica si la API de Cloud Functions y la API de Stackdriver Logging figuran en la lista de API y servicios habilitados.
    3. Si alguna de las API no aparece, haz clic en Agregar API y servicios.
    4. Busca y habilita las API según sea necesario.

Cómo exportar las entradas de registro de la supervisión de la integridad a un tema de Cloud Pub/Sub

Utiliza Logging para exportar todas las entradas de registro de supervisión de la integridad generadas por las instancias de VM protegidas a un tema de Cloud Pub/Sub. Este tema se utiliza como una fuente de datos para que el activador de Cloud Functions automatice las respuestas en los eventos de supervisión de la integridad.

  1. Ve a Stackdriver Logging
  2. Haz clic en la flecha desplegable a la derecha de Filtrar por etiqueta o búsqueda de texto, y luego haz clic en Convertir en filtro avanzado.
  3. Ingresa el siguiente filtro avanzado:

    resource.type="gce_instance" AND logName:"projects/YOUR_PROJECT_NAME/logs/compute.googleapis.com%2Fshielded_vm_integrity"
    

    reemplaza YOUR_PROJECT_NAME con el nombre de tu proyecto.

  4. Haz clic en Enviar filtro.

  5. Haz clic en Crear exportación.

  6. En Nombre del receptor, escribe integrity-monitoring.

  7. En Servicio del receptor, selecciona Cloud Pub/Sub.

  8. Haz clic en la flecha desplegable a la derecha de Destino del receptor, y luego haz clic en Crear tema nuevo de Cloud Pub/Sub.

  9. En Nombre, escribe integrity-monitoring, y luego haz clic en Crear.

  10. Haz clic en Crear receptor.

Cómo crear un activador de Cloud Functions que responda a las fallas de integridad

Crea un activador de Cloud Functions que lea los datos en el tema de Cloud Pub/Sub y detenga cualquier instancia de VM protegida que falle en la validación de integridad.

  1. Con el siguiente código, se define el activador de Cloud Functions. Cópialo en un archivo llamado main.py.

    import base64
    import json
    import googleapiclient.discovery
    
    def shutdown_vm(data, context):
        """A Cloud Function that shuts down a VM on failed integrity check."""
        log_entry = json.loads(base64.b64decode(data['data']).decode('utf-8'))
        payload = log_entry.get('jsonPayload', {})
        entry_type = payload.get('@type')
        if entry_type != 'type.googleapis.com/cloud_integrity.IntegrityEvent':
          raise TypeError("Unexpected log entry type: %s" % entry_type)
    
        report_event = (payload.get('earlyBootReportEvent')
            or payload.get('lateBootReportEvent'))
    
        policy_passed = report_event['policyEvaluationPassed']
        if not policy_passed:
          print('Integrity evaluation failed: %s' % report_event)
          print('Shutting down the VM')
    
          instance_id = log_entry['resource']['labels']['instance_id']
          project_id = log_entry['resource']['labels']['project_id']
          zone = log_entry['resource']['labels']['zone']
    
          # Shut down the instance.
          compute = googleapiclient.discovery.build(
              'compute', 'v1', cache_discovery=False)
    
          # Get the instance name from instance id.
          list_result = compute.instances().list(
              project=project_id,
              zone=zone,
                  filter='id eq %s' % instance_id).execute()
          if len(list_result['items']) != 1:
            raise KeyError('unexpected number of items: %d'
                % len(list_result['items']))
          instance_name = list_result['items'][0]['name']
    
          result = compute.instances().stop(project=project_id,
              zone=zone,
              instance=instance_name).execute()
          print('Instance %s in project %s has been scheduled for shut down.'
              % (instance_name, project_id))
    
  2. En la misma ubicación que main.py, crea un archivo llamado requirements.txt y cópialo en las siguientes dependencias:

    google-api-python-client==1.6.6
    google-auth==1.4.1
    google-auth-httplib2==0.0.3
    
  3. Abre una ventana de terminal y navega hacia el directorio que contiene main.py y requirements.txt.

  4. Ejecuta el comando gcloud beta functions deploy para implementar el activador:

    gcloud beta functions deploy shutdown_vm --project YOUR_PROJECT_NAME \
        --runtime python37 --trigger-resource integrity-monitoring \
        --trigger-event google.pubsub.topic.publish
    

    reemplaza YOUR_PROJECT_NAME con el nombre de tu proyecto.

Cómo crear una base de datos con las medidas buenas del modelo de referencia

Crea una base de datos de Cloud Firestore para proporcionar una fuente de medidas buenas del modelo de referencia de la política de integridad. Para mantener esta base de datos actualizada, debes agregar las medidas del modelo de referencia de forma manual.

  1. Ve a la página de instancias de VM.
  2. Haz clic en el ID de la instancia de VM protegida para abrir la página Detalles de la instancia de VM.
  3. En Registros, haz clic en Stackdriver Logging.
  4. Busca la entrada de registro lateBootReportEvent más reciente.
  5. Expande la entrada de registro > jsonPayload > lateBootReportEvent > policyMeasurements.
  6. Toma nota de los valores de los elementos que figuran en lateBootReportEvent > policyMeasurements.
  7. Ve a la consola de Cloud Firestore.
  8. Selecciona Start collection (Iniciar colección).
  9. En ID de la colección, escribe known_good_measurements.
  10. En ID del documento, escribe baseline1.
  11. En Nombre del campo, escribe el valor del campo pcrNum del elemento 0 en lateBootReportEvent > policyMeasurements.
  12. En Tipo de campo, selecciona mapa.
  13. Agrega tres campos de strings al campo de mapa, llamados hashAlgo, pcrNum, y value, respectivamente. Establece los mismos valores que los de los campos del elemento 0 en lateBootReportEvent > policyMeasurements.
  14. Crea más campos de mapa, uno por cada elemento adicional en lateBootReportEvent > policyMeasurements. Crea los mismos subcampos que en el primer campo de mapa. Los valores de esos subcampos deben estar vinculados a los de cada uno de los elementos adicionales.

    Por ejemplo, si utilizas una VM de Linux, la colección debería ser similar a la que se muestra a continuación una vez que hayas terminado:

    Una base de datos de Cloud Firestore que muestra una colección known_good_measurements completada.

Cómo actualizar el activador de Cloud Functions

  1. El siguiente código crea un activador de Cloud Functions que hace que cualquier instancia de VM protegida que falle en la validación de integridad aprenda el modelo de referencia nuevo si se encuentra en la base de datos de medidas buenas, o que se cierre en caso contrario. Copia este código y úsalo para reemplazar el código existente en main.py.

    import base64
    import json
    import googleapiclient.discovery
    
    import firebase_admin
    from firebase_admin import credentials
    from firebase_admin import firestore
    
    PROJECT_ID = 'YOUR_PROJECT_ID'
    
    firebase_admin.initialize_app(credentials.ApplicationDefault(), {
        'projectId': PROJECT_ID,
    })
    
    def pcr_values_to_dict(pcr_values):
      """Converts a list of PCR values to a dict, keyed by PCR num"""
      result = {}
      for value in pcr_values:
        result[value['pcrNum']] = value
      return result
    
    def instance_id_to_instance_name(compute, zone, project_id, instance_id):
      list_result = compute.instances().list(
          project=project_id,
          zone=zone,
          filter='id eq %s' % instance_id).execute()
      if len(list_result['items']) != 1:
        raise KeyError('unexpected number of items: %d'
            % len(list_result['items']))
      return list_result['items'][0]['name']
    
    def relearn_if_known_good(data, context):
        """A Cloud Function that shuts down a VM on failed integrity check.
        """
        log_entry = json.loads(base64.b64decode(data['data']).decode('utf-8'))
        payload = log_entry.get('jsonPayload', {})
        if entry_type != 'type.googleapis.com/cloud_integrity.IntegrityEvent':
          raise TypeError("Unexpected log entry type: %s" % entry_type)
    
        # We only send relearn signal upon receiving late boot report event: if
        # early boot measurements are in a known good database, but late boot
        # measurements aren't, and we send relearn signal upon receiving early boot
        # report event, the VM will also relearn late boot policy baseline, which we
        # don't want, because they aren't known good.
        report_event = payload.get('lateBootReportEvent')
        if report_event is None:
          return
    
        evaluation_passed = report_event['policyEvaluationPassed']
        if evaluation_passed:
          # Policy evaluation passed, nothing to do.
          return
    
        # See if the new measurement is known good, and if it is, relearn.
        measurements = pcr_values_to_dict(report_event['policyMeasurements'])
    
        db = firestore.Client()
        kg_ref = db.collection('known_good_measurements')
    
        # Check current measurements against known good database.
        relearn = False
        for kg in kg_ref.get():
          if kg.to_dict() == measurements:
            relearn = True
    
        if not relearn:
          print('New measurement is not known good. Shutting down a VM.')
          instance_name = instance_id_to_instance_name(
            compute, zone, project_id, instance_id)
          result = compute.instances().stop(project=project_id,
              zone=zone,
              instance=instance_name).execute()
          print('Instance %s in project %s has been scheduled for shut down.'
                % (instance_name, project_id))
    
        print('New measurement is known good. Relearning...')
        instance_id = log_entry['resource']['labels']['instance_id']
        project_id = log_entry['resource']['labels']['project_id']
        zone = log_entry['resource']['labels']['zone']
    
        # Issue relearn API call.
        compute = googleapiclient.discovery.build('compute', 'beta',
            cache_discovery=False)
        instance_name = instance_id_to_instance_name(
            compute, zone, project_id, instance_id)
        result = compute.instances().setShieldedVmIntegrityPolicy(
            project=project_id,
            zone=zone,
            instance=instance_name,
            body={'updateAutoLearnPolicy':True}).execute()
        print('Instance %s in project %s has been scheduled for relearning.'
              % (instance_name, project_id))
    
  2. Copia las siguientes dependencias y utilízalas para reemplazar el código existente en requirements.txt:

    google-api-python-client==1.6.6
    google-auth==1.4.1
    google-auth-httplib2==0.0.3
    google-cloud-firestore==0.29.0
    firebase-admin==2.13.0
    
  3. Abre una ventana de terminal y navega hacia el directorio que contiene main.py y requirements.txt.

  4. Ejecuta el comando gcloud beta functions deploy para implementar el activador:

    gcloud beta functions relearn_if_known_good --project YOUR_PROJECT_NAME \
        --runtime python37 --trigger-resource integrity-monitoring \
        --trigger-event google.pubsub.topic.publish
    

    reemplaza YOUR_PROJECT_NAME con el nombre de tu proyecto.

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…