Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 230 additions & 0 deletions gcp/modules/tiles_tlog/global/network.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/**
* Copyright 2026 The Sigstore Authors
*
* 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.
*/

module "shared" {
source = "../shared"

project_id = var.project_id
dns_subdomain_name = var.dns_subdomain_name
service_health_check_path = var.service_health_check_path
max_req_content_length = var.max_req_content_length
max_req_content_length_description = var.max_req_content_length_description
enable_healthcheck_logging = var.enable_healthcheck_logging
http_grpc_qpm_rate_limit = var.http_grpc_qpm_rate_limit
enable_adaptive_protection = var.enable_adaptive_protection
create_grpc_health_check = var.grpc_write_path == "" ? false : true
}

locals {
hostname = trimsuffix("${var.dns_subdomain_name}.${var.dns_domain_name}", ".")
prefix = replace(var.dns_subdomain_name, ".", "-")
}

resource "google_dns_record_set" "A_tlog" {
name = "${var.dns_subdomain_name}.${var.dns_domain_name}"
type = "A"
ttl = 60

project = var.project_id
managed_zone = var.dns_zone_name

rrdatas = [google_compute_global_address.global_lb_ipv4.address]
}

resource "google_certificate_manager_dns_authorization" "tlog_auth" {
name = "${local.prefix}-dns-auth"
domain = local.hostname
}

resource "google_dns_record_set" "CNAME_auth_tlog" {
project = var.project_id
name = google_certificate_manager_dns_authorization.tlog_auth.dns_resource_record[0].name
type = google_certificate_manager_dns_authorization.tlog_auth.dns_resource_record[0].type
ttl = 60
managed_zone = var.dns_zone_name
rrdatas = [google_certificate_manager_dns_authorization.tlog_auth.dns_resource_record[0].data]
}

resource "google_compute_global_address" "global_lb_ipv4" {
name = "${local.prefix}-global-ext-lb"
address_type = "EXTERNAL"
project = var.project_id
}

locals {
http_neg_map = { for neg in var.active_http_negs : "${neg.name}-${neg.zone}" => neg }
grpc_neg_map = { for neg in var.active_grpc_negs : "${neg.name}-${neg.zone}" => neg }
}

data "google_compute_network_endpoint_group" "k8s_http_neg" {
for_each = local.http_neg_map

name = each.value.name
project = var.project_id
zone = each.value.zone
}

data "google_compute_network_endpoint_group" "k8s_grpc_neg" {
for_each = local.grpc_neg_map

name = each.value.name
project = var.project_id
zone = each.value.zone
}

resource "google_compute_backend_service" "k8s_http_backend_service" {
name = "${local.prefix}-global-k8s-neg-backend-service"
project = var.project_id

load_balancing_scheme = "EXTERNAL_MANAGED"
port_name = "http"
protocol = "HTTP"

connection_draining_timeout_sec = 15
health_checks = [module.shared.http_health_check_id]

dynamic "backend" {
for_each = data.google_compute_network_endpoint_group.k8s_http_neg
iterator = neg

content {
group = neg.value.id
balancing_mode = "RATE"
max_rate_per_endpoint = var.backend_service_max_rps
}
}

security_policy = module.shared.security_policy_id

log_config {
enable = var.enable_backend_service_logging
}
}

resource "google_compute_backend_service" "k8s_grpc_backend_service" {
count = var.grpc_write_path == "" ? 0 : 1
name = "${local.prefix}-global-k8s-grpc-neg-backend-service"
project = var.project_id

load_balancing_scheme = "EXTERNAL_MANAGED"
port_name = "grpc"
protocol = "HTTP2"

connection_draining_timeout_sec = 15
health_checks = module.shared.grpc_health_check_id != "" ? [module.shared.grpc_health_check_id] : []

dynamic "backend" {
for_each = data.google_compute_network_endpoint_group.k8s_grpc_neg
iterator = neg

content {
group = neg.value.id
balancing_mode = "RATE"
max_rate_per_endpoint = var.backend_service_max_rps
}
}

security_policy = module.shared.security_policy_id

log_config {
enable = var.enable_backend_service_logging
}
}

resource "google_compute_url_map" "url_map" {
name = "${local.prefix}-global-lb"
project = var.project_id

default_service = google_compute_backend_service.k8s_http_backend_service.id

host_rule {
hosts = [local.hostname]
path_matcher = "global"
}

path_matcher {
name = "global"
default_service = google_compute_backend_service.k8s_http_backend_service.id
dynamic "route_rules" {
for_each = length(var.active_http_negs) > 0 ? [1] : []

content {
priority = 1
service = google_compute_backend_service.k8s_http_backend_service.id
match_rules {
path_template_match = var.http_write_path
}
match_rules {
full_path_match = "/healthz"
}
}
}
dynamic "route_rules" {
for_each = length(var.active_grpc_negs) > 0 && var.grpc_write_path != "" ? [1] : []

content {
priority = 2
service = google_compute_backend_service.k8s_grpc_backend_service[0].id
match_rules {
path_template_match = var.grpc_write_path
}
}
}
}
}

