From c5e54065c782bb9e40dcf0bb2ba1a52be64f59f2 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Thu, 5 Dec 2024 12:27:06 +0000 Subject: [PATCH] Add Default API Alerts --- modules/project/README.md | 39 ++- modules/project/api_metrics_alerts.tf | 361 ++++++++++++++++++++++++++ modules/project/variables.tf | 11 + 3 files changed, 408 insertions(+), 3 deletions(-) create mode 100644 modules/project/api_metrics_alerts.tf diff --git a/modules/project/README.md b/modules/project/README.md index 62bb6fea6d..cee73e2bd1 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -29,9 +29,7 @@ This module implements the creation and management of one GCP project including - [VPC Service Controls](#vpc-service-controls) - [Project Related Outputs](#project-related-outputs) - [Managing project related configuration without creating it](#managing-project-related-configuration-without-creating-it) -- [Files](#files) -- [Variables](#variables) -- [Outputs](#outputs) +- [tftest inventory=data.yaml e2e](#tftest-inventorydatayaml-e2e) ## Basic Project Creation @@ -1357,6 +1355,38 @@ module "bucket" { parent = var.project_id id = "${var.prefix}-bucket" } + +## API Alerts +There are events within Google Cloud that should be monitored and alerted on to ensure that you are aware of any potential security issues. +These actions are typically seen in cases of security breaches, or potential security breaches, although they can be genuine actions that are not security related, but are still important to monitor. +These events are typically +- Owner Role Assignment/Changes +- Audit Logging Configuration Changes +- Custom Roles Creation/editing/deletion +- VPC Network Firewall Rule Changes +- VPC Network Route Changes +- VPC Network Changes +- Cloud Storage IAM Permission Changes +- SQL Instances Configuration Changes +Although you may not use the services listed above, such as SQL, it is still important to monitor these events for compliance purposes +To enable these alerts by default on all projects created, it is recommended to default the variable `enable_default_api_alerts` within `variables.tf` to true, +You will also need to set the `default_api_alerts_email` variable to the email address that will receive these alerts +You can alternatively enable these alerts on a per-project basis by setting the variable `enable_api_alerts` to true on the module, along with the `default_api_alerts_email` variable +```terraform +module "project" { + source = "./fabric/modules/project" + billing_account = var.billing_account_id + name = "project" + parent = var.folder_id + prefix = var.prefix + services = [ + "stackdriver.googleapis.com" + ] + enable_api_alerts = true + default_api_alerts_email = "monitoring@company.com" +} +``` + # tftest inventory=data.yaml e2e ``` @@ -1366,6 +1396,7 @@ module "bucket" { | name | description | resources | |---|---|---| +| [api_metrics_alerts.tf](./api_metrics_alerts.tf) | None | google_logging_metric · google_monitoring_alert_policy · google_monitoring_notification_channel | | [cmek.tf](./cmek.tf) | Service Agent IAM Bindings for CMEK | google_kms_crypto_key_iam_member | | [iam.tf](./iam.tf) | IAM bindings. | google_project_iam_binding · google_project_iam_custom_role · google_project_iam_member | | [logging.tf](./logging.tf) | Log sinks and supporting resources. | google_bigquery_dataset_iam_member · google_logging_project_exclusion · google_logging_project_sink · google_project_iam_audit_config · google_project_iam_member · google_pubsub_topic_iam_member · google_storage_bucket_iam_member | @@ -1393,9 +1424,11 @@ module "bucket" { | [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | | [contacts](variables.tf#L36) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | | [custom_roles](variables.tf#L43) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| [default_api_alerts_email](variables.tf#L323) | Email address to receive default API alerts | string | | null | | [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | | [deletion_policy](variables.tf#L64) | Deletion policy setting for this project. | string | | "DELETE" | | [descriptive_name](variables.tf#L75) | Name of the project name. Used for project name instead of `name` variable. | string | | null | +| [enable_default_api_alerts](variables.tf#L318) | Enable default API alerts for the project, when API alerts are required for compliance reasons | bool | | false | | [factories_config](variables.tf#L81) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | diff --git a/modules/project/api_metrics_alerts.tf b/modules/project/api_metrics_alerts.tf new file mode 100644 index 0000000000..fcc205a69f --- /dev/null +++ b/modules/project/api_metrics_alerts.tf @@ -0,0 +1,361 @@ +resource "google_monitoring_notification_channel" "email" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + display_name = "Default Email Notification" + type = "email" + labels = { + email_address = var.default_api_alerts_email + } +} + +# +# Route Changes Metric and Policy +# +resource "google_logging_metric" "route_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + name = "network-route-config-changes" + description = "Monitor VPC network route configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "route_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "Network Route Changes" + conditions { + display_name = "Network Route Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "resource.type = \"global\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.route_changes[count.index].name}\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# Firewall Changes Metric and Policy +# +resource "google_logging_metric" "firewall_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + name = "network-firewall-config-changes" + description = "Monitor VPC network firewall configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "firewall_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "VPC Network Firewalls Changes" + conditions { + display_name = "VPC Network Firewalls Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "resource.type = \"global\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.firewall_changes[count.index].name}\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# VPC Changes Metric and Policy +# +resource "google_logging_metric" "vpc_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + name = "vpc-network-config-changes" + description = "Monitor VPC network configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "vpc_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "VPC Network Changes" + conditions { + display_name = "VPC Network Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "resource.type = \"global\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.vpc_changes[count.index].name}\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# CloudSQL Changes +# +resource "google_logging_metric" "cloudsql_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + name = "cloudsql-changes" + description = "Monitor Cloud SQL configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "cloudsql_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "CloudSQL Changes" + conditions { + display_name = "CloudSQL Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "metric.type = \"logging.googleapis.com/user/${google_logging_metric.cloudsql_changes[count.index].name}\" AND resource.type=\"global\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# Cloud Storage IAM Changes +# +resource "google_logging_metric" "cloudstorage_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + name = "cloudstorage-changes" + description = "Monitor Cloud Storage IAM configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "cloudstorage_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "CloudStorage IAM Changes" + conditions { + display_name = "CloudStorage IAM Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "resource.type = \"gcs_bucket\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.cloudstorage_changes[count.index].name}\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# Custom Role IAM Changes +# +resource "google_logging_metric" "customrole_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + name = "customrole-changes" + description = "Monitor IAM Custom Role configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "customrole_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "IAM Custom Role Changes" + conditions { + display_name = "IAM Custom Role Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "metric.type = \"logging.googleapis.com/user/${google_logging_metric.customrole_changes[count.index].name}\" AND resource.type=\"global\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# Audit Configuration Changes +# +resource "google_logging_metric" "audit_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + name = "audit-changes" + description = "Monitor Audit configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "audit_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "Audit Configuration Changes" + conditions { + display_name = "Audit Configuration Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "metric.type = \"logging.googleapis.com/user/${google_logging_metric.audit_changes[count.index].name}\" AND resource.type=\"global\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_MEAN" + cross_series_reducer = "REDUCE_COUNT" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} + +# +# IAM Owner Configuration Changes +# +resource "google_logging_metric" "owner_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + filter = "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + name = "iam-owner-changes" + description = "Monitor IAM Owner configuration changes inside GCP projects" + metric_descriptor { + metric_kind = "DELTA" + value_type = "INT64" + } +} + +resource "google_monitoring_alert_policy" "owner_changes" { + count = var.enable_default_api_alerts ? 1 : 0 + project = local.project.project_id + combiner = "OR" + display_name = "Owner IAM Configuration Changes" + conditions { + display_name = "Owner IAM Configuration Changed" + condition_threshold { + comparison = "COMPARISON_GT" + duration = "0s" + filter = "metric.type = \"logging.googleapis.com/user/${google_logging_metric.owner_changes[count.index].name}\" AND resource.type=\"global\"" + trigger { + count = 1 + } + aggregations { + per_series_aligner = "ALIGN_DELTA" + cross_series_reducer = "REDUCE_SUM" + alignment_period = "600s" + } + } + } + notification_channels = [ + google_monitoring_notification_channel.email[count.index].id, + ] + alert_strategy { + auto_close = "604800s" + } +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 88f59763a0..d2baeee541 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -314,3 +314,14 @@ variable "vpc_sc" { }) default = null } + +variable "enable_default_api_alerts" { + description = "Enable default API alerts for the project, when API alerts are required for compliance reasons" + type = bool + default = false +} +variable "default_api_alerts_email" { + description = "Email address to receive default API alerts" + type = string + default = null +}