mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
Supports TLS for INTERNAL_MANAGED load-balancer deployments
This commit is contained in:
parent
9a5ba08c27
commit
5434dd8e1c
9 changed files with 100 additions and 41 deletions
|
|
@ -442,13 +442,15 @@ vpcaccess, compute, servicenetworking, storage, artifactregistry).
|
|||
|
||||
## TLS
|
||||
|
||||
TLS and `lb_domains` apply only when `load_balancing_scheme = "EXTERNAL_MANAGED"`.
|
||||
For `INTERNAL_MANAGED`, set `lb_domains = []` (or leave it unset) and `allow_plaintext_lb = true`.
|
||||
TLS is supported for both LB schemes, with different certificate inputs:
|
||||
|
||||
- `EXTERNAL_MANAGED`: set `lb_domains`; this module creates a Google-managed cert
|
||||
- `INTERNAL_MANAGED`: set both `lb_domains` and `certificate_manager_certificates`; this module uses your pre-existing Certificate Manager certificates
|
||||
|
||||
`terraform plan` refuses to provision an HTTP-only LB by default — TLS
|
||||
is the supported posture. Two paths:
|
||||
|
||||
**Production / staging — set `lb_domains`:**
|
||||
**Production / staging (INTERNAL_MANAGED) — set `lb_domains`:**
|
||||
|
||||
1. `terraform apply` once with `allow_plaintext_lb = true` (intentional
|
||||
chicken-and-egg escape hatch) to provision the LB and read the anycast
|
||||
|
|
@ -464,10 +466,22 @@ managed cert sits in `PROVISIONING` for ~15-60 min on first apply until
|
|||
DNS propagation completes — `gcloud compute ssl-certificates describe
|
||||
<tenant>-litellm-<env>-cert` shows the state.
|
||||
|
||||
**Production / staging (INTERNAL_MANAGED) — set both hostnames and cert refs:**
|
||||
|
||||
Set:
|
||||
|
||||
- `lb_domains = ["proxy.internal.example.com"]`
|
||||
- `certificate_manager_certificates = ["projects/<project>/locations/global/certificates/<name>"]`
|
||||
|
||||
Result: a 443 internal forwarding rule and HTTPS target proxy using the
|
||||
provided certificates. If exactly one of the two variables is set,
|
||||
`terraform plan` fails with a clear precondition error.
|
||||
|
||||
**Trial / dev — explicitly opt into HTTP-only:**
|
||||
|
||||
Set `allow_plaintext_lb = true` and leave `lb_domains = []`. Without the
|
||||
flag, plan fails with a clear error pointing at the precondition.
|
||||
Set `allow_plaintext_lb = true`, leave `lb_domains = []`, and leave
|
||||
`certificate_manager_certificates = []`. Without the flag, plan fails
|
||||
with a clear error pointing at the precondition.
|
||||
Intended for short-lived trial / dev stacks only.
|
||||
|
||||
## Using as a module
|
||||
|
|
|
|||
|
|
@ -81,7 +81,12 @@ gcloud secrets versions access latest \
|
|||
|
||||
## Going to TLS
|
||||
|
||||
If you picked `allow_plaintext_lb=true` to bootstrap but want HTTPS for real, point a DNS A record at the LB IP, then re-run terraform with `lb_domains` set and `allow_plaintext_lb` removed:
|
||||
If you picked `allow_plaintext_lb=true` to bootstrap but want HTTPS for real:
|
||||
|
||||
- EXTERNAL_MANAGED: point a DNS A record at the LB IP and set `lb_domains`
|
||||
- INTERNAL_MANAGED: set both `lb_domains` and `certificate_manager_certificates`
|
||||
|
||||
Then re-run terraform with `allow_plaintext_lb` removed:
|
||||
|
||||
```bash
|
||||
terraform apply \
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
},
|
||||
{
|
||||
"name": "allow_plaintext_lb",
|
||||
"description": "Skip TLS on the load balancer (HTTP-only). Set true for trial/dev. For production, leave false and add lb_domains to terraform.tfvars after the first apply",
|
||||
"description": "Skip TLS on the load balancer (HTTP-only). Set true for trial/dev. For EXTERNAL_MANAGED production, leave false and add lb_domains after bootstrap. For INTERNAL_MANAGED TLS, set both lb_domains and certificate_manager_certificates",
|
||||
"default": "true",
|
||||
"options": ["true", "false"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,11 +43,12 @@ module "litellm" {
|
|||
image_registry = var.image_registry
|
||||
image_tag = var.image_tag
|
||||
|
||||
load_balancing_scheme = var.load_balancing_scheme
|
||||
lb_domains = var.lb_domains
|
||||
allow_plaintext_lb = var.allow_plaintext_lb
|
||||
cloudsql_deletion_protection = var.cloudsql_deletion_protection
|
||||
gcs_force_destroy = var.gcs_force_destroy
|
||||
load_balancing_scheme = var.load_balancing_scheme
|
||||
lb_domains = var.lb_domains
|
||||
certificate_manager_certificates = var.certificate_manager_certificates
|
||||
allow_plaintext_lb = var.allow_plaintext_lb
|
||||
cloudsql_deletion_protection = var.cloudsql_deletion_protection
|
||||
gcs_force_destroy = var.gcs_force_destroy
|
||||
|
||||
proxy_config = var.proxy_config
|
||||
gateway_extra_env = var.gateway_extra_env
|
||||
|
|
|
|||
|
|
@ -27,10 +27,11 @@ env = "stage"
|
|||
|
||||
# TLS: provide DNS names already pointing at the LB IP for a Google-managed
|
||||
# cert. Without one, plan fails unless allow_plaintext_lb = true is set
|
||||
# explicitly (trial/dev only). INTERNAL_MANAGED is private-IP only and
|
||||
# should keep lb_domains empty.
|
||||
# explicitly (trial/dev only). INTERNAL_MANAGED TLS requires both
|
||||
# lb_domains and certificate_manager_certificates.
|
||||
# load_balancing_scheme = "EXTERNAL_MANAGED" # or "INTERNAL_MANAGED"
|
||||
# lb_domains = ["proxy.example.com"]
|
||||
# lb_domains = ["proxy.example.com"]
|
||||
# certificate_manager_certificates = ["projects/<project>/locations/global/certificates/<name>"]
|
||||
# allow_plaintext_lb = true
|
||||
|
||||
# Storage and database retention. Defaults are safe — destroy preserves
|
||||
|
|
|
|||
|
|
@ -90,7 +90,13 @@ variable "image_tag" {
|
|||
|
||||
# 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 in EXTERNAL_MANAGED mode. Empty → no TLS."
|
||||
description = "TLS hostnames. EXTERNAL_MANAGED creates a Google-managed cert; INTERNAL_MANAGED requires this plus certificate_manager_certificates. Empty -> no TLS."
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "certificate_manager_certificates" {
|
||||
description = "Pre-existing Certificate Manager certificate self_links for INTERNAL_MANAGED TLS."
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,12 @@
|
|||
# is rewritten to redirect HTTP→HTTPS via a redirect-only URL map.
|
||||
|
||||
locals {
|
||||
tls_enabled = length(var.lb_domains) > 0
|
||||
is_external = var.load_balancing_scheme == "EXTERNAL_MANAGED"
|
||||
is_internal = var.load_balancing_scheme == "INTERNAL_MANAGED"
|
||||
|
||||
external_tls_enabled = local.is_external && length(var.lb_domains) > 0
|
||||
internal_tls_enabled = local.is_internal && length(var.lb_domains) > 0 && length(var.certificate_manager_certificates) > 0
|
||||
tls_enabled = local.external_tls_enabled || local.internal_tls_enabled
|
||||
}
|
||||
|
||||
resource "google_compute_global_address" "lb" {
|
||||
|
|
@ -139,7 +142,7 @@ resource "google_compute_url_map" "this" {
|
|||
# target proxy when TLS is enabled; otherwise the regular path-routing
|
||||
# URL map is attached to the HTTP proxy and everything stays plaintext.
|
||||
resource "google_compute_url_map" "https_redirect" {
|
||||
count = var.create_runtime && local.tls_enabled && local.is_external ? 1 : 0
|
||||
count = var.create_runtime && local.tls_enabled ? 1 : 0
|
||||
name = "${local.name}-redirect"
|
||||
|
||||
default_url_redirect {
|
||||
|
|
@ -159,8 +162,11 @@ resource "google_compute_target_http_proxy" "this" {
|
|||
# Operators must either supply DNS names or explicitly opt in.
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = var.load_balancing_scheme != "INTERNAL_MANAGED" || length(var.lb_domains) == 0
|
||||
error_message = "INTERNAL_MANAGED does not support lb_domains/TLS in this module. Set load_balancing_scheme = \"EXTERNAL_MANAGED\" to use lb_domains, or leave lb_domains empty for INTERNAL_MANAGED."
|
||||
condition = !local.is_internal || (
|
||||
(length(var.lb_domains) == 0 && length(var.certificate_manager_certificates) == 0) ||
|
||||
(length(var.lb_domains) > 0 && length(var.certificate_manager_certificates) > 0)
|
||||
)
|
||||
error_message = "INTERNAL_MANAGED TLS requires both `lb_domains` and `certificate_manager_certificates` to be set (or both empty for HTTP-only)."
|
||||
}
|
||||
|
||||
precondition {
|
||||
|
|
@ -205,7 +211,7 @@ resource "google_compute_global_forwarding_rule" "http_internal" {
|
|||
# transitions to ACTIVE.
|
||||
|
||||
resource "google_compute_managed_ssl_certificate" "this" {
|
||||
count = var.create_runtime && local.tls_enabled && local.is_external ? 1 : 0
|
||||
count = var.create_runtime && local.external_tls_enabled ? 1 : 0
|
||||
|
||||
# A managed cert's `domains` is immutable, so changing var.lb_domains
|
||||
# forces replacement, and the cert is referenced by the HTTPS target
|
||||
|
|
@ -225,19 +231,29 @@ resource "google_compute_managed_ssl_certificate" "this" {
|
|||
}
|
||||
|
||||
resource "google_compute_target_https_proxy" "this" {
|
||||
count = var.create_runtime && local.tls_enabled && local.is_external ? 1 : 0
|
||||
name = "${local.name}-https"
|
||||
url_map = google_compute_url_map.this[0].id
|
||||
ssl_certificates = [google_compute_managed_ssl_certificate.this[0].id]
|
||||
count = var.create_runtime && local.tls_enabled ? 1 : 0
|
||||
|
||||
name = "${local.name}-https"
|
||||
url_map = google_compute_url_map.this[0].id
|
||||
ssl_certificates = local.is_external ? [google_compute_managed_ssl_certificate.this[0].id] : null
|
||||
certificate_manager_certificates = local.is_internal ? var.certificate_manager_certificates : null
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "https" {
|
||||
count = var.create_runtime && local.tls_enabled && local.is_external ? 1 : 0
|
||||
count = var.create_runtime && local.external_tls_enabled ? 1 : 0
|
||||
name = "${local.name}-https"
|
||||
ip_protocol = "TCP"
|
||||
port_range = "443"
|
||||
load_balancing_scheme = var.load_balancing_scheme
|
||||
ip_address = google_compute_global_address.lb[0].address
|
||||
target = google_compute_target_https_proxy.this[0].id
|
||||
labels = local.labels
|
||||
|
||||
# Configuration for Global External LB
|
||||
ip_address = local.is_external ? google_compute_global_address.lb[0].address : null
|
||||
|
||||
# Configuration for Global Internal LB
|
||||
network = local.is_internal ? google_compute_network.this[0].id : null
|
||||
subnetwork = local.is_internal ? google_compute_subnetwork.this[0].id : null
|
||||
|
||||
depends_on = [google_compute_subnetwork.managed_proxy]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
output "lb_ip" {
|
||||
description = "Load balancer IP. Global anycast for EXTERNAL_MANAGED, global private IP for INTERNAL_MANAGED. Null when create_runtime is false."
|
||||
value = var.create_runtime ? (
|
||||
value = var.create_runtime ? (
|
||||
var.load_balancing_scheme == "EXTERNAL_MANAGED" ? google_compute_global_address.lb[0].address : google_compute_global_forwarding_rule.http_internal[0].ip_address
|
||||
) : (
|
||||
) : (
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
output "lb_url" {
|
||||
description = "Proxy URL, or null when create_runtime is false. Switches scheme based on whether lb_domains is set; when TLS is enabled the URL points at the first listed domain (since managed certs are tied to the hostname, not the anycast IP). The dashboard is served at /, the API at /v1/*."
|
||||
description = "Proxy URL, or null when create_runtime is false. Switches scheme based on whether TLS is enabled; when TLS is enabled the URL points at the first domain in lb_domains. The dashboard is served at /, the API at /v1/*."
|
||||
value = var.create_runtime ? (local.tls_enabled ? (
|
||||
"https://${var.lb_domains[0]}"
|
||||
) : (
|
||||
format("http://%s", var.load_balancing_scheme == "EXTERNAL_MANAGED" ? google_compute_global_address.lb[0].address : google_compute_global_forwarding_rule.http_internal[0].ip_address)
|
||||
)) : (
|
||||
) : (
|
||||
var.load_balancing_scheme == "EXTERNAL_MANAGED" ? "https://${google_compute_global_address.lb[0].address}" : "https://${google_compute_global_forwarding_rule.http_internal[0].ip_address}"
|
||||
)) : (
|
||||
null
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -373,14 +373,30 @@ variable "db_username" {
|
|||
|
||||
variable "lb_domains" {
|
||||
description = <<-EOT
|
||||
DNS names for a Google-managed SSL certificate fronting the LB. When
|
||||
non-empty, the stack provisions a 443 forwarding rule + HTTPS target
|
||||
proxy + managed cert covering these domains, and the existing 80
|
||||
forwarding rule serves a permanent 301 redirect to HTTPS. Leave empty
|
||||
([]) to disable TLS (must combine with `allow_plaintext_lb = true` for
|
||||
the plan to succeed — see README.md "TLS"). Each domain must already
|
||||
resolve to the LB's anycast IP (`lb_ip` output) for managed-cert
|
||||
provisioning to succeed.
|
||||
DNS names fronting the LB when TLS is enabled.
|
||||
|
||||
EXTERNAL_MANAGED: when non-empty, the stack provisions a Google-managed
|
||||
SSL certificate covering these domains, creates a 443 forwarding rule,
|
||||
and rewrites port 80 to a permanent HTTPS redirect.
|
||||
|
||||
INTERNAL_MANAGED: when enabling TLS, `lb_domains` must be non-empty and
|
||||
`certificate_manager_certificates` must also be provided.
|
||||
|
||||
Leave empty ([]) to disable TLS (must combine with
|
||||
`allow_plaintext_lb = true` for the plan to succeed — see README.md
|
||||
"TLS").
|
||||
EOT
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "certificate_manager_certificates" {
|
||||
description = <<-EOT
|
||||
Pre-existing Certificate Manager certificate resource references for
|
||||
INTERNAL_MANAGED TLS. Provide full certificate self_links.
|
||||
|
||||
This input is ignored for EXTERNAL_MANAGED, where `lb_domains` continues
|
||||
to drive Google-managed SSL certificate creation in this module.
|
||||
EOT
|
||||
type = list(string)
|
||||
default = []
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue