From 6c9491c984daccce5d90f0b730083b2d70a5b9d2 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 10:23:32 +0200 Subject: [PATCH 01/16] new secops gke forwarder --- blueprints/README.md | 3 +- blueprints/secops/README.md | 9 + .../secops/secops-gke-forwarder/README.md | 201 +++++++++++++ .../images/cloud-shell-button.png | Bin 0 -> 10762 bytes .../secops-gke-forwarder/images/diagram.png | Bin 0 -> 73782 bytes .../secops/secops-gke-forwarder/main.tf | 103 +++++++ .../secops/secops-gke-forwarder/outputs.tf | 19 ++ .../data/default-config.yaml.tpl | 174 +++++++++++ .../secops-forwarder-deployment/main.tf | 283 ++++++++++++++++++ .../manifests/service-attachment.yaml | 13 + .../secops-forwarder-deployment/outputs.tf | 20 ++ .../secops-forwarder-deployment/ssl.tf | 109 +++++++ .../secops-forwarder-deployment/variables.tf | 48 +++ .../secops-forwarder-deployment/versions.tf | 27 ++ .../secops/secops-gke-forwarder/variables.tf | 87 ++++++ 15 files changed, 1095 insertions(+), 1 deletion(-) create mode 100644 blueprints/secops/README.md create mode 100644 blueprints/secops/secops-gke-forwarder/README.md create mode 100644 blueprints/secops/secops-gke-forwarder/images/cloud-shell-button.png create mode 100644 blueprints/secops/secops-gke-forwarder/images/diagram.png create mode 100644 blueprints/secops/secops-gke-forwarder/main.tf create mode 100644 blueprints/secops/secops-gke-forwarder/outputs.tf create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/data/default-config.yaml.tpl create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/main.tf create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/outputs.tf create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/ssl.tf create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/variables.tf create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf create mode 100644 blueprints/secops/secops-gke-forwarder/variables.tf diff --git a/blueprints/README.md b/blueprints/README.md index 3fbe689817..b3d8d423c1 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -1,6 +1,6 @@ # Terraform end-to-end blueprints for Google Cloud -This section provides **[networking blueprints](./networking/)** that implement core patterns or features, **[data solutions blueprints](./data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./cloud-operations/)** that leverage specific products to meet specific operational needs, **[GKE](./gke/)** and **[Serverless](./serverless/)** blueprints, and **[factories](./factories/)** that implement resource factories for the repetitive creation of specific resources. +This section provides **[networking blueprints](./networking/)** that implement core patterns or features, **[data solutions blueprints](./data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./cloud-operations/)** that leverage specific products to meet specific operational needs, **[GKE](./gke/)**, **[SecOps](./secops/)** security blueprints, **[Serverless](./serverless/)** blueprints, and **[factories](./factories/)** that implement resource factories for the repetitive creation of specific resources. Currently available blueprints: @@ -10,6 +10,7 @@ Currently available blueprints: - **factories** - [Fabric resource factories](./factories) - **GKE** - [Binary Authorization Pipeline Blueprint](./gke/binauthz), [Storage API](./gke/binauthz/image), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api), [GKE Multitenant Blueprint](./gke/multitenant-fleet), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [GKE Autopilot](./gke/autopilot) - **networking** - [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [HA VPN over Interconnect](./networking/ha-vpn-over-interconnect/), [GLB and multi-regional daisy-chaining through hybrid NEGs](./networking/glb-hybrid-neg-internal), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), On-prem DNS and Google Private Access, [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke), [VPC Connectivity Lab](./networking/vpc-connectivity-lab/) +- **SecOps** - [SecOps GKE Forwarder](./secops/secops-gke-forwarder) - **serverless** - [Cloud Run series](./serverless/cloud-run-explore) - **third party solutions** - [OpenShift on GCP user-provisioned infrastructure](./third-party-solutions/openshift), [Wordpress deployment on Cloud Run](./third-party-solutions/wordpress/cloudrun) diff --git a/blueprints/secops/README.md b/blueprints/secops/README.md new file mode 100644 index 0000000000..0d42455f68 --- /dev/null +++ b/blueprints/secops/README.md @@ -0,0 +1,9 @@ +# Operations blueprints + +This repository provides a collection of Terraform blueprints designed to automate the implementation of custom integrations, agents and configurations for Google Cloud Security and Operations SecOps (aka Chronicle). + +## SecOps GKE Forwarder + + This [blueprint](./secops-gke-forwarder/) is a modular and scalable solution for setting up a SecOps forwarder on Google Kubernetes Engine (GKE). This forwarder is designed to handle multi-tenant data ingestion, ensuring secure and efficient log forwarding to your SecOps SIEM instances. + +
diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md new file mode 100644 index 0000000000..41a30a099e --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -0,0 +1,201 @@ +# SecOps GKE Forwarder + +This Terraform repository provides a modular and scalable solution for setting up a SecOps forwarder on Google Kubernetes Engine (GKE). This forwarder is designed to handle multi-tenant data ingestion, ensuring secure and efficient log forwarding to your Chronicle instance. + +### High level architecture + +The following diagram illustrates the high-level design of created resources, which can be adapted to specific requirements via variables: + +![Chronicle Forwarder](./images/diagram.png) + +### Key Features + +- **Automated GKE Cluster Creation**: Streamlines the provisioning of a dedicated Kubernetes cluster for the forwarder. +- **Scalable Forwarder Deployment**: Deploys the Chronicle forwarder as a Kubernetes Deployment, allowing for easy scaling to accommodate varying log volumes. +- **Multi-tenant Support**: Enables the forwarder to ingest log data from multiple sources or tenants, maintaining clear separation within Chronicle. +- **Modular Configuration**: Provides flexible Terraform modules to customize network settings, resource allocation, and tenant-specific configurations. + +### 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): + +* The project ID + +The VPC host project, VPC and subnets should already exist and the following networking requirements are satisfied: +- configured PSA for Cloud SQL on the VPC +- subnets configured with PGA and Cloud NAT for internet access +- Inbound firewall rule for IAP on port 22 +- Inbound firewall rule for TCP ports 80, 443, 2222 from proxy subnet CIDR (gitlab) + +#### 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 [terraform.tfvars.sample](terraform.tfvars.sample) as starting point - just +copy it to `terraform.tfvars` and edit the latter. See the variables +documentation below. + +#### Step 3: Prepare the providers in the root module + +Setup terraform providers in the root module to deal with kubernetes resources as follows: + +```terraform +data "google_client_config" "identity" { + count = module.chronicle-forwarder.fleet_host != null ? 1 : 0 +} + +provider "kubernetes" { + host = module.chronicle-forwarder.fleet_host + token = try(data.google_client_config.identity.0.access_token, null) +} + +provider "kubectl" { + host = module.chronicle-forwarder.fleet_host + token = try(data.google_client_config.identity.0.access_token, null) +} +``` + +#### Step 4: Deploy resources + +Initialize your Terraform environment and deploy the resources: + +```shell +terraform init +terraform apply +``` + +Get kubeconfig to connect to the cluster using the command below: + +```shell +gcloud container fleet memberships get-credentials CLUSTER_NAME --project PROJECT +``` + +Then running the command `kubectl get pods` you should receive the following message: + +``` +"No resources found in default namespace." +``` + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [network_config](variables.tf#L79) | Shared VPC network configurations to use for Gitlab Runner VM. | object({…}) | ✓ | | +| [prefix](variables.tf#L28) | Prefix used for resource names. | string | ✓ | | +| [project_id](variables.tf#L47) | Project id, references existing project if `project_create` is null. | string | ✓ | | +| [region](variables.tf#L52) | GCP region. | string | ✓ | | +| [chronicle_forwarder](variables.tf#L17) | Chronicle GKE forwarder configuration. | object({…}) | | {} | +| [project_create](variables.tf#L38) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | +| [tenants](variables.tf#L57) | Chronicle forwarders tenants config. | map(object({…})) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [fleet_host](outputs.tf#L17) | | | + +## Test + +```hcl +module "test" { + source = "./fabric/blueprints/secops/secops-gke-forwarder" + project_id = "tmp-prod-net-landing-0" + region = "europe-west8" + network_config = { + host_project = "prod-net-landing-0" + network_self_link = "https://www.googleapis.com/compute/v1/projects/prod-net-landing-0/global/networks/prod-landing-0" + subnet_self_link = "https://www.googleapis.com/compute/v1/projects/prod-net-landing-0/regions/europe-west1/subnetworks/gke" + ip_range_gke_master = "192.168.0.0/28" + } + prefix = "tmp" + tenants = { + tenant-1 = { + chronicle_forwarder_image = "cf_production_stable" + chronicle_region = "europe" + tenant_id = "tenant-1" + namespace = "ten-1" + forwarder_config = { + config_file_content = file("data/config.yaml") + } + } + tenant-2 = { + chronicle_forwarder_image = "cf_production_stable" + chronicle_region = "europe" + tenant_id = "tenant-2" + namespace = "tenant-2" + forwarder_config = { + secret_key = file("data/secret_key.json") + customer_id = "XXXXXXX-XXXX-XXXX-XXXX-XXXXXX" + collector_id = "XXXXXXX-XXXX-XXXX-XXXX-XXXXXX" + } + } + } +} +# tftest modules=5 resources=33 files=credentials,config +``` + +``` +# tftest-file id=credentials path=data/secret_key.json +{ +"type": "service_account", +"project_id": "xxxx", +"private_key_id": "xxxxxxxxxxxxxx", +"private_key": "-----BEGIN PRIVATE KEY-----\nsdcCDSCsLxhfQIOwdvzCn5wcwJ7xVA=\n-----END PRIVATE KEY-----\n", +"client_email": "sample@sample.iam.gserviceaccount.com", +"client_id": "ASDCVSACSA", +"auth_uri": "https://accounts.google.com/o/oauth2/auth", +"token_uri": "https://oauth2.googleapis.com/token", +"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", +"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/sample.iam.gserviceaccount.com", +"universe_domain": "googleapis.com" +} +``` + +``` +# tftest-file id=config path=data/config.yaml +output: + url: malachiteingestion-pa.googleapis.com:443 + identity: + identity: + collector_id: COLLECTOR_ID \ + customer_id: CUSTOMER_ID \ + +collectors: + - syslog: + common: + enabled: true + data_type: "WINDOWS_DHCP" + data_hint: + batch_n_seconds: 10 + batch_n_bytes: 1048576 + tcp_address: 0.0.0.0:10514 + udp_address: 0.0.0.0:10514 + connection_timeout_sec: 60 + tcp_buffer_size: 524288 + - syslog: + common: + enabled: true + data_type: "WINDOWS_DNS" + data_hint: + batch_n_seconds: 10 + batch_n_bytes: 1048576 + tcp_address: 0.0.0.0:10515 + connection_timeout_sec: 60 + tcp_buffer_size: 524288 +``` \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/images/cloud-shell-button.png b/blueprints/secops/secops-gke-forwarder/images/cloud-shell-button.png new file mode 100644 index 0000000000000000000000000000000000000000..21a3f3de9d130679049a1085476073e5bf54a43d GIT binary patch literal 10762 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