resource "google_certificate_manager_certificate" "ssl_certificate" {
name = "${local.prefix}-global-ssl-cert"
project = var.project_id

managed {
domains = [local.hostname]
dns_authorizations = [
google_certificate_manager_dns_authorization.tlog_auth.id
]
}
}

resource "google_certificate_manager_certificate_map" "tlog_certificate_map" {
name = "${local.prefix}-cert-map"
}

resource "google_certificate_manager_certificate_map_entry" "tlog_certificate_map_entry" {
name = "${local.prefix}-cert-map-entry"
map = google_certificate_manager_certificate_map.tlog_certificate_map.name
certificates = [google_certificate_manager_certificate.ssl_certificate.id]
hostname = local.hostname
}

resource "google_compute_target_https_proxy" "lb_proxy" {
name = "${local.prefix}-global-https-proxy"
project = var.project_id

url_map = google_compute_url_map.url_map.id

ssl_policy = module.shared.ssl_policy_id
certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.tlog_certificate_map.id}"
}

resource "google_compute_global_forwarding_rule" "https_forwarding_rule" {
name = "${local.prefix}-global-https-forwarding-rule"
project = var.project_id

ip_address = google_compute_global_address.global_lb_ipv4.address
target = google_compute_target_https_proxy.lb_proxy.id
port_range = "443"
load_balancing_scheme = "EXTERNAL_MANAGED"
}
35 changes: 35 additions & 0 deletions gcp/modules/tiles_tlog/global/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2026 The Sigstore Authors
*
* 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 "http_health_check_id" {
value = module.shared.http_health_check_id
}

output "grpc_health_check_id" {
value = module.shared.grpc_health_check_id
}

output "security_policy_id" {
value = module.shared.security_policy_id
}

output "bucket_security_policy_id" {
value = module.shared.bucket_security_policy_id
}

output "ssl_policy_id" {
value = module.shared.ssl_policy_id
}
116 changes: 116 additions & 0 deletions gcp/modules/tiles_tlog/global/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright 2026 The Sigstore Authors
*
* 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 "project_id" {
type = string
default = ""
validation {
condition = length(var.project_id) > 0
error_message = "Must specify project_id variable."
}
}

variable "dns_zone_name" {
description = "Name of DNS Zone object in Google Cloud DNS"
type = string
}

variable "dns_domain_name" {
description = "Name of DNS domain name in Google Cloud DNS"
type = string
}

variable "dns_subdomain_name" {
description = "Subdomain name for the service, e.g. 'v2.rekor' or 'tessera.ct'"
type = string
}

variable "active_http_negs" {
type = list(object({
name = string
zone = string
}))
description = "List of objects containing the names and zones of active HTTP NEGs across all shards and regions to route write traffic to."
default = []
}

variable "active_grpc_negs" {
type = list(object({
name = string
zone = string
}))
description = "List of objects containing the names and zones of active gRPC NEGs across all shards and regions to route write traffic to."
default = []
}

variable "backend_service_max_rps" {
description = "Max requests per second that a single backend instance can handle."
type = number
default = 5
}

variable "enable_backend_service_logging" {
description = "Whether to enable logging for the HTTP backend service."
type = bool
default = true
}

variable "http_write_path" {
description = "The path for write requests on HTTP"
type = string
}

variable "grpc_write_path" {
description = "The path for write requests on GRPC"
type = string
default = ""
}

variable "service_health_check_path" {
description = "HTTP URL request path for the service health check"
type = string
default = "/healthz"
}

variable "max_req_content_length" {
description = "maximum request content length in bytes for the write path"
type = number
default = 8388608 // 8 MB
}

variable "max_req_content_length_description" {
description = "maximum request content length, used only for security policy description"
type = string
default = "8MB"
}

variable "enable_healthcheck_logging" {
description = "whether to enable logging for the HTTP and gRPC health checks"
type = bool
default = true
}

variable "http_grpc_qpm_rate_limit" {
description = "count of write requests per minute allowed to HTTP and gRPC backends"
type = number
default = 600 // 10 QPS
}

variable "enable_adaptive_protection" {
description = "whether to enable layer 7 DDoS adaptive protection"
type = bool
default = true
}
Loading