Skip to content

Google SecOps SIEM

In order to integrate SailPoint Entro with Google SecOps SIEM you will need to forward SailPoint Entro's risks to the platform by creating a webhook in Google SecOps' SIEM Feed settings.

SailPoint Entro is an official Google SecOps technology partner.

Webhook Feed Configuration

  1. In your Google SecOps tenant, go to Settings > SIEM Settings

  2. From the Settings panel, at the bottom of the screen, select Feeds

  3. Create a new webhook by clicking on the "+ ADD NEW FEED" button at the top of the screen

  4. Fill in the form as below

    Note

    You will use the "Entrust DataControl Audit" Log type as SailPoint Entro is working on offering our Log Type within the Google SecOps platform

  5. If needed, add an ingestion label as show below

  6. Generate the secret key and copy it to a safe place

  7. On the Feeds screen, select the created feed and click "View Feed"

  8. In the View feed modal, copy the Endpoint Information address

  9. Head over to your GCP Project affiliated with the Google SecOps instance, and create a new API key in the Credentials section. Copy it once created.

  10. Restrict the API key's access to Chronicle

  11. Create the following URL structure and copy it onto SailPoint Entro in the Webhooks section

<Secops_Endpoint>?secret=<generated secret>&key=<gcp_api_key>

Now, SailPoint Entro risks will be forwarded to your Google SecOps SIEM.

Parsing

