Skip to content

Commit e13200a

Browse files
committed
Add support for external LBs for fulcio and TSA
Add a sigstore_global module that deploys common resources to support external load balancers for Fulcio and TSA. If deployed in a single region, individual modules call their respective "global" modules to deploy universal resources like Cloud Armor policies or IP addresses. If deployed in multi-region, the individual modules defer to the sigstore_global module to deploy these resources and additional load balancer resources so they are only managed once. The modules have the option of not managing the DNS record to enable a smooth transition from having the individual module manage it to managing it with the global module. We use DNS Authorization using CNAME records instead of raw Certificate Manager certificates because the DNS record might, during the transition period, be managed by another module, and the IP address may not resolve to the new global IP address. Using DNS Authorization means we can still provision all the SSL resources even before the DNS A record is fully transitioned to the new global IP address. Also move project_roles to the global module, since IAM bindings are also global resources and can't be duplicated. Calling modules can just set their IAM bindings in sigstore_global instead of sigstore. Signed-off-by: Colleen Murphy <colleenmurphy@google.com>
1 parent d3bf606 commit e13200a

15 files changed

Lines changed: 1395 additions & 192 deletions

File tree

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
/**
2+
* Copyright 2026 The Sigstore Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
########################### PER-SERVICE ############################
18+
# One per service globally, whether single region or multi-region. #
19+
####################################################################
20+
21+
locals {
22+
hostname = trimsuffix("fulcio.${var.dns_domain_name}", ".")
23+
}
24+
25+
resource "google_dns_record_set" "A_fulcio" {
26+
count = var.manage_dns_a_record ? 1 : 0
27+
28+
name = "fulcio.${var.dns_domain_name}"
29+
type = "A"
30+
ttl = 60
31+
32+
project = var.project_id
33+
34+
managed_zone = var.dns_zone_name
35+
rrdatas = [google_compute_global_address.gce_lb_ipv4.address]
36+
}
37+
38+
resource "google_certificate_manager_dns_authorization" "fulcio_auth" {
39+
count = var.single_region ? 0 : 1
40+
41+
name = "fulcio-dns-auth"
42+
domain = local.hostname
43+
}
44+
45+
resource "google_dns_record_set" "CNAME_auth_fulcio" {
46+
count = var.single_region ? 0 : 1
47+
48+
project = var.project_id
49+
50+
name = google_certificate_manager_dns_authorization.fulcio_auth[count.index].dns_resource_record[0].name
51+
type = google_certificate_manager_dns_authorization.fulcio_auth[count.index].dns_resource_record[0].type
52+
ttl = 60
53+
54+
managed_zone = var.dns_zone_name
55+
rrdatas = [google_certificate_manager_dns_authorization.fulcio_auth[count.index].dns_resource_record[0].data]
56+
}
57+
58+
resource "google_compute_global_address" "gce_lb_ipv4" {
59+
name = var.lb_address_name == "" ? format("fulcio-%s-gce-ext-lb", var.cluster_name) : var.lb_address_name
60+
address_type = "EXTERNAL"
61+
project = var.project_id
62+
}
63+
64+
resource "google_compute_security_policy" "http_security_policy" {
65+
count = var.enable_cloud_armor ? 1 : 0
66+
67+
name = var.cloud_armor_policy_name
68+
project = var.project_id
69+
type = "CLOUD_ARMOR"
70+
71+
dynamic "rule" {
72+
for_each = var.cloud_armor_rules
73+
content {
74+
action = rule.value.action
75+
priority = rule.value.priority
76+
match {
77+
versioned_expr = rule.value.match.versioned_expr
78+
dynamic "config" {
79+
for_each = rule.value.match.config != null ? [rule.value.match.config] : []
80+
content {
81+
src_ip_ranges = config.value.src_ip_ranges
82+
}
83+
}
84+
dynamic "expr" {
85+
for_each = rule.value.match.expr != null ? [rule.value.match.expr] : []
86+
content {
87+
expression = expr.value.expression
88+
}
89+
}
90+
}
91+
92+
dynamic "rate_limit_options" {
93+
for_each = rule.value.rate_limit_options != null ? [rule.value.rate_limit_options] : []
94+
content {
95+
enforce_on_key = rate_limit_options.value.enforce_on_key
96+
conform_action = rate_limit_options.value.conform_action
97+
exceed_action = rate_limit_options.value.exceed_action
98+
rate_limit_threshold {
99+
count = rate_limit_options.value.qpm_rate_limit
100+
interval_sec = rate_limit_options.value.interval_sec
101+
}
102+
}
103+
}
104+
105+
dynamic "redirect_options" {
106+
for_each = rule.value.redirect_options != null ? [rule.value.redirect_options] : []
107+
content {
108+
type = redirect_options.value.type
109+
target = redirect_options.value.target
110+
}
111+
}
112+
113+
description = rule.value.description
114+
}
115+
116+
}
117+
118+
rule {
119+
action = "allow"
120+
priority = "2147483647"
121+
match {
122+
versioned_expr = "SRC_IPS_V1"
123+
config {
124+
src_ip_ranges = ["*"]
125+
}
126+
}
127+
description = "default rule"
128+
}
129+
130+
advanced_options_config {
131+
json_parsing = "STANDARD"
132+
}
133+
134+
adaptive_protection_config {
135+
layer_7_ddos_defense_config {
136+
enable = var.enable_adaptive_protection
137+
}
138+
}
139+
}
140+
141+
resource "google_compute_ssl_policy" "ssl_policy" {
142+
count = var.enable_ssl_policy ? 1 : 0
143+
144+
name = var.ssl_policy_name
145+
project = var.project_id
146+
147+
profile = "MODERN"
148+
min_tls_version = "TLS_1_2"
149+
}
150+
151+
####################### PER-SERVICE MULTIREGION ########################
152+
# One per service globally, only if using a multi-region configuration #
153+
# rather than K8s-Ingress-driven load balancing. #
154+
########################################################################
155+
156+
resource "google_compute_health_check" "http_health_check" {
157+
count = var.single_region ? 0 : 1
158+
159+
name = "fulcio-http-health-check"
160+
project = var.project_id
161+
162+
timeout_sec = 5
163+
check_interval_sec = 5
164+
healthy_threshold = 2
165+
unhealthy_threshold = 2
166+
167+
http_health_check {
168+
request_path = "/healthz"
169+
port_specification = "USE_SERVING_PORT"
170+
}
171+
172+
log_config {
173+
enable = var.enable_healthcheck_logging
174+
}
175+
}
176+
177+
resource "google_compute_health_check" "grpc_health_check" {
178+
count = var.single_region ? 0 : 1
179+
180+
name = "fulcio-grpc-health-check"
181+
project = var.project_id
182+
183+
timeout_sec = 5
184+
check_interval_sec = 5
185+
healthy_threshold = 2
186+
unhealthy_threshold = 2
187+
188+
tcp_health_check {
189+
port_specification = "USE_SERVING_PORT"
190+
}
191+
192+
log_config {
193+
enable = var.enable_healthcheck_logging
194+
}
195+
}
196+
197+
data "google_compute_network_endpoint_group" "k8s_http_neg" {
198+
for_each = toset(var.network_endpoint_group_zones)
199+
200+
name = var.network_endpoint_group_name
201+
project = var.project_id
202+
zone = each.key
203+
}
204+
205+
data "google_compute_network_endpoint_group" "k8s_grpc_neg" {
206+
for_each = toset(var.network_endpoint_group_zones)
207+
208+
name = var.network_endpoint_group_name_grpc
209+
project = var.project_id
210+
zone = each.key
211+
}
212+
213+
resource "google_compute_backend_service" "http_backend_service" {
214+
count = var.single_region ? 0 : 1
215+
216+
name = "fulcio-http-backend"
217+
project = var.project_id
218+
219+
load_balancing_scheme = "EXTERNAL_MANAGED"
220+
port_name = "http"
221+
protocol = "HTTP"
222+
223+
connection_draining_timeout_sec = 15
224+
health_checks = [google_compute_health_check.http_health_check[count.index].id]
225+
226+
dynamic "backend" {
227+
for_each = data.google_compute_network_endpoint_group.k8s_http_neg
228+
iterator = neg
229+
content {
230+
group = neg.value.id
231+
balancing_mode = "RATE"
232+
max_rate_per_endpoint = var.backend_service_max_rps
233+
}
234+
}
235+
236+
depends_on = [google_compute_security_policy.http_security_policy]
237+
security_policy = length(google_compute_security_policy.http_security_policy) > 0 ? google_compute_security_policy.http_security_policy[0].self_link : ""
238+
239+
log_config {
240+
enable = var.enable_backend_service_logging
241+
}
242+
}
243+
244+
resource "google_compute_backend_service" "grpc_backend_service" {
245+
count = var.single_region ? 0 : 1
246+
247+
name = "fulcio-grpc-backend"
248+
project = var.project_id
249+
250+
load_balancing_scheme = "EXTERNAL_MANAGED"
251+
port_name = "grpc"
252+
protocol = "HTTP2"
253+
254+
connection_draining_timeout_sec = 15
255+
health_checks = [google_compute_health_check.grpc_health_check[count.index].id]
256+
257+
dynamic "backend" {
258+
for_each = data.google_compute_network_endpoint_group.k8s_grpc_neg
259+
iterator = neg
260+
content {
261+
group = neg.value.id
262+
balancing_mode = "RATE"
263+
max_rate_per_endpoint = var.backend_service_max_rps
264+
}
265+
}
266+
267+
depends_on = [google_compute_security_policy.http_security_policy]
268+
security_policy = length(google_compute_security_policy.http_security_policy) > 0 ? google_compute_security_policy.http_security_policy[0].self_link : ""
269+
270+
log_config {
271+
enable = var.enable_backend_service_logging
272+
}
273+
}
274+
275+
resource "google_compute_url_map" "url_map" {
276+
count = var.single_region ? 0 : 1
277+
278+
name = "fulcio-lb"
279+
project = var.project_id
280+
281+
default_service = google_compute_backend_service.http_backend_service[count.index].id
282+
283+
host_rule {
284+
hosts = var.dns_domain_name == "" ? ["*"] : [local.hostname]
285+
path_matcher = "fulcio"
286+
}
287+
288+
path_matcher {
289+
name = "fulcio"
290+
default_service = google_compute_backend_service.http_backend_service[count.index].id
291+
path_rule {
292+
paths = ["/*"]
293+
service = google_compute_backend_service.http_backend_service[count.index].id
294+
}
295+
path_rule {
296+
paths = ["/dev.sigstore.fulcio.v2.CA"]
297+
service = google_compute_backend_service.grpc_backend_service[count.index].id
298+
}
299+
path_rule {
300+
paths = ["/dev.sigstore.fulcio.v2.CA/*"]
301+
service = google_compute_backend_service.grpc_backend_service[count.index].id
302+
}
303+
}
304+
}
305+
306+
resource "google_certificate_manager_certificate" "ssl_certificate" {
307+
count = var.single_region ? 0 : 1
308+
309+
name = "fulcio-ssl-cert"
310+
project = var.project_id
311+
312+
managed {
313+
domains = [local.hostname]
314+
dns_authorizations = [
315+
google_certificate_manager_dns_authorization.fulcio_auth[count.index].id
316+
]
317+
}
318+
}
319+
320+
resource "google_certificate_manager_certificate_map" "fulcio_certificate_map" {
321+
count = var.single_region ? 0 : 1
322+
323+
name = "fulcio-cert-map"
324+
}
325+
326+
resource "google_certificate_manager_certificate_map_entry" "fulcio_certificate_map_entry" {
327+
count = var.single_region ? 0 : 1
328+
329+
name = "fulcio-cert-map-entry"
330+
map = google_certificate_manager_certificate_map.fulcio_certificate_map[count.index].name
331+
certificates = [google_certificate_manager_certificate.ssl_certificate[count.index].id]
332+
hostname = local.hostname
333+
}
334+
335+
resource "google_compute_target_https_proxy" "lb_proxy" {
336+
count = var.single_region ? 0 : 1
337+
338+
name = "fulcio-https-proxy"
339+
project = var.project_id
340+
341+
url_map = google_compute_url_map.url_map[count.index].id
342+
343+
ssl_policy = google_compute_ssl_policy.ssl_policy[count.index].id
344+
certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.fulcio_certificate_map[count.index].id}"
345+
}
346+
347+
resource "google_compute_global_forwarding_rule" "https_forwarding_rule" {
348+
count = var.single_region ? 0 : 1
349+
350+
name = "fulcio-https-forwarding-rule"
351+
project = var.project_id
352+
353+
ip_address = google_compute_global_address.gce_lb_ipv4.address
354+
target = google_compute_target_https_proxy.lb_proxy[count.index].id
355+
port_range = "443"
356+
load_balancing_scheme = "EXTERNAL_MANAGED"
357+
ip_protocol = "TCP"
358+
}

0 commit comments

Comments
 (0)