Reacties op integriteitsvalidatiefouten automatiseren

Leer hoe u een trigger voor Cloud Functions kunt gebruiken om automatisch te reageren op gebeurtenissen naar aanleiding van integriteitscontroles door afgeschermde VM-instanties.

Overzicht

Tijdens de integriteitscontrole worden metingen van afgeschermde VM-instanties verzameld en weergegeven in Stackdriver Logging. Als de integriteitsmetingen elke keer dat een afgeschermde VM-instantie opnieuw wordt opgestart variëren, mislukt de integriteitsvalidatie. Deze fout wordt vastgelegd als een geregistreerde gebeurtenis en wordt ook opgenomen in Stackdriver Monitoring.

De integriteitsmetingen van een afgeschermde VM kunnen soms ook om legitieme redenen variëren. Een systeemupdate kan bijvoorbeeld wijzigingen in de kernel van het besturingssysteem veroorzaken. Daarom kunt u een afgeschermde VM-instantie tijdens een integriteitscontrole vragen een nieuwe benchmark voor integriteitsbeleid te leren voor wanneer een verwachte integriteitsvalidatiefout optreedt.

In deze tutorial maakt u eerst een eenvoudig automatisch systeem dat afgeschermde VM-instanties met mislukte integriteitsvalidatie afsluit:

  1. Exporteer alle gebeurtenissen naar aanleiding van de integriteitscontrole naar een Cloud Pub/Sub-onderwerp.
  2. Maak een trigger voor Cloud Functions die de gebeurtenissen in dat onderwerp gebruikt om afgeschermde VM-instanties te identificeren en af te sluiten wanneer de integriteitsvalidatie mislukt.

Vervolgens kunt u het systeem desgewenst uitbreiden zodat het systeem afgeschermde VM-instanties met mislukte integriteitsvalidatie vraagt om de nieuwe benchmark te leren als deze overeenkomt met een bekende juiste meting, of deze instanties anders afsluit:

  1. Maak een Cloud Firestore-database om een aantal bekende benchmarkmetingen met goede integriteit te behouden.
  2. Update de trigger voor Cloud Functions zodat deze afgeschermde VM-instanties met mislukte integriteitsvalidatie vraagt om de nieuwe benchmark te leren als deze zich in de database bevindt, of ze anders afsluit.

Als u ervoor kiest om de uitgebreide oplossing te implementeren, kunt u deze op de volgende manier gebruiken:

  1. Elke keer dat er een update is die naar verwachting om een geldige reden validatiefouten veroorzaakt, voert u die update uit in een enkele afgeschermde VM-instantie in de instantiegroep.
  2. Gebruik het eind van het opstartproces van de geüpdatete VM-instantie als bron en voeg de benchmarkmetingen voor het nieuwe beleid toe aan de database door een nieuw document te maken in de verzameling known_good_measurements. Zie Een database van bekende juiste benchmarkmetingen maken voor meer informatie.
  3. Update de resterende afgeschermde VM-instanties. De trigger vraagt de resterende instanties om de nieuwe benchmark te leren, omdat de juistheid ervan kan worden geverifieerd. Zie De trigger voor Cloud Functions updaten om bekende juiste benchmarks te leren voor meer informatie.

Vereisten

  • Gebruik een project waarvoor Cloud Firestore in systeemeigen modus is geselecteerd als de databaseservice. U selecteert de databaseservice wanneer u het project aanmaakt. Dit kan later niet worden gewijzigd. Als uw project geen gebruikmaakt van Cloud Firestore in systeemeigen modus, wordt het bericht 'Dit project maakt gebruik van een andere databaseservice' weergegeven wanneer u de Cloud Firestore-console opent.
  • Zorg dat een afgeschermde VM-instantie met Compute Engine in dat project fungeert als de bron van benchmarkmetingen voor integriteit. De afgeschermde VM-instantie moet minstens één keer opnieuw zijn opgestart.
  • Zorg dat de opdrachtregeltool gcloud is geïnstalleerd.
  • Schakel de Stackdriver Logging API's en Cloud Functions API's in door deze stappen te volgen:

    1. Ga naar 'API's en services'
    2. Kijk of Cloud Functions API en Stackdriver Logging API worden weergegeven in de lijst Ingeschakelde API's en services.
    3. Als een van de API's niet wordt weergegeven, klikt u op API's en services toevoegen.
    4. Zoek indien nodig naar de API's en schakel ze in.

Logboekitems van integriteitscontroles exporteren naar een Cloud Pub/Sub-onderwerp

