Como automatizar respostas para falhas de validação da integridade

Aprenda a usar um acionador do Cloud Functions para atuar automaticamente nos eventos de monitoramento de integridade da VM protegida.

Visão geral

O monitoramento de integridade coleta medições de instâncias de VM protegidas e as exibe no Stackdriver Logging. Se as medições de integridade forem alteradas nas inicializações de uma instância de VM protegida, a validação de integridade falhará. Essa falha é captada como um evento registrado e também é gerada no Stackdriver Monitoring.

Às vezes, as medições de integridade da VM protegida são alteradas por um motivo legítimo. Por exemplo, uma atualização do sistema pode causar alterações esperadas no kernel do sistema operacional. Por isso, o monitoramento de integridade permite que você solicite que uma instância de VM protegida aprenda um novo valor de referência de política de integridade, no caso de uma falha de validação de integridade esperada.

Neste tutorial, primeiro você criará um sistema automatizado simples que encerre as instâncias de VM protegidas que falharem na validação de integridade:

  1. Exporte todos os eventos de monitoramento de integridade para um tópico do Cloud Pub/Sub.
  2. Crie um acionador do Cloud Functions que use os eventos nesse tópico para identificar e encerrar instâncias de VM protegidas que falharem na validação de integridade.

Em seguida, é possível expandir o sistema caso as instâncias de VM protegidas falhem na validação de integridade. Isso fará com que elas aprendam o novo valor de referência, se ele corresponder a uma medição válida. Caso contrário, elas serão encerradas.

  1. Crie um banco de dados do Cloud Firestore para manter um conjunto de medições válidas e conhecidas de valores de referência de integridade.
  2. Atualize o acionador do Cloud Functions, isso fará com que as instâncias de VM protegidas que falharem na validação de integridade aprendam o novo valor de referência, se ele estiver no banco de dados. Caso contrário, elas serão encerradas.

Se você optar por implementar a solução expandida, use-a da seguinte maneira:

  1. Sempre que houver uma atualização que possa causar uma falha de validação por um motivo legítimo, execute essa atualização em uma única instância de VM protegida no grupo de instâncias.
  2. Use o evento de inicialização tardia da instância da VM atualizada como uma origem e adicione as novas medições de valores de referência da política ao banco de dados por meio da criação de um novo documento na coleção known_good_measurements. Para saber mais, consulte Criar um banco de dados de medições válidas de valores de referência.
  3. Atualize as demais instâncias de VM protegidas. O acionador solicita que as demais instâncias aprendam o novo valor de referência porque ele pode ser verificado como válido.

Pré-requisitos

  • Usar um projeto que tenha o Cloud Firestore no modo Native selecionado como o serviço de banco de dados. Essa seleção é feita quando você cria o projeto, e ela não pode ser alterada. Se o projeto não usar o Cloud Firestore no modo Native, a mensagem "Este projeto usa outro serviço de banco de dados" será exibida quando você abrir o Console do Cloud Firestore.
  • Ter uma instância de VM protegida do Compute Engine nesse projeto para servir como origem das medições de valores de referência de integridade. É preciso que a instância da VM protegida tenha sido reiniciada pelo menos uma vez.
  • Ter a ferramenta de linha de comando gcloud instalada.
  • Ativar as APIs do Stackdriver Logging e do Cloud Functions seguindo estes passos:

    1. Acessar APIs e serviços
    2. Veja se a API do Cloud Functions e a API do Stackdriver Logging aparecem na lista APIs e serviços ativados.
    3. Se alguma das APIs não aparecer, clique em Adicionar APIs e serviços.
    4. Pesquise e ative as APIs, conforme necessário.

Exportar entradas de registro de monitoramento de integridade para um tópico do Cloud Pub/Sub

