From 0e87d10260f16d0ce3c0b1c9d05c68084366646c Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Thu, 2 Jan 2025 12:53:03 +0100 Subject: [PATCH] new secops anonymization pipeline --- blueprints/secops/README.md | 5 + .../secops-anonymization-pipeline/README.md | 135 ++++++++ .../secops-anonymization-pipeline/dlp.tf | 283 ++++++++++++++++ .../images/cloud-shell-button.png | Bin 0 -> 10762 bytes .../images/diagram.png | Bin 0 -> 92564 bytes .../secops-anonymization-pipeline/main.tf | 294 +++++++++++++++++ .../secops-anonymization-pipeline/outputs.tf | 20 ++ .../source/dlp_job_template.json.tpl | 31 ++ .../source/main.py | 302 ++++++++++++++++++ .../source/requirements.txt | 25 ++ .../source/shared/__init__.py | 14 + .../source/shared/secops.py | 111 +++++++ .../source/shared/utils.py | 224 +++++++++++++ .../variables.tf | 116 +++++++ 14 files changed, 1560 insertions(+) create mode 100644 blueprints/secops/secops-anonymization-pipeline/README.md create mode 100644 blueprints/secops/secops-anonymization-pipeline/dlp.tf create mode 100644 blueprints/secops/secops-anonymization-pipeline/images/cloud-shell-button.png create mode 100644 blueprints/secops/secops-anonymization-pipeline/images/diagram.png create mode 100644 blueprints/secops/secops-anonymization-pipeline/main.tf create mode 100644 blueprints/secops/secops-anonymization-pipeline/outputs.tf create mode 100644 blueprints/secops/secops-anonymization-pipeline/source/dlp_job_template.json.tpl create mode 100644 blueprints/secops/secops-anonymization-pipeline/source/main.py create mode 100644 blueprints/secops/secops-anonymization-pipeline/source/requirements.txt create mode 100644 blueprints/secops/secops-anonymization-pipeline/source/shared/__init__.py create mode 100644 blueprints/secops/secops-anonymization-pipeline/source/shared/secops.py create mode 100644 blueprints/secops/secops-anonymization-pipeline/source/shared/utils.py create mode 100644 blueprints/secops/secops-anonymization-pipeline/variables.tf diff --git a/blueprints/secops/README.md b/blueprints/secops/README.md index 04082de5d8..93185856d8 100644 --- a/blueprints/secops/README.md +++ b/blueprints/secops/README.md @@ -8,6 +8,11 @@ This repository provides a collection of Terraform blueprints designed to automa
+## SecOps Anonymization pipeline + + This [blueprint](./secops-anonymization-pipeline/) is a modular solution for deploying a SecOps pipeline for exporting raw data from a SecOps tenant, optionally anonymize it and then import data back in a different SecOps tenant. + +
## SecOps GKE Forwarder diff --git a/blueprints/secops/secops-anonymization-pipeline/README.md b/blueprints/secops/secops-anonymization-pipeline/README.md new file mode 100644 index 0000000000..e7bbfa7447 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/README.md @@ -0,0 +1,135 @@ +# SecOps Anonymization Pipeline + +This blueprint offers a comprehensive and adaptable solution for constructing a SecOps pipeline for exporting raw data from a SecOps tenant, optionally anonymize this data and then import data back in a different SecOps tenant. The pipeline is built on top of various Google Cloud products. + +### High level architecture + +The following diagram illustrates the high-level design of the solution, which can be adapted to specific requirements via variables and/or simple terraform and Python code customizations: + +![SecOps Anonymization Pipeline](./images/diagram.png) + +The use case is a SecOps deployment composed of 2 tenants (one for production and one for development/testing). There might be the need to export production data from the prod tenant and import them back in DEV (possibly anonymizing it) for rules and/or parser development, that is why this pipeline might be convenient for speeding up the data migration process. + +### Pipeline Steps + +- **SecOps Export**: Triggered via the corresponding TRIGGER-EXPORT action. Call SecOps Export API to trigger raw logs export on a GCS bucket based on either all the log types or one o more of them for a specific time frame. By default, the export will be for the previous day, otherwise the following parameters can be specified to change the time frame: + * `EXPORT_DATE` date for the export (format %Y-%m-%d) + * `EXPORT_START_DATETIME` and `EXPORT_END_DATETIME` start and end datetime for the export (format %Y-%m-%dT%H:%M:%SZ). This is useful for verbose log source with GB/TB of raw logs ingested on a daily basis +- **Anonymize Data**: Triggered via the corresponding ANONYMIZE-DATA action. Split the exported CSV files to one or more CSV files where the size of each file is less than 60MB (which is the maximum file size supported by DLP). It also renames those files in .log for better handling by the DLP Job. It will then trigger an asynchronous DLP job to anonymize data. +- **Import Data**: Triggered via the corresponding IMPORT-DATA action. Import the exported raw logs (or anonymized ones according to the pipeline configuration) data into the target SecOps tenant leveraging the [Ingestion API](https://cloud.google.com/chronicle/docs/reference/ingestion-api). + +### Limitations + +- The pipeline can be schedule to run on a daily basis or on-demand, being all asynchronous tasks the anonymization and/or import logs should be triggered after the export is completed successfully +- This pipeline is built for migrating few logs between tenants, lack of multi-threading and limitations on the Cloud Function memory result in the function being able to process at most order of MB of raw logs data (no GB) +- Currently, SecOps export API supports 3 concurrent export requests for each tenant, due to each export request being associated to eithe all log types or a specific log type this result in no more than 3 log types exported within the same export request. + +### Deployment + +#### Step 0: Cloning the repository + +If you want to deploy from your Cloud Shell, click on the image below, sign in +if required and when the prompt appears, click on “confirm”. + +[![Open Cloudshell](./images/cloud-shell-button.png)](https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2FGoogleCloudPlatform%2Fcloud-foundation-fabric&cloudshell_workspace=blueprints%2Fthird-party-solutions%2Fwordpress%2Fcloudrun) + +Otherwise, in your console of choice: + +```bash +git clone REPO_URL +``` + +Before you deploy the architecture, you will need at least the following +information (for more precise configuration see the Variables section): + +* GCP Project ID for SecOps anonymization pipeline deployment +* SecOps tenants information: + * GCP projects of SecOps tenants + * customer ID + * deployment region for both the tenants (must be the same) + * SA credentials with export permissions on source tenant + * SA credentials with ingestion API grants on target tenant + +#### Step 2: Prepare the variables + +Once you have the required information, head back to your cloned repository. +Make sure you’re in the directory of this tutorial (where this README is in). + +Configure the Terraform variables in your `terraform.tfvars` file. +See sample TF variables in README.md file as starting point - just +copy them to a new `terraform.tfvars` file and update accordingly. +See the variables documentation below for more information. + +#### Step 3: Prepare the DLP template + +When anonymization is required (variable `skip_anonymization` is false) a Data Loss prevention API configuration is required for the corresponding DLP job. + +By default, the blueprint will provision a very basic DLP inspect and de-identify template for identifying and masking with just a sample value common PII information such as email addresses, person names, IP addresses and so on, more information available on the corresponding TF script in [dlp.tf](./dlp.tf). + +In general a more advanced configuration is required for custom info types or a better de-identification template based on multiple anonymized values for the same info type (more email addresses or IP addresses to guarantee differentiation), in that case you can build your own DLP templates and pass them to the anonymization pipeline leveraging the `dlp_config` variable. + +#### Step 4: Deploy resources + +Initialize your Terraform environment and deploy the resources: + +```shell +terraform init +terraform apply +``` + +#### Step 5: Test solution + +Test the solution triggering an export from the Cloud Scheduler page, after few hours (accoding to the size of the export) logs should be available on secops-export bucket. Please check for any issue during export using the corresponding APIs and the export ID. + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [prefix](variables.tf#L59) | Prefix used for resource names. | string | ✓ | | +| [project_id](variables.tf#L78) | Project id, references existing project if `project_create` is null. | string | ✓ | | +| [secops_config](variables.tf#L95) | SecOps config. | object({…}) | ✓ | | +| [anonymization_scheduler](variables.tf#L17) | Schedule for triggering export, anonymization and import of data. | object({…}) | | {…} | +| [cloud_function_config](variables.tf#L31) | Optional Cloud Function configuration. | object({…}) | | {} | +| [dlp_config](variables.tf#L49) | Data Loss prevention configuration. | object({…}) | | null | +| [project_create](variables.tf#L69) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | +| [regions](variables.tf#L83) | Regions: primary for all resources and secondary for clouds scheduler since the latter is available in few regions. | object({…}) | | {…} | +| [skip_anonymization](variables.tf#L112) | Whether to skip anonymization step and just import data exported from source tenant. | bool | | false | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [function_sa](outputs.tf#L17) | Chronicle Anonymization function service account. | | + +## Test + +```hcl +module "test" { + source = "./fabric/blueprints/secops/secops-anonymization-pipeline" + secops_config = { + region = "europe" + alpha_apis_region = "eu" + source_tenant = { + gcp_project = "SOURCE_PROJECT_ID" + export_sa_key_base64 = "dGVzdAo=" + } + target_tenant = { + gcp_project = "TARGET_PROJECT_ID" + customer_id = "xxx-xxxxxx-xxxxx" + ingestion_sa_key_base64 = "dGVzdAo=" + } + } + skip_anonymization = false + prefix = "pre" + project_id = "gcp-project-id" + project_create = { + billing_account_id = "12345-ABCDEF-12345" + parent = "folders/2345678901" + } + regions = { + primary = "europe-west1" + secondary = "europe-west1" + } +} +# tftest modules=8 resources=54 +``` diff --git a/blueprints/secops/secops-anonymization-pipeline/dlp.tf b/blueprints/secops/secops-anonymization-pipeline/dlp.tf new file mode 100644 index 0000000000..3366ddcda8 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/dlp.tf @@ -0,0 +1,283 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_data_loss_prevention_deidentify_template" "dlp_deidentify_template" { + count = var.dlp_config == null ? 1 : 0 + parent = "projects/${module.project.project_id}/locations/${var.regions.primary}" + description = "SecOps Anonymization pipeline deidentify template." + display_name = "sample_deidentify_config_template" + + deidentify_config { + info_type_transformations { + transformations { + info_types { + name = "PHONE_NUMBER" + } + primitive_transformation { + replace_config { + new_value { + integer_value = 3333333333 + } + } + } + } + transformations { + info_types { + name = "AGE" + } + primitive_transformation { + replace_config { + new_value { + integer_value = 10 + } + } + } + } + transformations { + info_types { + name = "EMAIL_ADDRESS" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "john.doe@fakedomain.com" + } + } + } + } + transformations { + info_types { + name = "LAST_NAME" + } + primitive_transformation { + replace_config { + new_value { + string_value = "doe" + } + } + } + } + transformations { + info_types { + name = "PERSON_NAME" + } + primitive_transformation { + replace_config { + new_value { + string_value = "john" + } + } + } + } + transformations { + info_types { + name = "DATE_OF_BIRTH" + } + primitive_transformation { + replace_config { + new_value { + date_value { + year = 1990 + month = 1 + day = 1 + } + } + } + } + } + transformations { + info_types { + name = "CREDIT_CARD_NUMBER" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "1234567812345678" + } + } + } + } + transformations { + info_types { + name = "CREDIT_CARD_TRACK_NUMBER" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "1234567812345678" + } + } + } + } + transformations { + info_types { + name = "ETHNIC_GROUP" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "None" + } + } + } + } + transformations { + info_types { + name = "GENDER" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "Gender" + } + } + } + } + transformations { + info_types { + name = "IBAN_CODE" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "2131312312312312" + } + } + } + } + transformations { + info_types { + name = "PASSPORT" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "2131312312312312" + } + } + } + } + transformations { + info_types { + name = "STREET_ADDRESS" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "street address" + } + } + } + } + transformations { + info_types { + name = "SWIFT_CODE" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "2131312312312312" + } + } + } + } + transformations { + info_types { + name = "VEHICLE_IDENTIFICATION_NUMBER" + } + + primitive_transformation { + replace_config { + new_value { + string_value = "2131312312312312" + } + } + } + } + } + } +} + +resource "google_data_loss_prevention_inspect_template" "dlp_inspect_template" { + count = var.dlp_config == null ? 1 : 0 + parent = "projects/${module.project.project_id}/locations/${var.regions.primary}" + description = "Data Loss prevention sample inspect config." + display_name = "sample_inspect_config_template" + + inspect_config { + info_types { + name = "ADVERTISING_ID" + } + info_types { + name = "AGE" + } + info_types { + name = "CREDIT_CARD_NUMBER" + } + info_types { + name = "CREDIT_CARD_TRACK_NUMBER" + } + info_types { + name = "EMAIL_ADDRESS" + } + info_types { + name = "DATE_OF_BIRTH" + } + info_types { + name = "ETHNIC_GROUP" + } + info_types { + name = "GENDER" + } + info_types { + name = "IBAN_CODE" + } + info_types { + name = "PASSPORT" + } + info_types { + name = "PERSON_NAME" + } + info_types { + name = "FIRST_NAME" + } + info_types { + name = "LAST_NAME" + } + info_types { + name = "PHONE_NUMBER" + } + info_types { + name = "STREET_ADDRESS" + } + info_types { + name = "SWIFT_CODE" + } + info_types { + name = "VEHICLE_IDENTIFICATION_NUMBER" + } + min_likelihood = "POSSIBLE" + } +} diff --git a/blueprints/secops/secops-anonymization-pipeline/images/cloud-shell-button.png b/blueprints/secops/secops-anonymization-pipeline/images/cloud-shell-button.png new file mode 100644 index 0000000000000000000000000000000000000000..21a3f3de9d130679049a1085476073e5bf54a43d GIT binary patch literal 10762 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ɰ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 a0gXM|Q|fsCc!?HpxF7L4T>1O?TmJ v if !(var.skip_anonymization && k == "anonymize-data") } + project = module.project.project_id + name = "secops_${each.key}" + description = "Trigger SecOps anonymization function." + schedule = each.value + time_zone = "Etc/UTC" + attempt_deadline = "320s" + region = var.regions.secondary + retry_config { + retry_count = 1 + } + http_target { + http_method = "POST" + uri = module.function.uri + body = base64encode(jsonencode({ + ACTION = upper(each.key) + })) + headers = { "Content-Type" : "application/json" } + oidc_token { + service_account_email = module.scheduler-sa.email + audience = module.function.uri + } + } + lifecycle { + ignore_changes = [ + http_target + ] + } +} diff --git a/blueprints/secops/secops-anonymization-pipeline/outputs.tf b/blueprints/secops/secops-anonymization-pipeline/outputs.tf new file mode 100644 index 0000000000..970e3703d5 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "function_sa" { + description = "Chronicle Anonymization function service account." + value = try(module.function.service_account_email, null) +} diff --git a/blueprints/secops/secops-anonymization-pipeline/source/dlp_job_template.json.tpl b/blueprints/secops/secops-anonymization-pipeline/source/dlp_job_template.json.tpl new file mode 100644 index 0000000000..55b47bf1c5 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/source/dlp_job_template.json.tpl @@ -0,0 +1,31 @@ +{ + "actions": [ + { + "deidentify": { + "file_types_to_transform": [ + "TEXT_FILE", + "IMAGE", + "CSV", + "TSV" + ], + "transformation_details_storage_config": {}, + "transformation_config": { + "deidentify_template": "{{ deidentify_template_id }}", + "structured_deidentify_template": "", + "image_redact_template": "" + }, + "cloud_storage_output": "gs://{{output_bucket}}/" + } + } + ], + "inspect_template_name": "{{ inspect_template_id }}", + "storage_config": { + "cloud_storage_options": { + "file_set": { + "url": "gs://{{ export_bucket }}/{{export_id}}/**" + }, + "file_types": ["TEXT_FILE", "CSV", "TSV", "EXCEL", "AVRO"], + "files_limit_percent": 100 + } + } +} diff --git a/blueprints/secops/secops-anonymization-pipeline/source/main.py b/blueprints/secops/secops-anonymization-pipeline/source/main.py new file mode 100644 index 0000000000..a969d04ff8 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/source/main.py @@ -0,0 +1,302 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import binascii +import json +import os +import click +import logging +import sys +import google.cloud.logging +from google.auth.transport.requests import AuthorizedSession +from google.oauth2 import service_account +from shared.secops import SecOpsUtils +from jinja2 import Template +from shared import utils +from google.cloud import dlp_v2 +from google.cloud import storage +from datetime import date, timedelta + +client = google.cloud.logging.Client() +client.setup_logging() + +LOGGER = logging.getLogger('secops') +logging.basicConfig( + level=logging.DEBUG if os.environ.get('DEBUG') else logging.INFO, + format='[%(levelname)-8s] - %(asctime)s - %(message)s') +logging.root.setLevel(logging.DEBUG) + +SCOPES = [ + "https://www.googleapis.com/auth/chronicle-backstory", + "https://www.googleapis.com/auth/malachite-ingestion" +] + +# Threshold value in bytes for ingesting the logs to the SecOps. +# SecOps Ingestion API allows the maximum 1MB of payload and we kept 0.5MB as a buffer. +SIZE_THRESHOLD_BYTES = 950000 + +SECOPS_REGION = os.environ.get("SECOPS_REGION") +SECOPS_ALPHA_APIS_REGION = os.environ.get("SECOPS_ALPHA_APIS_REGION") +GCP_PROJECT_ID = os.environ.get("GCP_PROJECT") +SECOPS_EXPORT_BUCKET = os.environ.get("SECOPS_EXPORT_BUCKET") +SECOPS_OUTPUT_BUCKET = os.environ.get("SECOPS_OUTPUT_BUCKET") +SECOPS_SOURCE_SA_KEY_SECRET_PATH = os.environ.get( + "SECOPS_SOURCE_SA_KEY_SECRET_PATH") +SECOPS_TARGET_SA_KEY_SECRET_PATH = os.environ.get( + "SECOPS_TARGET_SA_KEY_SECRET_PATH") +SECOPS_TARGET_CUSTOMER_ID = os.environ.get("SECOPS_TARGET_CUSTOMER_ID") + +SKIP_ANONYMIZATION = False if (os.environ.get( + "SKIP_ANONYMIZATION", "false").lower() == "false") else True +DLP_DEIDENTIFY_TEMPLATE_ID = os.environ.get("DLP_DEIDENTIFY_TEMPLATE_ID") +DLP_INSPECT_TEMPLATE_ID = os.environ.get("DLP_INSPECT_TEMPLATE_ID") +DLP_REGION = os.environ.get("DLP_REGION") + +INGESTION_API_URL = F"https://{SECOPS_REGION}-malachiteingestion-pa.googleapis.com" +URI_UNSTRUCTURED = f"{INGESTION_API_URL}/v2/unstructuredlogentries:batchCreate" + + +def import_logs(export_date): + storage_client = storage.Client() + BUCKET = SECOPS_OUTPUT_BUCKET if not SKIP_ANONYMIZATION else SECOPS_EXPORT_BUCKET + bucket = storage_client.bucket(BUCKET) + export_ids = utils.get_secops_export_folders_for_date(BUCKET, export_date) + backstory_credentials = service_account.Credentials.from_service_account_file( + SECOPS_TARGET_SA_KEY_SECRET_PATH, scopes=SCOPES) + authed_session = AuthorizedSession(backstory_credentials) + + for export_id in export_ids: + for folder in utils.list_anonymized_folders(BUCKET, export_id): + log_type = folder.split("-")[0] + + for log_file in utils.list_log_files(BUCKET, f"{export_id}/{folder}"): + blob = bucket.blob(log_file) # Directly get the blob object + with blob.open("r") as f: + cur_entries = [] + body = { + "customer_id": SECOPS_TARGET_CUSTOMER_ID, + "log_type": log_type, + "entries": cur_entries + } + size_of_empty_payload = sys.getsizeof(json.dumps(body)) + for line in f: + next_entries = cur_entries + [{"logText": line.rstrip('\n')}] + if size_of_empty_payload + sys.getsizeof( + json.dumps(next_entries)) >= SIZE_THRESHOLD_BYTES: + body["entries"] = cur_entries + LOGGER.debug(body) + LOGGER.debug(sys.getsizeof(json.dumps(body))) + response = authed_session.post(URI_UNSTRUCTURED, json=body) + LOGGER.debug(response) + cur_entries = [{"logText": line.rstrip('\n')}] + else: + cur_entries.append({"logText": line.rstrip('\n')}) + + # Send any remaining entries + if cur_entries: + body["entries"] = cur_entries + LOGGER.debug(sys.getsizeof(json.dumps(body))) + LOGGER.debug(body) + response = authed_session.post(URI_UNSTRUCTURED, json=body) + LOGGER.debug(response) + + # delete both export and anonymized buckets after ingesting logs + utils.delete_folder(BUCKET, export_id) + if not SKIP_ANONYMIZATION: + utils.delete_folder(SECOPS_EXPORT_BUCKET, export_id) + + LOGGER.info("Finished importing data.") + + +def trigger_export(export_date: str, export_start_datetime: str, + export_end_datetime: str, log_types: list): + """ + Trigger secops export using Data Export API for a specific date + :param secops_source_sa_key_secret_path: + :param secops_export_bucket: + :param secops_target_project_id: + :param log_types: + :param export_end_datetime: + :param export_start_datetime: + :param export_date: + :param date: datetime (as string) with DD-MM-YYYY format + :return: + """ + backstory_credentials = service_account.Credentials.from_service_account_file( + SECOPS_SOURCE_SA_KEY_SECRET_PATH, scopes=SCOPES) + secops_utils = SecOpsUtils(backstory_credentials) + previous_day = utils.get_previous_day(export_date) if export_date else None + + export_ids = [] + try: + if log_types is None: + export_response = secops_utils.create_data_export( + project=GCP_PROJECT_ID, export_date=previous_day, + export_start_datetime=export_start_datetime, + export_end_datetime=export_end_datetime) + LOGGER.info(export_response) + export_ids.append(export_response["dataExportId"]) + LOGGER.info( + f"Triggered export with ID: {export_response['dataExportId']}") + else: + for log_type in log_types: + export_response = secops_utils.create_data_export( + project=GCP_PROJECT_ID, export_date=previous_day, + export_start_datetime=export_start_datetime, + export_end_datetime=export_end_datetime, log_type=log_type) + LOGGER.info(export_response) + export_ids.append(export_response["dataExportId"]) + LOGGER.info( + f"Triggered export with ID: {export_response['dataExportId']}") + except Exception as e: + LOGGER.error(f"Error during export': {e}") + raise SystemExit(f'Error during secops export: {e}') + + LOGGER.info(f"Export IDs: {export_response['dataExportId']}") + return export_ids + + +def anonymize_data(export_date): + """ + Trigger DLP Job and setup secops feeds to ingest data from output bucket. + :param export_date: date for which data should be anonymized + :return: + """ + backstory_credentials = service_account.Credentials.from_service_account_file( + SECOPS_SOURCE_SA_KEY_SECRET_PATH, scopes=SCOPES) + secops_utils = SecOpsUtils(backstory_credentials) + export_ids = utils.get_secops_export_folders_for_date(SECOPS_EXPORT_BUCKET, + export_date=export_date) + + export_finished = True + for export_id in export_ids: + export = secops_utils.get_data_export(export_id=export_id) + export_state = export["dataExportStatus"]["stage"] + LOGGER.info(f"Export status: {export_state}.") + if export_state != "FINISHED_SUCCESS": + export_finished = False + + if export_finished: + for export_id in export_ids: + utils.split_and_rename_csv_to_log_files(SECOPS_EXPORT_BUCKET, export_id) + + with open("dlp_job_template.json.tpl", "r") as template_file: + content = template_file.read() + template = Template(content) + rendered_str = template.render({ + "export_bucket": SECOPS_EXPORT_BUCKET, + "output_bucket": SECOPS_OUTPUT_BUCKET, + "deidentify_template_id": DLP_DEIDENTIFY_TEMPLATE_ID, + "inspect_template_id": DLP_INSPECT_TEMPLATE_ID, + "export_id": export_id + }) + LOGGER.info(f"Filled template: {rendered_str}") + dlp_job = json.loads(rendered_str) + LOGGER.info(dlp_job) + + job_request = { + "parent": f"projects/{GCP_PROJECT_ID}/locations/{DLP_REGION}", + "inspect_job": dlp_job + } + + dlp_client = dlp_v2.DlpServiceClient( + client_options={'quota_project_id': GCP_PROJECT_ID}) + response = dlp_client.create_dlp_job(request=job_request) + LOGGER.info(response) + + else: + LOGGER.error("Export is not finished yet, please try again later.") + + LOGGER.info("Triggered all DLP jobs successfully.") + + +def main(request): + """ + Entry point for Cloud Function triggered by HTTP request. + :param request: payload of HTTP request triggering cloud function + :return: + """ + debug = os.environ.get('DEBUG') + logging.basicConfig(level=logging.INFO) + LOGGER.info('processing http payload') + try: + payload = json.loads(request.data) + except (binascii.Error, json.JSONDecodeError) as e: + raise SystemExit(f'Invalid payload: {e.args[0]}.') + if "EXPORT_DATE" in payload: + export_date = payload.get('EXPORT_DATE') + else: + export_date = date.today().strftime("%Y-%m-%d") + action = payload.get('ACTION') + export_start_datetime = payload.get('EXPORT_START_DATETIME', None) + export_end_datetime = payload.get('EXPORT_END_DATETIME', None) + log_types = payload.get('LOG_TYPES', None) + + match action: + case "TRIGGER-EXPORT": + trigger_export(export_date=export_date, + export_start_datetime=export_start_datetime, + export_end_datetime=export_end_datetime, + log_types=log_types) + case "ANONYMIZE-DATA": + anonymize_data(export_date=export_date) + case "IMPORT-DATA": + import_logs(export_date=export_date) + case _: + return "Action must be either 'TRIGGER-EXPORT', 'ANONYMIZE-DATA' or 'IMPORT-DATA'" + + return "Success." + + +@click.command() +@click.option('--export-date', '-d', required=False, type=str, + help='Date for secops export and anonymization.') +@click.option('--export-start-datetime', '-d', required=False, type=str, + help='Start datetime for secops export and anonymization.') +@click.option('--export-end-datetime', '-d', required=False, type=str, + help='End datetime for secops export and anonymization.') +@click.option('--log-type', type=str, multiple=True) +@click.option( + '--action', + type=click.Choice(['TRIGGER-EXPORT', 'ANONYMIZE-DATA', + 'IMPORT-DATA']), required=True) +@click.option('--debug', is_flag=True, default=False, + help='Turn on debug logging.') +def main_cli(export_date, export_start_datetime, export_end_datetime, + log_type: list, action: str, debug=False): + """ + CLI entry point. + :param date: date for secops export and anonymization + :param debug: whether to enable debug logs + :return: + """ + logging.basicConfig(level=logging.INFO if not debug else logging.DEBUG) + match action: + case "TRIGGER-EXPORT": + trigger_export(export_date=export_date, + export_start_datetime=export_start_datetime, + export_end_datetime=export_end_datetime, + log_types=log_type) + case "ANONYMIZE-DATA": + anonymize_data(export_date=export_date) + case "IMPORT-DATA": + import_logs(export_date=export_date) + case _: + return "Action must be either 'TRIGGER-EXPORT', 'ANONYMIZE-DATA' or 'IMPORT-DATA'" + + return "Success." + + +if __name__ == '__main__': + main_cli() diff --git a/blueprints/secops/secops-anonymization-pipeline/source/requirements.txt b/blueprints/secops/secops-anonymization-pipeline/source/requirements.txt new file mode 100644 index 0000000000..820bd38188 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/source/requirements.txt @@ -0,0 +1,25 @@ +# coding=utf-8 +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +google-cloud-pubsub +requests==2.27.1 +jwt==1.3.1 +google-auth +google-cloud-secret-manager +jinja2 +google-cloud-storage +click==8.1.3 +google-cloud-dlp +google-cloud-logging diff --git a/blueprints/secops/secops-anonymization-pipeline/source/shared/__init__.py b/blueprints/secops/secops-anonymization-pipeline/source/shared/__init__.py new file mode 100644 index 0000000000..9e79de8bd9 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/source/shared/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Shared module for SecOps Anonymization Pipeline.""" diff --git a/blueprints/secops/secops-anonymization-pipeline/source/shared/secops.py b/blueprints/secops/secops-anonymization-pipeline/source/shared/secops.py new file mode 100644 index 0000000000..21df22859f --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/source/shared/secops.py @@ -0,0 +1,111 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import google.auth +import logging +import requests +import os +from . import utils +from google.auth.transport.requests import AuthorizedSession +"""SecOps utility functions.""" + +LOGGER = logging.getLogger("secops") +SECOPS_REGION = os.environ.get("SECOPS_REGION") +SECOPS_EXPORT_BUCKET = os.environ.get("SECOPS_EXPORT_BUCKET") +SECOPS_OUTPUT_BUCKET = os.environ.get("SECOPS_OUTPUT_BUCKET") + + +class SecOpsUtils: + + def __init__(self, credentials=None): + self.BACKSTORY_API_URL = f"https://{SECOPS_REGION}-backstory.googleapis.com/v1/tools/dataexport" + self.INGESTION_API_URL = F"https://{SECOPS_REGION}-malachiteingestion-pa.googleapis.com" + self.HTTP = AuthorizedSession(credentials=credentials if credentials + is not None else google.auth.default()[0]) + + def create_data_export(self, project, export_date, export_start_datetime, + export_end_datetime, log_type: str = None): + """ + Trigger Chronicle data export for the given date and log types. + + :param export_start_datetime: + :param export_date: + :param project: + :param session: auth session for API call + :param date: date for which data will be exported + :return: Chronicle Data export response. + """ + if export_start_datetime and export_end_datetime: + start_time, end_time = export_start_datetime, export_end_datetime + else: + start_time, end_time = utils.format_date_time_range( + date_input=export_date) + gcs_bucket = f"projects/{project}/buckets/{SECOPS_EXPORT_BUCKET}" + + body = { + "startTime": start_time, + "endTime": end_time, + "logType": "ALL_TYPES" if log_type is None else log_type, + "gcsBucket": gcs_bucket, + } + + response = self.HTTP.post(self.BACKSTORY_API_URL, json=body) + response.raise_for_status() + print(f"Data export created successfully.") + return response.json() + + def get_data_export(self, export_id: str) -> str: + """ + Get Chronicle data export information. + + :param export_id: ID of Chronicle export to get information from + :return: Data Export status + :raises requests.exceptions.HTTPError: If the API request fails. + """ + try: + response = self.HTTP.get(f"{self.BACKSTORY_API_URL}/{export_id}") + response.raise_for_status( + ) # Raise HTTPError for bad responses (4xx or 5xx) + print( + f"Data export for '{export_id}' retrieved, content is {response.json()}" + ) + return response.json() + except requests.exceptions.HTTPError as e: + print(f"Error fetching data export '{export_id}': {e}") + # You can choose to handle the error in a more specific way here, + # like retrying the request, logging the error, or raising a custom exception. + raise # Re-raise the exception to be handled by the caller + + def list_log_types(self, date): + start_date, end_date = utils.format_date_time_range(date) + params = { + "startTime": start_date, + "endTime": end_date, + } + response = self.HTTP.get(f"{self.BACKSTORY_API_URL}/listavailablelogtypes") + response.raise_for_status() + if response.status_code == 200: + logging.info(f"Log types for date: {date} is {response.json()}") + log_types = response.json()["availableLogTypes"] + else: + error_message = response.json().get("error", + {}).get("message", "Unknown error") + status_code = response.status_code + logging.error( + f"Error listing log types on {date} (Status code: {status_code}) Error message: {error_message}" + ) + raise Exception("Error while listing log types.") + + return log_types diff --git a/blueprints/secops/secops-anonymization-pipeline/source/shared/utils.py b/blueprints/secops/secops-anonymization-pipeline/source/shared/utils.py new file mode 100644 index 0000000000..bdc93dabd5 --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/source/shared/utils.py @@ -0,0 +1,224 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import logging +import math +import csv +from google.cloud import secretmanager, storage, exceptions +from datetime import datetime, timedelta, timezone, time + +LOGGER = logging.getLogger('secops') +"""Utility functions required for ingestion scripts.""" +MAX_FILE_SIZE = 61440000 # Max size supported by DLP + + +def get_value_from_secret_manager(resource_path: str) -> str: + """Retrieve the value of the secret from the Google Cloud Secret Manager. + + Args: + resource_path (str): Path of the secret with version included. Ex.: + "projects//secrets//versions/1", + "projects//secrets//versions/latest" + + Returns: + str: Payload for secret. + """ + client = secretmanager.SecretManagerServiceClient() + response = client.access_secret_version(name=resource_path) + return response.payload.data.decode("UTF-8") + + +def format_date_time_range(date_input): + """ + Creates datetime objects for the beginning and end of the input date + and formats them. + + Args: + date_input: A string representing the date (e.g., "2024-06-10"). + + Returns: + A tuple containing two formatted strings: + - Start of day: "YYYY-MM-DDTHH:MM:SSZ" + - End of day: "YYYY-MM-DDTHH:MM:SSZ" + """ + date_obj = datetime.strptime(date_input, "%Y-%m-%d") + + start_of_day = datetime.combine(date_obj.date(), time.min, + tzinfo=timezone.utc) + end_of_day = start_of_day + timedelta(days=1, seconds=-1) + + # Format both datetime objects + formatted_start = start_of_day.strftime("%Y-%m-%dT%H:%M:%SZ") + formatted_end = end_of_day.strftime("%Y-%m-%dT%H:%M:%SZ") + + return formatted_start, formatted_end + + +def get_previous_day(date_input): + date_obj = datetime.strptime(date_input, "%Y-%m-%d") + previous_day = date_obj + timedelta(days=-1) + return previous_day.strftime("%Y-%m-%d") + + +def list_anonymized_folders(bucket_name, folder_name): + """Lists all folders (prefixes) within a specified folder in a GCS bucket. + + Args: + bucket_name: Name of the GCS bucket. + folder_name: Name of the folder (prefix) to search within. + + Returns: + A list of folder names (prefixes) found. + """ + folders = [] + storage_client = storage.Client() + for blob in storage_client.list_blobs(bucket_name, prefix=f"{folder_name}/"): + folder_name = blob.name.split('/')[1] + if not folder_name in folders: + folders.append(folder_name) + + return folders + + +def delete_folder(bucket_name, folder_name): + """Deletes a folder from a Google Cloud Storage bucket. + + Args: + bucket_name: The name of the bucket. + folder_name: The name of the folder to delete. + """ + + storage_client = storage.Client() + bucket = storage_client.bucket(bucket_name) + + # List all blobs with the given prefix (folder name) + blobs = list(bucket.list_blobs(prefix=folder_name)) + + # Delete the blobs in parallel + bucket.delete_blobs(blobs) + + print(f"Folder {folder_name} deleted from bucket {bucket_name}") + + +def list_log_files(bucket_name, folder_name): + """Lists all folders (prefixes) within a specified folder in a GCS bucket. + + Args: + bucket_name: Name of the GCS bucket. + folder_name: Name of the folder (prefix) to search within. + + Returns: + A list of folder names (prefixes) found. + """ + + storage_client = storage.Client() + csv_files = [] + for blob in storage_client.list_blobs(bucket_name, prefix=f"{folder_name}/"): + if blob.name.endswith(".log") or blob.name.endswith(".csv"): + csv_files.append(blob.name) + + return csv_files + + +def split_csv(bucket_name, blob_name, file_size): + """Splits a CSV file into smaller chunks and uploads them back to the bucket. + + Args: + bucket_name: The name of the GCS bucket. + blob_name: The name of the CSV blob in the bucket. + max_file_size: The maximum size of each chunk in bytes. + """ + storage_client = storage.Client() + bucket = storage_client.bucket(bucket_name) + blob = bucket.blob(blob_name) + + # Download the blob to a local file + temp_file = '/tmp/temp.csv' + blob.download_to_filename(temp_file) + + file = open(temp_file, encoding="utf8") + numline = sum(1 for row in csv.reader(file)) + + # Read the CSV file in chunks + chunk_number = math.ceil(numline * MAX_FILE_SIZE / file_size) + index = 0 + lines = [] + with open(temp_file, 'r', encoding="utf8") as f_in: + reader = csv.reader(f_in, delimiter='\n') + for line in reader: + lines.append(line[0] + "\n") + if len(lines) == chunk_number: + chunk_filename = f'{blob_name.split(".")[0]}_{index}.log' + chunk_path = f'/tmp/temp-{index}.csv' + with open(chunk_path, 'w') as fout: + fout.writelines(lines) + chunk_blob = bucket.blob(f'{chunk_filename}') + chunk_blob.upload_from_filename(chunk_path) + print(f'Uploaded {chunk_filename} to {bucket_name}') + os.remove(chunk_path) # Remove the local chunk file + index += 1 + lines = [] + + chunk_filename = f'{blob_name.split(".")[0]}_{index}.log' + chunk_path = f'/tmp/temp-{index}.csv' + with open(chunk_path, 'w') as fout: + fout.writelines(lines) + chunk_blob = bucket.blob(f'{chunk_filename}') + chunk_blob.upload_from_filename(chunk_path) + print(f'Uploaded {chunk_filename} to {bucket_name}') + os.remove(chunk_path) # Remove the local chunk file + index += 1 + lines = [] + + # Remove the temporary file + os.remove(temp_file) + + # remove old log file + blob = bucket.blob(blob_name) + blob.delete() + + +def split_and_rename_csv_to_log_files(bucket_name, folder_name): + """Renames all .csv files to .log files within a GCS bucket folder (and subfolders). + + Args: + bucket_name (str): Name of the GCS bucket. + folder_prefix (str): Prefix of the folder within the bucket to process. + """ + + storage_client = storage.Client() + bucket = storage_client.bucket(bucket_name) + + blobs = storage_client.list_blobs(bucket, prefix=f"{folder_name}/") + for blob in blobs: + if blob.name.endswith(".csv") and blob.size >= MAX_FILE_SIZE: + split_csv(bucket_name, blob.name, blob.size) + elif blob.name.endswith(".csv"): + new_name = blob.name.replace(".csv", ".log") + bucket.rename_blob(blob, new_name) + + +def get_secops_export_folders_for_date(bucket_name, export_date): + storage_client = storage.Client() + export_ids = [] + + for blob in storage_client.list_blobs(bucket_name): + if blob.time_created.strftime( + "%Y-%m-%d") == export_date and blob.name.split( + '/')[0] not in export_ids: + export_ids.append(blob.name.split('/')[0]) + + return export_ids diff --git a/blueprints/secops/secops-anonymization-pipeline/variables.tf b/blueprints/secops/secops-anonymization-pipeline/variables.tf new file mode 100644 index 0000000000..f54d57276b --- /dev/null +++ b/blueprints/secops/secops-anonymization-pipeline/variables.tf @@ -0,0 +1,116 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "anonymization_scheduler" { + description = "Schedule for triggering export, anonymization and import of data." + type = object({ + trigger-export = string + anonymize-data = string + import-data = string + }) + default = { + trigger-export = "0 8 29 2 *" + anonymize-data = "0 12 29 2 *" + import-data = "0 13 29 2 *" + } +} + +variable "cloud_function_config" { + description = "Optional Cloud Function configuration." + type = object({ + build_worker_pool_id = optional(string) + build_sa = optional(string) + debug = optional(bool, false) + cpu = optional(number, 1) + memory_mb = optional(number, 2048) + timeout_seconds = optional(number, 3600) + vpc_connector = optional(object({ + name = string + egress_settings = optional(string, "ALL_TRAFFIC") + })) + }) + default = {} + nullable = false +} + +variable "dlp_config" { + description = "Data Loss prevention configuration." + type = object({ + region = string + deidentify_template_id = string + inspect_template_id = string + }) + default = null +} + +variable "prefix" { + description = "Prefix used for resource names." + type = string + nullable = false + validation { + condition = var.prefix != "" + error_message = "Prefix cannot be empty." + } +} + +variable "project_create" { + description = "Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format." + type = object({ + billing_account_id = string + parent = string + }) + default = null +} + +variable "project_id" { + description = "Project id, references existing project if `project_create` is null." + type = string +} + +variable "regions" { + description = "Regions: primary for all resources and secondary for clouds scheduler since the latter is available in few regions." + type = object({ + primary = string + secondary = string + }) + default = { + primary = "europe-west1" + secondary = "europe-west1" + } +} + +variable "secops_config" { + description = "SecOps config." + type = object({ + region = string + alpha_apis_region = string + source_tenant = object({ + gcp_project = string + export_sa_key_base64 = string + }) + target_tenant = object({ + gcp_project = string + customer_id = string + ingestion_sa_key_base64 = string + }) + }) +} + +variable "skip_anonymization" { + description = "Whether to skip anonymization step and just import data exported from source tenant." + type = bool + default = false +}