From 82034588e9030190c63495746799e80c9a9f4a1b Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Sat, 30 May 2026 12:12:19 +0400 Subject: [PATCH] fix(gke): plumb UseSSL through GKE Autopilot deploy path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GKE Autopilot cloud-compose deploy path in `pkg/clouds/pulumi/gcp/gke_autopilot_stack.go` was building a `kubernetes.Args{}` struct without setting `UseSSL`, so it landed at the Go bool zero value (`false`). The per-stack Caddyfile rendering in `simple_container.go:707-708` then never emitted `import hsts`: if args.UseSSL { imports = append(imports, "import hsts") } Effect: every GKE Autopilot deploy silently omitted `import hsts` from its rendered Caddyfile entry, so the parent Caddy's `(hsts)` snippet never fired — no HSTS site-level header, no HTTP→HTTPS redirect — even for stacks declaring `lbConfig.https: true` in their stack yaml. The Cloudrun deploy path (`kube_run.go:102`) was already correctly setting `args.UseSSL`, so this is a GKE-specific asymmetry. Verified by reading a live K8s Service annotation pre-fix: `import gzip` + `import handle_static` were present, `import hsts` missing. After fix (validated via a preview build of this branch), `import hsts` appears on the same Service. Fix: derive `useSSL` from the consumer's `lbConfig.https` setting (the existing per-stack yaml field that already declares "this stack is HTTPS-fronted") and pass it to `kubernetes.Args.UseSSL`. Same field also determines the site-block protocol in the same rendering pass (`proto = lo.If(lbConfig.Https, "https").Else("http")`), so the two signals stay aligned. Stacks not setting `lbConfig.https: true` continue to get no HSTS — matching today's behaviour exactly for the non-HTTPS case. No new schema fields; no new template-level knobs. One signal, one behaviour: declare HTTPS at the consumer level, get HSTS automatically. Two SC outputs gated by `args.UseSSL` in the kubernetes.Args contract that this PR therefore turns on for HTTPS GKE stacks: 1. `import hsts` in the per-stack Caddyfile entry → the parent Caddy's `(hsts)` snippet sets Strict-Transport-Security site-level (covers ALL response paths including static assets + error pages, not just proxied responses) AND redirects HTTP→HTTPS on X-Forwarded-Proto: http. 2. `ingress.kubernetes.io/ssl-redirect: false` annotation on the Ingress (Caddy owns the redirect, so the ingress shouldn't). Both already correctly fire on the Cloudrun path; this fix brings GKE to parity. No tests added: the existing `TestCaddyfileEntry_*` suite covers the rendering once `args.UseSSL` is true. The derivation itself is a single expression too small to need its own unit test — end-to-end via SC preview pipeline is the meaningful verification. Signed-off-by: Dmitrii Creed --- pkg/clouds/pulumi/gcp/gke_autopilot_stack.go | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pkg/clouds/pulumi/gcp/gke_autopilot_stack.go b/pkg/clouds/pulumi/gcp/gke_autopilot_stack.go index 34cf7070..350bb448 100644 --- a/pkg/clouds/pulumi/gcp/gke_autopilot_stack.go +++ b/pkg/clouds/pulumi/gcp/gke_autopilot_stack.go @@ -147,6 +147,30 @@ func GkeAutopilotStack(ctx *sdk.Context, stack api.Stack, input api.ResourceInpu params.Log.Info(ctx.Context(), "🔍 DEBUG: gkeAutopilotInput.Deployment.Affinity is nil") } + // UseSSL gates two SC outputs in the kubernetes.Args contract: + // 1. `import hsts` in the per-stack Caddyfile entry → the parent + // Caddy's `(hsts)` snippet then sets Strict-Transport-Security + // site-level AND redirects HTTP→HTTPS on X-Forwarded-Proto: http. + // 2. The `ingress.kubernetes.io/ssl-redirect: false` annotation on + // the Ingress (Caddy owns the redirect, so the ingress shouldn't). + // + // We derive it from `lbConfig.https` (a per-stack yaml field that the + // consumer already sets when their stack is HTTPS-fronted) rather than + // introducing a new template-level knob — same signal, fewer concepts. + // Net behaviour: stacks declaring `lbConfig.https: true` opt into the + // HSTS snippet; everything else stays plain HTTP and the snippet stays + // inert (its `redir @httpReq` would otherwise loop on origins without + // a TLS cert). + // + // Pre-this-fix, the field was unset on the kubernetes.Args struct, so + // it landed at the bool zero value and `import hsts` was never added — + // silently dropping HSTS on every GKE Autopilot deploy regardless of + // the consumer's lbConfig.https setting. + var useSSL bool + if sc := gkeAutopilotInput.Deployment.StackConfig; sc != nil { + useSSL = lo.FromPtr(sc.LBConfig).Https + } + kubeArgs := kubernetes.Args{ Input: input, Deployment: gkeAutopilotInput.Deployment, @@ -154,6 +178,7 @@ func GkeAutopilotStack(ctx *sdk.Context, stack api.Stack, input api.ResourceInpu Params: params, KubeProvider: kubeProvider, ComputeContext: params.ComputeContext, + UseSSL: useSSL, GenerateCaddyfileEntry: domain != "", Annotations: map[string]string{ "pulumi.com/patchForce": "true",