Use o Logging para exportar todas as entradas de registro de monitoramento de integridade geradas por instâncias de VM protegidas para um tópico do Cloud Pub/Sub. Use esse tópico como uma fonte de dados para um acionador do Cloud Functions para automatizar respostas a eventos de monitoramento de integridade.

  1. Acessar o Stackdriver Logging
  2. Clique na seta suspensa à direita de Filtrar por rótulo ou pesquisa de texto e clique em Converter para filtro avançado.
  3. Digite o seguinte filtro avançado:

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

    substituindo YOUR_PROJECT_NAME pelo nome do seu projeto.

  4. Clique em Enviar filtro.

  5. Clique em Criar exportação.

  6. Para Nome do coletor, digite integrity-monitoring.

  7. Para Serviço de coletor, selecione Cloud Pub/Sub.

  8. Clique na seta suspensa à direita do Destino do coletor e depois clique em Criar novo tópico do Cloud Pub/Sub.

  9. Para Nome, digite integrity-monitoring e depois clique em Criar.

  10. Clique em Criar coletor.

Criar um acionador do Cloud Functions para responder a falhas de integridade

Crie um acionador do Cloud Functions que leia os dados no tópico Cloud Pub/Sub e que interrompa qualquer instância de VM protegida que falhe na validação de integridade.

  1. O código a seguir define o acionador do Cloud Functions. Copie-o para um arquivo chamado 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. No mesmo local que main.py, crie um arquivo chamado requirements.txt e o copie nas seguintes dependências:

    google-api-python-client==1.6.6
    google-auth==1.4.1
    google-auth-httplib2==0.0.3
    
  3. Abra uma janela de terminal e navegue até o diretório em que estão main.py e requirements.txt.

  4. Execute o comando gcloud beta functions deploy para implantar o acionador:

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

    substituindo YOUR_PROJECT_NAME pelo nome do seu projeto.

Criar um banco de dados de medições válidas de valores de referência

Crie um banco de dados do Cloud Firestore para fornecer uma fonte de medições de valores de referência de políticas de integridade válidos. É preciso adicionar manualmente as medições de valores de referência para manter esse banco de dados atualizado.

  1. Acessar a página Instâncias de VM
  2. Clique no código da instância da VM protegida para abrir a página Detalhes da instância de VM.
  3. Em Registros, clique em Stackdriver Logging.
  4. Localize a entrada de registro lateBootReportEvent mais recente.
  5. Expanda a entrada de registro > jsonPayload > lateBootReportEvent > policyMeasurements.
  6. Observe os valores dos elementos contidos em lateBootReportEvent > policyMeasurements.
  7. Acesse o Console do Cloud Firestore
  8. Escolha Iniciar coleção.
  9. Para Código da coleção, digite known_good_measurements.
  10. Para Código do documento, digite baseline1.
  11. Para Nome do campo, digite o valor do campo pcrNum do elemento 0 em lateBootReportEvent > policyMeasurements.
  12. Para o Tipo de campo, selecione o mapa.
  13. Adicione três campos de string ao campo do mapa, denominados hashAlgo, pcrNum e value, respectivamente. Transforme os valores desses campos nos valores dos campos do elemento 0 em lateBootReportEvent > policyMeasurements.
  14. Crie mais campos de mapa, um para cada elemento extra em lateBootReportEvent > policyMeasurements. Dê a eles os mesmos subcampos que o primeiro campo do mapa. Os valores para esses subcampos devem ser associados aos de cada um dos outros elementos.

    Por exemplo, se você estiver usando uma VM do Linux, a coleção deverá ser semelhante à seguinte quando você terminar:

    Um banco de dados do Cloud Firestore mostrando uma coleção completa de known_good_measurements.

Atualizar o acionador do Cloud Functions

  1. O código a seguir cria um acionador do Cloud Functions que faz com que qualquer instância de VM protegida que falhar na validação de integridade aprenda o novo valor de referência, se ele estiver no banco de dados de medições válidas. Caso contrário, ela será encerrada. Copie este código e use-o para substituir o código atual em 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. Copie as seguintes dependências e use-as para substituir o código atual em 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. Abra uma janela de terminal e navegue até o diretório em que estão main.py e requirements.txt.

  4. Execute o comando gcloud beta functions deploy para implantar o acionador:

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

    substituindo YOUR_PROJECT_NAME pelo nome do seu projeto.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…