Gebruik Logging om alle logboekitems van integriteitscontroles die zijn gegenereerd door beschermde VM-instanties te exporteren naar een Cloud Pub/Sub-onderwerp. U gebruikt dit onderwerp als gegevensbron voor een trigger voor Cloud Functions om automatisch te reageren op gebeurtenissen naar aanleiding van integriteitscontroles.

  1. Ga naar Stackdriver Logging.
  2. Klik op de dropdownpijl rechts van Filteren op label of tekstzoekopdracht en klik vervolgens op Converteren naar geavanceerde filter.
  3. Typ de volgende geavanceerde filter:

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

    en vervang YOUR_PROJECT_ID door de ID van uw project.

  4. Klik op Filter verzenden.

  5. Klik op Export maken.

  6. Bij Naam van logboeklocatie typt u integriteitscontrole.

  7. Selecteer Cloud Pub/Sub voor Logboeklocatieservice.

  8. Klik op de dropdownpijl rechts van Bestemming logboeklocatie en klik vervolgens op Nieuw Cloud Pub/Sub-onderwerp maken.

  9. Bij Naam typt u integriteitscontrole en vervolgens klikt u op Maken.

  10. Klik op Logboeklocatie maken.

Een trigger voor Cloud Functions maken om te reageren op integriteitsfouten

Maak een trigger voor Cloud Functions die de gegevens in het Cloud Pub/Sub-onderwerp leest en alle afgeschermde VM-instanties met mislukte integriteitsvalidatie onderbreekt.

  1. De volgende code definieert de trigger voor Cloud Functions. Kopieer deze code naar een bestand met de naam 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'))
    
        if report_event is None:
          # We received a different event type, ignore.
          return
    
        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. Maak op dezelfde locatie als main.py een bestand met de naam requirements.txt en kopieer de volgende afhankelijkheden:

    google-api-python-client==1.6.6
    google-auth==1.4.1
    google-auth-httplib2==0.0.3
    
  3. Open een terminalvenster en ga naar de directory met main.py en requirements.txt.

  4. Voer de opdracht gcloud beta functions deploy uit om de trigger te implementeren:

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

    en vervang YOUR_PROJECT_ID door de ID van uw project.

Een database van bekende juiste benchmarkmetingen maken

Maak een Cloud Firestore-database zodat u beschikt over een aantal bekende benchmarkmetingen met goede integriteit. U moet handmatig benchmarkmetingen toevoegen om deze database up-to-date te houden.

  1. Ga naar de pagina VM-instanties.
  2. Klik op de ID van de afgeschermde VM-instantie om de pagina Details VM-instantie te openen.
  3. Klik onder Logboeken op Stackdriver Logging.
  4. Zoek naar het meest recente logboekitem lateBootReportEvent.
  5. Vouw het logboekitem uit > jsonPayload > lateBootReportEvent > policyMeasurements.
  6. Let op de waarden voor de elementen in lateBootReportEvent > policyMeasurements.
  7. Ga naar de Cloud Firestore-console.
  8. Kies Verzameling starten.
  9. Bij Collectie-ID typt u known_good_measurements.
  10. Bij Document-ID typt u baseline1.
  11. Bij Veldnaam typt u de pcrNum-veldwaarde van element 0 in lateBootReportEvent > policyMeasurements.
  12. Bij Veldtype selecteert u kaart.
  13. Voeg drie velden met tekenreeksen toe aan het kaartveld, respectievelijk hashAlgo, pcrNum en waarde. Verander hun waarden in de waarden van de velden van element 0 in lateBootReportEvent > policyMeasurements.
  14. Maak meer kaartvelden, één voor elk extra element in lateBootReportEvent > policyMeasurements. Geef ze dezelfde subvelden als het eerste kaartveld. De waarden voor die subvelden moeten overeenkomen met die in elk van de extra elementen.

    Als u bijvoorbeeld een Linux VM gebruikt, moet de verzameling er wanneer u klaar bent ongeveer als volgt uitzien:

    Een Cloud Firestore-database met een voltooide verzameling known_good_measurements.

De trigger voor Cloud Functions updaten om bekende juiste benchmarks te leren

  1. De volgende code maakt een trigger voor Cloud Functions die ervoor zorgt dat alle afgeschermde VM-instanties met mislukte integriteitsvalidatie de nieuwe benchmark leren als deze zich in de database met bekende goede metingen bevindt, of dat ze anders worden afgesloten. Kopieer deze code en overschrijf de bestaande code in 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', {})
        entry_type = payload.get('@type')
        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().setShieldedInstanceIntegrityPolicy(
            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. Kopieer de volgende afhankelijkheden en gebruik deze om de bestaande code in requirements.txt te overschrijven:

    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. Open een terminalvenster en ga naar de directory met main.py en requirements.txt.

  4. Voer de opdracht gcloud beta functions deploy uit om de trigger te implementeren:

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

    en vervang YOUR_PROJECT_ID door de ID van uw project.