Google SecOps requires the webhook payload data to be parsed as SecOps' entity.

  1. Head over to Parsers

  2. Create a parser

  3. Fill in the modal as below

  4. Once created, a coding section will open

  5. Paste the code in the section below into the middle pane

    12KB
    secops-parser.txt
    Custom parser · text

    SailPoint Entro Google SecOps SIEM custom parser

    filter {
      # Parse the JSON message first
      json {
        source => "message"
        on_error => "not_json"
      }
    
      # Only process if JSON parsing was successful
      if ![not_json] {
    
        # Set basic vendor info first
        mutate {
          replace => {
            "event.idm.read_only_udm.metadata.event_type" => "SCAN_UNCATEGORIZED"
            "event.idm.read_only_udm.metadata.vendor_name" => "Entro"
            "event.idm.read_only_udm.metadata.product_name" => "Entro Security"
            "event.idm.read_only_udm.metadata.log_type" => "ENTRO_WEBHOOK"
          }
        }
    
    if [notificationType] == "Risk Status Changed" {
      mutate {
        rename => { "updatedRisk" => "riskData" }
      }
    }
    
    if [notificationType] == "Risk Created" {
      mutate {
        rename => { "createdRisk" => "riskData" }
      }
    }
        if [riskData][ruleCode] == "OVEREXPOSED" {
          drop {}
        }
    
        if [riskData][ruleCode] == "GOLD_MINE" {
          drop {}
        }
    
        # Extract basic fields directly
        mutate {
          replace => {
            "event.idm.read_only_udm.metadata.product_log_id" => "%{riskData.guid}"
            "event.idm.read_only_udm.metadata.description" => "%{riskData.name}"
            "event.idm.read_only_udm.metadata.product_event_type" => "%{riskData.type}"
          }
          on_error => "_basic_fields_failed"
        }
    
        # Convert detection time to timestamp
        date {
          match => ["riskData.detectionTime", "UNIX"]
          target => "event.idm.read_only_udm.metadata.event_timestamp"
          on_error => "_date_parsing_failed"
        }
    
        # Map principal with proper device/asset information
        mutate {
          replace => {
            "event.idm.read_only_udm.principal.hostname" => "entro-scanner"
            "event.idm.read_only_udm.principal.asset.hostname" => "entro-scanner"
            "event.idm.read_only_udm.principal.asset.asset_id" => "entro:security-scanner"
          }
        }
    
        mutate {
        replace => {
            "event.idm.read_only_udm.principal.user.userid" => "%{riskData.owner}"
            "event.idm.read_only_udm.principal.user.user_display_name" => "%{riskData.employee.name}"
        }
        on_error => "_principal_user_failed"
        }
    
        # Set fallbacks if fields were empty
        if "%{event.idm.read_only_udm.principal.user.userid}" == "" {
        mutate { replace => { "event.idm.read_only_udm.principal.user.userid" => "unknown-user" } }
        }
    
        if "%{event.idm.read_only_udm.principal.user.user_display_name}" == "" {
        mutate { replace => { "event.idm.read_only_udm.principal.user.user_display_name" => "unknown-user" } }
        }
    
        # Generic target information with proper resource details
        mutate {
          replace => {
            "event.idm.read_only_udm.target.resource.name" => "%{riskData.path}"
            "event.idm.read_only_udm.target.resource.resource_type" => "STORAGE_OBJECT"
            "event.idm.read_only_udm.target.hostname" => "%{riskData.source}"
          }
          on_error => "_target_failed"
        }
    
        # Check if path is empty and set fallback
        if "%{riskData.path}" == "" {
          mutate {
            replace => {
              "event.idm.read_only_udm.target.resource.name" => "unknown-resource"
            }
          }
        }
    
        # Check if source is empty and set fallback for target hostname
        if "%{riskData.source}" == "" {
          mutate {
            replace => {
              "event.idm.read_only_udm.target.hostname" => "unknown-source"
            }
          }
        }
    
        # Create security result object
        mutate {
          replace => {
            "_security_result.rule_id" => "%{riskData.guid}"
            "_security_result.rule_name" => "%{riskData.ruleCode}"
            "_security_result.summary" => "%{riskData.name}"
            "_security_result.description" => "%{riskData.name}"
          }
          on_error => "_security_basic_failed"
        }
    
        # Map severity dynamically
        if "%{riskData.severity}" == "LOW" {
          mutate { replace => { "_security_result.severity" => "LOW" } }
        }
    
        if "%{riskData.severity}" == "MEDIUM" {
          mutate { replace => { "_security_result.severity" => "MEDIUM" } }
        }
    
        if "%{riskData.severity}" == "HIGH" {
          mutate { replace => { "_security_result.severity" => "HIGH" } }
        }
    
        if "%{riskData.severity}" == "CRITICAL" {
          mutate { replace => { "_security_result.severity" => "CRITICAL" } }
        }
    
        # Default severity if none matched
        if "%{_security_result.severity}" == "" {
          mutate { replace => { "_security_result.severity" => "MEDIUM" } }
        }
    
        # Map category dynamically - generic approach
        if "%{riskData.category}" == "EXPOSED_SECRET" {
          mutate { replace => { "_category_item" => "DATA_EXFILTRATION" } }
        } else {
          mutate { replace => { "_category_item" => "POLICY_VIOLATION" } }
        }
    
        mutate {
          merge => {
            "_security_result.category" => "_category_item"
          }
          on_error => "_category_merge_failed"
        }
    
        # Map action dynamically based on status
        if "%{riskData.status}" == "RESOLVED" {
          mutate { replace => { "_action_item" => "BLOCK" } }
        } else {
          mutate { replace => { "_action_item" => "ALLOW" } }
        }
    
        mutate {
          merge => {
            "_security_result.action" => "_action_item"
          }
          on_error => "_action_merge_failed"
        }
    
        # Add ALL root fields as detection fields for searchability
        mutate {
          replace => {
            "_detection_field_status.key" => "status"
            "_detection_field_status.value" => "%{riskData.status}"
          }
          on_error => "_detection_status_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_status"
          }
          on_error => "_detection_status_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_name.key" => "risk_name"
            "_detection_field_name.value" => "%{riskData.name}"
          }
          on_error => "_detection_name_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_name"
          }
          on_error => "_detection_name_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_source.key" => "source"
            "_detection_field_source.value" => "%{riskData.source}"
          }
          on_error => "_detection_source_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_source"
          }
          on_error => "_detection_source_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_owner.key" => "owner"
            "_detection_field_owner.value" => "%{riskData.owner}"
          }
          on_error => "_detection_owner_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_owner"
          }
          on_error => "_detection_owner_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_severity.key" => "severity"
            "_detection_field_severity.value" => "%{riskData.severity}"
          }
          on_error => "_detection_severity_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_severity"
          }
          on_error => "_detection_severity_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_type.key" => "risk_type"
            "_detection_field_type.value" => "%{riskData.type}"
          }
          on_error => "_detection_type_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_type"
          }
          on_error => "_detection_type_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_category.key" => "category"
            "_detection_field_category.value" => "%{riskData.category}"
          }
          on_error => "_detection_category_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_category"
          }
          on_error => "_detection_category_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_rule_code.key" => "rule_code"
            "_detection_field_rule_code.value" => "%{riskData.ruleCode}"
          }
          on_error => "_detection_rule_code_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_rule_code"
          }
          on_error => "_detection_rule_code_merge_failed"
        }
    
        mutate {
          replace => {
            "_detection_field_path.key" => "path"
            "_detection_field_path.value" => "%{riskData.path}"
          }
          on_error => "_detection_path_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_path"
          }
          on_error => "_detection_path_merge_failed"
        }
    
        # Add notification type detection field
        mutate {
          replace => {
            "_detection_field_notification.key" => "notification_type"
            "_detection_field_notification.value" => "%{notificationType}"
          }
          on_error => "_detection_notification_failed"
        }
    
        mutate {
          merge => {
            "_security_result.detection_fields" => "_detection_field_notification"
          }
          on_error => "_detection_notification_merge_failed"
        }
    
        # Merge security result into the event
        mutate {
          merge => {
            "event.idm.read_only_udm.security_result" => "_security_result"
          }
          on_error => "_security_result_merge_failed"
        }
    
        # Add core additional fields
        mutate {
          replace => {
            "_additional_notification_type.key" => "notification_type"
            "_additional_notification_type.value.string_value" => "%{notificationType}"
          }
          on_error => "_notification_type_failed"
        }
    
        mutate {
          merge => {
            "event.idm.read_only_udm.additional.fields" => "_additional_notification_type"
          }
          on_error => "_notification_type_merge_failed"
        }
    
        mutate {
          replace => {
            "_additional_owner_uid.key" => "owner_uid"
            "_additional_owner_uid.value.string_value" => "%{riskData.ownerUid}"
          }
          on_error => "_owner_uid_failed"
        }
    
        mutate {
          merge => {
            "event.idm.read_only_udm.additional.fields" => "_additional_owner_uid"
          }
          on_error => "_owner_uid_merge_failed"
        }
    
        mutate {
          replace => {
            "_additional_archived.key" => "is_archived"
            "_additional_archived.value.string_value" => "%{riskData.isArchived}"
          }
          on_error => "_archived_failed"
        }
    
        mutate {
          merge => {
            "event.idm.read_only_udm.additional.fields" => "_additional_archived"
          }
          on_error => "_archived_merge_failed"
        }
    
        mutate {
          replace => {
            "_additional_creation_date.key" => "creation_date"
            "_additional_creation_date.value.string_value" => "%{riskData.creationDate}"
          }
          on_error => "_creation_date_failed"
        }
    
        mutate {
          merge => {
            "event.idm.read_only_udm.additional.fields" => "_additional_creation_date"
          }
          on_error => "_creation_date_merge_failed"
        }
    
        mutate {
          replace => {
            "_additional_modify_date.key" => "modify_date"
            "_additional_modify_date.value.string_value" => "%{riskData.modifyDate}"
          }
          on_error => "_modify_date_failed"
        }
    
        mutate {
          merge => {
            "event.idm.read_only_udm.additional.fields" => "_additional_modify_date"
          }
          on_error => "_modify_date_merge_failed"
        }
    
        mutate {
          replace => {
            "_additional_employee_status.key" => "employee_status"
            "_additional_employee_status.value.string_value" => "%{riskData.employee.status}"
          }
          on_error => "_employee_status_failed"
        }
    
        mutate {
          merge => {
            "event.idm.read_only_udm.additional.fields" => "_additional_employee_status"
          }
          on_error => "_employee_status_merge_failed"
        }
    
        # Final merge to output
        mutate {
          merge => {
            "@output" => "event"
          }
          on_error => "_final_merge_failed"
        }
      }
    }
    
  6. Click on Validate

  7. When validation passes, click on Next and Submit

  8. The following message should appear

Now you will be able to process events in the SIEM Search investigation panel