cloudrun-malware-scanner/terraform/service/main.tf (129 lines of code) (raw):

# 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. provider "google" { project = var.project_id region = var.region } data "google_project" "project" { project_id = var.project_id } ## Read config and extract bucket names locals { config_json = jsondecode(var.config_json) unscanned_bucket_names = local.config_json.buckets[*].unscanned } ## Verify service account exists # data "google_service_account" "malware_scanner_sa" { account_id = var.service_name project = data.google_project.project.project_id } ## Lookup the hash of the latest image ## data "google_artifact_registry_docker_image" "scanner-service-image" { location = var.region repository_id = var.service_name image_name = "${var.service_name}:latest" project = data.google_project.project.project_id } ## Deploy the Cloud Run Service # resource "google_cloud_run_v2_service" "malware_scanner" { name = var.service_name location = var.region ingress = "INGRESS_TRAFFIC_ALL" template { scaling { max_instance_count = 5 min_instance_count = 1 } service_account = data.google_service_account.malware_scanner_sa.email timeout = "${var.deadline}s" max_instance_request_concurrency = 20 containers { image = data.google_artifact_registry_docker_image.scanner-service-image.self_link resources { limits = { cpu = "1" memory = "4Gi" } cpu_idle = false # CPU is still allocated outside of requests startup_cpu_boost = true } env { name = "CONFIG_JSON" value = var.config_json } startup_probe { # Allow 90 secs before we start probing # Then allow up to 15*10 = 150s before we give up # (total possible startup time = 240s) initial_delay_seconds = 90 failure_threshold = 15 period_seconds = 10 timeout_seconds = 1 http_get { path = "/ready" } } liveness_probe { # Poll every 30 secs, allowing for 3 failures # period_seconds = 30 failure_threshold = 3 timeout_seconds = 30 http_get { path = "/ready" } } } } traffic { type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" percent = 100 } } ## Verify unscanned bucket(s) exist # data "google_storage_bucket" "unscanned-bucket" { for_each = toset(local.unscanned_bucket_names) name = each.value } ## Create EventArc Triggers on unscanned bucket(s) # resource "google_eventarc_trigger" "gcs-object-written" { for_each = toset(local.unscanned_bucket_names) name = "gcs-trigger-${each.value}" location = var.bucket_location matching_criteria { attribute = "type" value = "google.cloud.storage.object.v1.finalized" } matching_criteria { attribute = "bucket" value = data.google_storage_bucket.unscanned-bucket[each.value].name } destination { cloud_run_service { service = google_cloud_run_v2_service.malware_scanner.name region = google_cloud_run_v2_service.malware_scanner.location } } service_account = data.google_service_account.malware_scanner_sa.email } ## Update pubsub subscriptions to increase deadlines # resource "null_resource" "update-subscription-ack-deadline" { for_each = toset(local.unscanned_bucket_names) provisioner "local-exec" { command = "gcloud pubsub subscriptions update \"${google_eventarc_trigger.gcs-object-written[each.key].transport[0].pubsub[0].subscription}\" --ack-deadline=${var.deadline}" } } ## Deploy scheduled task to refresh the CVD Mirror # To avoid having too many clients use the same time slot, # ClamAV requires that updates are scheduled at a random minute between 3 # and 57 avoiding multiples of 10. resource "random_integer" "cvd_mirror_update_schedule_minutes" { min = 3 max = 57 } locals { # Avoid multiples of 10 by subtracting 3. cvd_mirror_update_schedule_minutes = ( random_integer.cvd_mirror_update_schedule_minutes.result % 10 == 0 ? random_integer.cvd_mirror_update_schedule_minutes.result - 3 : random_integer.cvd_mirror_update_schedule_minutes.result ) } resource "google_cloud_scheduler_job" "cvd_mirror_update" { name = "${var.service_name}-cvd-mirror-update" schedule = "${local.cvd_mirror_update_schedule_minutes} */2 * * *" attempt_deadline = "320s" region = var.region http_target { http_method = "POST" uri = google_cloud_run_v2_service.malware_scanner.uri body = base64encode("{\"kind\":\"schedule#cvd_update\"}") headers = { "Content-Type" = "application/json" } oidc_token { service_account_email = data.google_service_account.malware_scanner_sa.email } } }