UM_f#(M#9 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

L| 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 + coalesce(value.forwarder_config.config_file_content, try(templatefile("${path.module}/data/default-config.yaml.tpl", { + chronicle_url = "${value.chronicle_region}-malachiteingestion-pa.googleapis.com:443", + customer_id = value.forwarder_config.customer_id + collector_id = value.forwarder_config.collector_id + secret_key = value.forwarder_config.secret_key + tls_required = value.forwarder_config.tls_config.required + }), null)) + } + deployment_names = { + for key, value in var.tenants : key => "cfps-${value.tenant_id}" + } + tenants_exposing_service_attachments = { + for key, value in var.tenants : key => value if value.network_config.expose_service_attachment + } + tenants_exposing_udp_service_attachments = { + for key, value in var.tenants : key => value if value.network_config.expose_service_attachment && value.forwarder_config.tls_config == null + } + tenants_chronicle_forwarder_lb_ports = { + for k, v in var.tenants : k => coalesce(v.network_config.load_balancer_ports, local._chronicle_forwarder_default_lb_ports) + } + tenants_secret_tls = { + for k, v in var.tenants : k => { + cer = try(tls_locally_signed_cert.forwarder_server_singed_cert[k].cert_pem, v.forwarder_config.tls_config.cert_pub) + key = try(tls_private_key.forwarder_server_key[k].private_key_pem, v.forwarder_config.tls_config.cert_key) + } if v.forwarder_config.tls_config.required + } +} + +resource "kubernetes_namespace" "namespace" { + for_each = var.tenants + metadata { + name = each.value.namespace + } +} + +resource "kubernetes_service" "service_tcp" { + for_each = var.tenants + metadata { + name = "cfps-${each.value.tenant_id}-tcp" + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + annotations = { + "networking.gke.io/load-balancer-type" = "Internal" + } + } + spec { + selector = { + app = local.deployment_names[each.key] + } + type = "LoadBalancer" + port { + name = "hc" + protocol = "TCP" + port = 8080 + target_port = 8080 + } + dynamic "port" { + for_each = local.tenants_chronicle_forwarder_lb_ports[each.key] + content { + name = "tcp-${port.value}" + protocol = "TCP" + port = port.value + target_port = port.value + } + } + } + timeouts { + create = "5m" + } +} + +resource "kubectl_manifest" "service_attachment_tcp" { + for_each = local.tenants_exposing_service_attachments + yaml_body = templatefile("${path.module}/manifests/service-attachment.yaml", { + tenant = each.value.tenant_id + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + service_name = kubernetes_service.service_tcp[each.key].metadata[0].name + }) + timeouts { + create = "5m" + } +} + +resource "kubernetes_service" "service_udp" { + for_each = { for k, v in var.tenants : k => v if v.forwarder_config.tls_config != null } + metadata { + name = "cfps-${each.value.tenant_id}-udp" + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + annotations = { + "networking.gke.io/load-balancer-type" = "Internal" + } + } + spec { + selector = { + app = local.deployment_names[each.key] + } + type = "LoadBalancer" + dynamic "port" { + for_each = local.tenants_chronicle_forwarder_lb_ports[each.key] + content { + name = "udp-${port.value}" + protocol = "UDP" + port = port.value + target_port = port.value + } + } + } +} + +resource "kubectl_manifest" "service_attachment_udp" { + for_each = local.tenants_exposing_udp_service_attachments + yaml_body = templatefile("${path.module}/manifests/service-attachment.yaml", { + tenant = each.value.tenant_id + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + service_name = kubernetes_service.service_udp[each.key].metadata[0].name + }) + timeouts { + create = "5m" + } +} + +resource "kubernetes_deployment" "cfps" { + for_each = var.tenants + metadata { + name = local.deployment_names[each.key] + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + labels = { + app = local.deployment_names[each.key] + } + } + spec { + replicas = 2 # default number of pods + selector { + match_labels = { + app = local.deployment_names[each.key] + } + } + template { + metadata { + labels = { + app = local.deployment_names[each.key] + } + } + spec { + container { + name = "cfps" + image = "gcr.io/chronicle-container/${var.tenants[each.key].chronicle_forwarder_image}" + image_pull_policy = "IfNotPresent" + + resources { + requests = { + cpu = "2" # 2 CPUs requested + memory = "2Gi" # 2 GB of RAM requested + } + limits = { + cpu = "4" # 4 CPUs limit + memory = "6Gi" # 4 GB of RAM limit + } + } + + volume_mount { + mount_path = "/opt/chronicle/external/" + name = kubernetes_secret.secret_config[each.key].metadata[0].name + read_only = true + } + dynamic "volume_mount" { + for_each = try(kubernetes_secret.tls[each.key], null) != null ? [""] : [] + content { + mount_path = "/opt/chronicle/external/certs" + name = kubernetes_secret.tls[each.key].metadata[0].name + read_only = true + } + } + liveness_probe { + http_get { + path = "/meta/available" + port = 8080 + } + failure_threshold = 3 + period_seconds = 30 + } + readiness_probe { + http_get { + path = "/meta/ready" + port = 8080 + } + failure_threshold = 1 + period_seconds = 30 + } + } + volume { + name = kubernetes_secret.secret_config[each.key].metadata[0].name + secret { + secret_name = kubernetes_secret.secret_config[each.key].metadata[0].name + } + } + dynamic "volume" { + for_each = try(kubernetes_secret.tls[each.key], null) != null ? [""] : [] + content { + name = kubernetes_secret.tls[each.key].metadata[0].name + secret { + secret_name = kubernetes_secret.tls[each.key].metadata[0].name + } + } + } + } + } + } +} + +resource "kubernetes_horizontal_pod_autoscaler_v2" "cfps" { + for_each = var.tenants + metadata { + name = "${local.deployment_names[each.key]}-hpa" + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + } + spec { + scale_target_ref { + kind = "Deployment" + name = kubernetes_deployment.cfps[each.key].metadata[0].name + } + + min_replicas = 2 # Minimum number of pods + max_replicas = 5 # Maximum allowed replicas + + metric { + type = "Resource" + resource { + name = "cpu" + target { + type = "Utilization" + average_utilization = "80" + } + } + } + } +} + +resource "kubernetes_secret" "secret_config" { + for_each = var.tenants + metadata { + name = "cfps-secret-config-${each.key}" + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + } + data = { + "config.conf" = local.chronicle_forwarder_config[each.key] + } + type = "Opaque" +} + +resource "kubernetes_secret" "tls" { + for_each = local.tenants_secret_tls + metadata { + name = "cfps-secret-tls-${each.key}" + namespace = kubernetes_namespace.namespace[each.key].metadata[0].name + } + data = { + "tls.crt" = each.value.cer + "tls.key" = each.value.key + } + type = "kubernetes.io/tls" +} diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml new file mode 100644 index 0000000000..8ae79be1b7 --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml @@ -0,0 +1,13 @@ +apiVersion: networking.gke.io/v1 +kind: ServiceAttachment +metadata: + name: chronicle-sa-${tenant} + namespace: ${namespace} +spec: + connectionPreference: ACCEPT_MANUAL + natSubnets: + - psc + proxyProtocol: false + resourceRef: + kind: Service + name: ${service_name} \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/outputs.tf b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/outputs.tf new file mode 100644 index 0000000000..1815b1a4cf --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2024 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 "tenants_ssl_cert_ca" { + description = "Tenants Self Signed CA certificates." + value = tls_self_signed_cert.forwarder_ca_cert +} \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/ssl.tf b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/ssl.tf new file mode 100644 index 0000000000..cdc31ff591 --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/ssl.tf @@ -0,0 +1,109 @@ +/** + * Copyright 2024 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. + */ + +locals { + self_signed_certs_for_tenant = { + for k, v in var.tenants : k => v if v.forwarder_config.tls_config.required && v.forwarder_config.tls_config.cert_key == null + } + cert_subjects = [ + { + country = "IT" + province = "Lombardy" + locality = "Milan" + organization = "Example" + organizational_unit = "Example" + } + ] +} + +####################################################################### +# FORWARDER CA PRIVATE KEY # +####################################################################### + +resource "tls_private_key" "forwarder_ca_private_key" { + for_each = local.self_signed_certs_for_tenant + algorithm = "RSA" +} + +####################################################################### +# FORWARDER CA CERT # +####################################################################### + +resource "tls_self_signed_cert" "forwarder_ca_cert" { + for_each = local.self_signed_certs_for_tenant + private_key_pem = tls_private_key.forwarder_ca_private_key[each.key].private_key_pem + is_ca_certificate = true + dynamic "subject" { + for_each = toset(local.cert_subjects) + content { + country = subject.value.country + province = subject.value.province + locality = subject.value.locality + common_name = "Chronicle Forwarder" + organization = subject.value.organization + organizational_unit = each.value.tenant_id + } + } + validity_period_hours = 87600 // 3650 days or 10 years + allowed_uses = [ + "digital_signature", + "cert_signing", + "crl_signing", + ] +} + +####################################################################### +# SERVER CERT SIGNED BY CA # +####################################################################### + +resource "tls_private_key" "forwarder_server_key" { + for_each = local.self_signed_certs_for_tenant + algorithm = "RSA" +} + +resource "tls_cert_request" "forwarder_server_csr" { + for_each = local.self_signed_certs_for_tenant + private_key_pem = tls_private_key.forwarder_server_key[each.key].private_key_pem + dns_names = ["${each.value.tenant_id}.chronicle-forwarder.gke"] + + dynamic "subject" { + for_each = toset(local.cert_subjects) + content { + country = subject.value.country + province = subject.value.province + locality = subject.value.locality + common_name = "Gitlab" + organization = subject.value.organization + organizational_unit = each.value.tenant_id + } + } +} + +resource "tls_locally_signed_cert" "forwarder_server_singed_cert" { + for_each = local.self_signed_certs_for_tenant + cert_request_pem = tls_cert_request.forwarder_server_csr[each.key].cert_request_pem + ca_private_key_pem = tls_private_key.forwarder_ca_private_key[each.key].private_key_pem + ca_cert_pem = tls_self_signed_cert.forwarder_ca_cert[each.key].cert_pem + + validity_period_hours = 87600 // 3650 days or 10 years + + allowed_uses = [ + "digital_signature", + "key_encipherment", + "server_auth", + "client_auth", + ] +} diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/variables.tf b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/variables.tf new file mode 100644 index 0000000000..8c5bfc17c6 --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/variables.tf @@ -0,0 +1,48 @@ +/** + * Copyright 2024 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 "image" { + description = "Container image to use." + type = string + nullable = false + default = "redis:6.2" +} + +variable "tenants" { + description = "Chronicle forwarders tenants config." + type = map(object({ + chronicle_forwarder_image = optional(string, "cf_production_stable") + chronicle_region = string + tenant_id = string + namespace = string + network_config = optional(object({ + expose_service_attachment = optional(bool, false) + load_balancer_ports = optional(list(string), null) + }), {}) + forwarder_config = object({ + config_file_content = optional(string) + customer_id = optional(string) + collector_id = optional(string) + secret_key = optional(string) + tls_config = optional(object({ + required = optional(bool, false) + cert_pub = optional(string) + cert_key = optional(string) + }), { required = false }) + }) + })) + default = {} +} \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf new file mode 100644 index 0000000000..e9afe5c229 --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf @@ -0,0 +1,27 @@ +/** + * Copyright 2024 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. + */ + +terraform { + required_providers { + restful = { + source = "magodo/restful" + } + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.7.0" + } + } +} \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/variables.tf b/blueprints/secops/secops-gke-forwarder/variables.tf new file mode 100644 index 0000000000..64c6c4f978 --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/variables.tf @@ -0,0 +1,87 @@ +/** + * Copyright 2024 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 "chronicle_forwarder" { + description = "Chronicle GKE forwarder configuration." + type = object({ + cluster_name = optional(string, "chronicle-log-ingestion") + master_authorized_ranges = optional(map(string), { + rfc-1918-10-8 = "10.0.0.0/8" + }) + }) + default = {} +} + +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 "region" { + description = "GCP region." + type = string +} + +variable "tenants" { + description = "Chronicle forwarders tenants config." + type = map(object({ + chronicle_forwarder_image = optional(string, "cf_production_stable") + chronicle_region = string + tenant_id = string + namespace = string + forwarder_config = object({ + config_file_content = optional(string) + customer_id = optional(string) + collector_id = optional(string) + secret_key = optional(string) + tls_config = optional(object({ + required = optional(bool, false) + cert_pub = optional(string) + cert_key = optional(string) + }), { required = false }) + }) + })) + default = {} +} + +variable "network_config" { + description = "Shared VPC network configurations to use for Gitlab Runner VM." + type = object({ + host_project = optional(string) + network_self_link = string + subnet_self_link = string + ip_range_gke_master = string + }) +} From 2563adc465166677e5bcd9f2b6d6975a4b18dc16 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 10:34:54 +0200 Subject: [PATCH 02/16] add boilerplate to service attachment yaml file --- .../manifests/service-attachment.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml index 8ae79be1b7..1555a34bb3 100644 --- a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/manifests/service-attachment.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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 +# +# https://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. + apiVersion: networking.gke.io/v1 kind: ServiceAttachment metadata: From ec5661fff86b1cd665fb57f352cb3822c06a5185 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 10:47:21 +0200 Subject: [PATCH 03/16] small updates --- .../secops/secops-gke-forwarder/README.md | 14 ++++++------- .../secops/secops-gke-forwarder/outputs.tf | 3 ++- .../secops/secops-gke-forwarder/variables.tf | 20 +++++++++---------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index 41a30a099e..8d0d1679e0 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -96,19 +96,19 @@ Then running the command `kubectl get pods` you should receive the following mes | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [network_config](variables.tf#L79) | Shared VPC network configurations to use for Gitlab Runner VM. | object({…}) | ✓ | | -| [prefix](variables.tf#L28) | Prefix used for resource names. | string | ✓ | | -| [project_id](variables.tf#L47) | Project id, references existing project if `project_create` is null. | string | ✓ | | -| [region](variables.tf#L52) | GCP region. | string | ✓ | | +| [network_config](variables.tf#L28) | Shared VPC network configurations to use for Gitlab Runner VM. | object({…}) | ✓ | | +| [prefix](variables.tf#L38) | Prefix used for resource names. | string | ✓ | | +| [project_id](variables.tf#L57) | Project id, references existing project if `project_create` is null. | string | ✓ | | +| [region](variables.tf#L62) | GCP region. | string | ✓ | | | [chronicle_forwarder](variables.tf#L17) | Chronicle GKE forwarder configuration. | object({…}) | | {} | -| [project_create](variables.tf#L38) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | -| [tenants](variables.tf#L57) | Chronicle forwarders tenants config. | map(object({…})) | | {} | +| [project_create](variables.tf#L48) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | +| [tenants](variables.tf#L67) | Chronicle forwarders tenants config. | map(object({…})) | | {} | ## Outputs | name | description | sensitive | |---|---|:---:| -| [fleet_host](outputs.tf#L17) | | | +| [fleet_host](outputs.tf#L17) | GKE Fleet host. | | ## Test diff --git a/blueprints/secops/secops-gke-forwarder/outputs.tf b/blueprints/secops/secops-gke-forwarder/outputs.tf index bfd8cc7239..447abc572d 100644 --- a/blueprints/secops/secops-gke-forwarder/outputs.tf +++ b/blueprints/secops/secops-gke-forwarder/outputs.tf @@ -15,5 +15,6 @@ */ output "fleet_host" { - value = local.fleet_host + description = "GKE Fleet host." + value = local.fleet_host } \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/variables.tf b/blueprints/secops/secops-gke-forwarder/variables.tf index 64c6c4f978..09de19212c 100644 --- a/blueprints/secops/secops-gke-forwarder/variables.tf +++ b/blueprints/secops/secops-gke-forwarder/variables.tf @@ -25,6 +25,16 @@ variable "chronicle_forwarder" { default = {} } +variable "network_config" { + description = "Shared VPC network configurations to use for Gitlab Runner VM." + type = object({ + host_project = optional(string) + network_self_link = string + subnet_self_link = string + ip_range_gke_master = string + }) +} + variable "prefix" { description = "Prefix used for resource names." type = string @@ -75,13 +85,3 @@ variable "tenants" { })) default = {} } - -variable "network_config" { - description = "Shared VPC network configurations to use for Gitlab Runner VM." - type = object({ - host_project = optional(string) - network_self_link = string - subnet_self_link = string - ip_range_gke_master = string - }) -} From 1f3ecbdd8e72617033dd008270dfc8c1575fcbe4 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 10:51:24 +0200 Subject: [PATCH 04/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index 8d0d1679e0..c69fb553e5 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -47,7 +47,7 @@ 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 [terraform.tfvars.sample](terraform.tfvars.sample) as starting point - just +See the example test at the end of this README.md as starting point - just copy it to `terraform.tfvars` and edit the latter. See the variables documentation below. From d7933c0c5339dc6baa1030d205eafa4f1a05edfd Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 11:15:58 +0200 Subject: [PATCH 05/16] small updates --- .../secops-forwarder-deployment/versions.tf | 27 ------------------- .../versions_override.tf | 25 +++++++++++++++++ .../secops/secops-gke-forwarder/versions.tf | 27 +++++++++++++++++++ 3 files changed, 52 insertions(+), 27 deletions(-) delete mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf create mode 100644 blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions_override.tf create mode 100644 blueprints/secops/secops-gke-forwarder/versions.tf diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf deleted file mode 100644 index e9afe5c229..0000000000 --- a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions.tf +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2024 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. - */ - -terraform { - required_providers { - restful = { - source = "magodo/restful" - } - kubectl = { - source = "gavinbunney/kubectl" - version = ">= 1.7.0" - } - } -} \ No newline at end of file diff --git a/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions_override.tf b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions_override.tf new file mode 100644 index 0000000000..75d041486f --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/secops-forwarder-deployment/versions_override.tf @@ -0,0 +1,25 @@ +# Copyright 2024 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 +# +# https://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. + +terraform { + required_providers { + restful = { + source = "magodo/restful" + } + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.7.0" + } + } +} diff --git a/blueprints/secops/secops-gke-forwarder/versions.tf b/blueprints/secops/secops-gke-forwarder/versions.tf new file mode 100644 index 0000000000..437c75b0aa --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/versions.tf @@ -0,0 +1,27 @@ +# Copyright 2024 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 +# +# https://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. + +terraform { + required_version = ">= 1.7.4" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 5.41.0, < 6.0.0" # tftest + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 5.41.0, < 6.0.0" # tftest + } + } +} From 801ff0ae948127d72fd3c29226608f7e328b085e Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 11:21:46 +0200 Subject: [PATCH 06/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index c69fb553e5..99607e9bae 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -115,7 +115,7 @@ Then running the command `kubectl get pods` you should receive the following mes ```hcl module "test" { source = "./fabric/blueprints/secops/secops-gke-forwarder" - project_id = "tmp-prod-net-landing-0" + project_id = "test" region = "europe-west8" network_config = { host_project = "prod-net-landing-0" From 84c3b204e436317ca85d3a3ad54983c223173962 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 12:04:53 +0200 Subject: [PATCH 07/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index 99607e9bae..2786748f0c 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -116,6 +116,9 @@ Then running the command `kubectl get pods` you should receive the following mes module "test" { source = "./fabric/blueprints/secops/secops-gke-forwarder" project_id = "test" + project_create_config = { + billing_account = "12345-ABCDE-12345" + } region = "europe-west8" network_config = { host_project = "prod-net-landing-0" From 7f8b2d74af803f5c2c68ba9901e701c77b9fe121 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 12:27:42 +0200 Subject: [PATCH 08/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index 2786748f0c..bca016b804 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -116,8 +116,9 @@ Then running the command `kubectl get pods` you should receive the following mes module "test" { source = "./fabric/blueprints/secops/secops-gke-forwarder" project_id = "test" - project_create_config = { - billing_account = "12345-ABCDE-12345" + project_create = { + billing_account_id = "12345-ABCDEF-12345" + parent_id = "folders/2345678901" } region = "europe-west8" network_config = { From 3a7da29c1e85befba807e3795e9978839cd812fa Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 12:43:08 +0200 Subject: [PATCH 09/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index bca016b804..d3d9115ef2 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -118,7 +118,7 @@ module "test" { project_id = "test" project_create = { billing_account_id = "12345-ABCDEF-12345" - parent_id = "folders/2345678901" + parent = "folders/2345678901" } region = "europe-west8" network_config = { From 419ababd84eed693bac2fa11e05d0091a0169349 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 13:37:56 +0200 Subject: [PATCH 10/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index d3d9115ef2..c28d361047 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -151,7 +151,7 @@ module "test" { } } } -# tftest modules=5 resources=33 files=credentials,config +# tftest modules=5 resources=34 files=credentials,config ``` ``` From 6c9369298eb12a2520d723c747b7df64648fc2a3 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 14:11:36 +0200 Subject: [PATCH 11/16] small updates --- blueprints/secops/secops-gke-forwarder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index c28d361047..cf632a17f2 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -120,7 +120,7 @@ module "test" { billing_account_id = "12345-ABCDEF-12345" parent = "folders/2345678901" } - region = "europe-west8" + region = "europe-west8" network_config = { host_project = "prod-net-landing-0" network_self_link = "https://www.googleapis.com/compute/v1/projects/prod-net-landing-0/global/networks/prod-landing-0" From 426c9a630007b9594311a95a7fa7a18a0d4976d8 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 15:11:01 +0200 Subject: [PATCH 12/16] add OWNERS file --- blueprints/secops/secops-gke-forwarder/OWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 blueprints/secops/secops-gke-forwarder/OWNERS diff --git a/blueprints/secops/secops-gke-forwarder/OWNERS b/blueprints/secops/secops-gke-forwarder/OWNERS new file mode 100644 index 0000000000..65c8fc162f --- /dev/null +++ b/blueprints/secops/secops-gke-forwarder/OWNERS @@ -0,0 +1 @@ +simonebruzzechesse From e830f7b5e447c2cf5ae213bf62876484afeefc5f Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 17:53:53 +0200 Subject: [PATCH 13/16] small fixes --- blueprints/secops/secops-gke-forwarder/README.md | 12 ++++-------- blueprints/secops/secops-gke-forwarder/main.tf | 8 ++++---- blueprints/secops/secops-gke-forwarder/variables.tf | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index cf632a17f2..df5b98b653 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -27,19 +27,15 @@ if required and when the prompt appears, click on “confirm”. Otherwise, in your console of choice: ```bash -git clone REPO_URL +git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric.git ``` Before you deploy the architecture, you will need at least the following -information (for more precise configuration see the Variables section): +information/configurations in place (for more precise configuration see the Variables section): * The project ID - -The VPC host project, VPC and subnets should already exist and the following networking requirements are satisfied: -- configured PSA for Cloud SQL on the VPC -- subnets configured with PGA and Cloud NAT for internet access -- Inbound firewall rule for IAP on port 22 -- Inbound firewall rule for TCP ports 80, 443, 2222 from proxy subnet CIDR (gitlab) +* The VPC host project +* VPC and subnets should already exist #### Step 2: Prepare the variables diff --git a/blueprints/secops/secops-gke-forwarder/main.tf b/blueprints/secops/secops-gke-forwarder/main.tf index 4c5197a003..cbf256cb72 100644 --- a/blueprints/secops/secops-gke-forwarder/main.tf +++ b/blueprints/secops/secops-gke-forwarder/main.tf @@ -23,7 +23,7 @@ locals { } module "project" { - source = "./../../../modules/project" + source = "../../../modules/project" billing_account = (var.project_create != null ? var.project_create.billing_account_id : null @@ -32,7 +32,7 @@ module "project" { ? var.project_create.parent : null ) - prefix = var.project_create == null ? null : var.prefix + prefix = var.prefix project_create = var.project_create != null name = var.project_id services = concat([ @@ -48,7 +48,7 @@ module "project" { } module "fleet" { - source = "./../../../modules/gke-hub" + source = "../../../modules/gke-hub" project_id = var.project_id clusters = { "chronicle-log-ingestion" = module.chronicle-forwarder.id @@ -56,7 +56,7 @@ module "fleet" { } module "chronicle-forwarder" { - source = "./../../../modules/gke-cluster-autopilot" + source = "../../../modules/gke-cluster-autopilot" project_id = var.project_id name = var.chronicle_forwarder.cluster_name location = var.region diff --git a/blueprints/secops/secops-gke-forwarder/variables.tf b/blueprints/secops/secops-gke-forwarder/variables.tf index 09de19212c..db377dea89 100644 --- a/blueprints/secops/secops-gke-forwarder/variables.tf +++ b/blueprints/secops/secops-gke-forwarder/variables.tf @@ -26,7 +26,7 @@ variable "chronicle_forwarder" { } variable "network_config" { - description = "Shared VPC network configurations to use for Gitlab Runner VM." + description = "Shared VPC network configurations to use for GKE cluster." type = object({ host_project = optional(string) network_self_link = string From a7eb557d1471542f7eaa5597d6387e60c8a1df59 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 23 Aug 2024 17:56:10 +0200 Subject: [PATCH 14/16] small fixes --- blueprints/secops/secops-gke-forwarder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md index df5b98b653..a75cc970aa 100644 --- a/blueprints/secops/secops-gke-forwarder/README.md +++ b/blueprints/secops/secops-gke-forwarder/README.md @@ -92,7 +92,7 @@ Then running the command `kubectl get pods` you should receive the following mes | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [network_config](variables.tf#L28) | Shared VPC network configurations to use for Gitlab Runner VM. | object({…}) | ✓ | | +| [network_config](variables.tf#L28) | Shared VPC network configurations to use for GKE cluster. | object({…}) | ✓ | | | [prefix](variables.tf#L38) | Prefix used for resource names. | string | ✓ | | | [project_id](variables.tf#L57) | Project id, references existing project if `project_create` is null. | string | ✓ | | | [region](variables.tf#L62) | GCP region. | string | ✓ | | From c64f8dd13dcb2a1009589bb1f8c64220b66773c2 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 6 Sep 2024 15:03:12 +0200 Subject: [PATCH 15/16] update versions.tf --- blueprints/secops/secops-gke-forwarder/versions.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/secops/secops-gke-forwarder/versions.tf b/blueprints/secops/secops-gke-forwarder/versions.tf index 437c75b0aa..bd3123dd99 100644 --- a/blueprints/secops/secops-gke-forwarder/versions.tf +++ b/blueprints/secops/secops-gke-forwarder/versions.tf @@ -17,11 +17,11 @@ terraform { required_providers { google = { source = "hashicorp/google" - version = ">= 5.41.0, < 6.0.0" # tftest + version = ">= 6.1.0, < 7.0.0" # tftest } google-beta = { source = "hashicorp/google-beta" - version = ">= 5.41.0, < 6.0.0" # tftest + version = ">= 6.1.0, < 7.0.0" # tftest } } } From 220b0e8b84971157a4fffdef78f586b89353f727 Mon Sep 17 00:00:00 2001 From: bruzzechesse Date: Fri, 6 Sep 2024 15:28:19 +0200 Subject: [PATCH 16/16] update versions.tf --- blueprints/secops/secops-gke-forwarder/versions.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blueprints/secops/secops-gke-forwarder/versions.tf b/blueprints/secops/secops-gke-forwarder/versions.tf index bd3123dd99..f569ce5af2 100644 --- a/blueprints/secops/secops-gke-forwarder/versions.tf +++ b/blueprints/secops/secops-gke-forwarder/versions.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Fabric release: v35.0.0 + terraform { required_version = ">= 1.7.4" required_providers {