feat(terraform): Exposes invoker_iam_disabled input to support environments enforcing DRS policy

This commit is contained in:
Loren Gordon 2026-08-21 09:28:27 -07:00
parent 31a67561ab
commit 75788a0547
No known key found for this signature in database
6 changed files with 72 additions and 15 deletions

View file

@ -286,6 +286,7 @@ example files.
cd terraform/litellm/gcp/examples/default
cp terraform.tfvars.example terraform.tfvars
# Edit: project, region, tenant, env, image_registry, proxy_config, gateway_extra_secrets.
# If your org enforces Domain Restricted Sharing (DRS), also set invoker_iam_disabled = true.
terraform init
terraform apply
@ -342,6 +343,23 @@ Set `allow_plaintext_lb = true` and leave `lb_domains = []`. Without the
flag, plan fails with a clear error pointing at the precondition.
Intended for short-lived trial / dev stacks only.
## Domain Restricted Sharing (DRS)
Some organizations enforce Domain Restricted Sharing policies that reject
`allUsers` IAM members on Cloud Run. This module supports those environments
through the `invoker_iam_disabled` input.
- `invoker_iam_disabled = true`: disables the Cloud Run invoker IAM check on
gateway/backend/ui services and skips the `allUsers` `run.invoker` bindings.
- `invoker_iam_disabled = false` (or unset): implements `allUsers` invoker
bindings so the load balancer can call the Cloud Run services.
Example for DRS-constrained environments:
```hcl
invoker_iam_disabled = true
```
## Using as a module
The directory itself is a module with **no `provider` block** — the caller

View file

@ -195,11 +195,12 @@ resource "google_cloud_run_v2_service" "gateway" {
}
}
name = "${local.name}-gateway"
location = var.region
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
labels = local.labels
deletion_protection = false
name = "${local.name}-gateway"
location = var.region
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
labels = local.labels
invoker_iam_disabled = var.invoker_iam_disabled
deletion_protection = false
template {
service_account = google_service_account.runtime.email
@ -324,11 +325,12 @@ resource "google_cloud_run_v2_service" "backend" {
}
}
name = "${local.name}-backend"
location = var.region
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
labels = local.labels
deletion_protection = false
name = "${local.name}-backend"
location = var.region
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
labels = local.labels
invoker_iam_disabled = var.invoker_iam_disabled
deletion_protection = false
template {
service_account = google_service_account.runtime.email
@ -443,11 +445,12 @@ resource "google_cloud_run_v2_service" "backend" {
# with zero IAM bindings, so a compromised UI container can't pivot to
# Secret Manager / Cloud SQL via the metadata service.
resource "google_cloud_run_v2_service" "ui" {
name = "${local.name}-ui"
location = var.region
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
labels = local.labels
deletion_protection = false
name = "${local.name}-ui"
location = var.region
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
labels = local.labels
invoker_iam_disabled = var.invoker_iam_disabled
deletion_protection = false
template {
service_account = google_service_account.ui_runtime.email
@ -491,6 +494,8 @@ resource "google_cloud_run_v2_service" "ui" {
# (LITELLM_MASTER_KEY); these IAM bindings just open up Cloud Run's invoker
# gate so the LB request makes it to the container.
resource "google_cloud_run_v2_service_iam_member" "gateway_allusers" {
count = var.invoker_iam_disabled == true ? 0 : 1
project = var.project_id
location = google_cloud_run_v2_service.gateway.location
name = google_cloud_run_v2_service.gateway.name
@ -499,6 +504,8 @@ resource "google_cloud_run_v2_service_iam_member" "gateway_allusers" {
}
resource "google_cloud_run_v2_service_iam_member" "backend_allusers" {
count = var.invoker_iam_disabled == true ? 0 : 1
project = var.project_id
location = google_cloud_run_v2_service.backend.location
name = google_cloud_run_v2_service.backend.name
@ -507,6 +514,8 @@ resource "google_cloud_run_v2_service_iam_member" "backend_allusers" {
}
resource "google_cloud_run_v2_service_iam_member" "ui_allusers" {
count = var.invoker_iam_disabled == true ? 0 : 1
project = var.project_id
location = google_cloud_run_v2_service.ui.location
name = google_cloud_run_v2_service.ui.name

View file

@ -38,6 +38,8 @@ module "litellm" {
image_registry = var.image_registry
image_tag = var.image_tag
invoker_iam_disabled = var.invoker_iam_disabled
lb_domains = var.lb_domains
allow_plaintext_lb = var.allow_plaintext_lb
cloudsql_deletion_protection = var.cloudsql_deletion_protection

View file

@ -64,6 +64,13 @@ variable "image_tag" {
default = "v1.86.0-dev"
}
# ---------- Load balancer auth mechanism ----------
variable "invoker_iam_disabled" {
description = "Disable the Cloud Run invoker IAM check. When true, the allUsers grant is skipped. Enable if the environment implements the DomainRestrictedSharing policy."
type = bool
default = null
}
# TLS provide DNS names for a managed cert, or opt into HTTP-only for dev.
variable "lb_domains" {
description = "DNS names (already pointing at lb_ip) for a Google-managed cert. Empty → no TLS."

View file

@ -0,0 +1,14 @@
moved {
from = google_cloud_run_v2_service_iam_member.gateway_allusers
to = google_cloud_run_v2_service_iam_member.gateway_allusers[0]
}
moved {
from = google_cloud_run_v2_service_iam_member.backend_allusers
to = google_cloud_run_v2_service_iam_member.backend_allusers[0]
}
moved {
from = google_cloud_run_v2_service_iam_member.ui_allusers
to = google_cloud_run_v2_service_iam_member.ui_allusers[0]
}

View file

@ -155,6 +155,13 @@ variable "migrations_image" {
default = ""
}
# ---------- Load balancer auth mechanism ----------
variable "invoker_iam_disabled" {
description = "Disable the Cloud Run invoker IAM check. When true, the allUsers grant is skipped. Enable if the environment implements the DomainRestrictedSharing policy."
type = bool
default = null
}
# ---------- Service sizing ----------
variable "gateway_cpu" {