Skip to content

Move the Terraform templates here and align their sizing - #60

Open
David Larsen (dc-larsen) wants to merge 3 commits into
mainfrom
terraform-templates
Open

David Larsen (dc-larsen) wants to merge 3 commits into
mainfrom
terraform-templates

Conversation

@dc-larsen

@dc-larsen David Larsen (dc-larsen) commented Oct 17, 2026 •

Copy link
Copy Markdown

Summary

Moves the three per-platform Terraform templates from the socketdev-demo org into this repo, so every Registry Firewall deployment template lives in one place. It also aligns their default sizing with the new Sizing and High Availability docs page (SocketDev/docs#146).

  • Move (commit 1, files unchanged):

    Source repo Commit New path
    socketdev-demo/socket-firewall-aws-ecs-fargate fb0eccb terraform/aws-ecs-fargate/
    socketdev-demo/socket-firewall-azure-container-apps 91ddad4 terraform/azure-container-apps/
    socketdev-demo/socket-firewall-gcp-cloud-run 7126b69 terraform/gcp-cloud-run/
  • Sizing defaults (commit 2):

    Template Before After
    ECS Fargate 2 tasks, 2 vCPU / 4 GB 3 tasks, 4 vCPU / 8 GB
    Azure Container Apps min 1 / max 5, 1.0 CPU / 2Gi min 3 / max 10, 4.0 CPU / 8Gi
    Cloud Run min 1 / max 10, 1 CPU / 512Mi min 3 / max 10, 4 CPU / 8Gi
    • All three generated socket.yml files now set nginx.worker_processes from the CPU allocation. Without it, auto counts the host's CPUs, so a 1-CPU Container Apps replica could start one worker per host core. They also set worker_connections: 4096, worker_rlimit_nofile: 8192, and socket.api_read_timeout: 60 (the default falls back to 300 s).
    • Liveness probe timeouts on Container Apps and Cloud Run go from 5 s to 10 s, so a busy replica isn't restarted.
    • The example tfvars carry the same defaults and add Redis sizing notes: Azure Cache Standard C2 or larger, ElastiCache cache.t4g.medium or larger, and Memorystore Standard 2 GB or larger. Each note also says to use non-cluster mode and volatile-lru.
  • Links (commits 2 and 3):

    • Template READMEs now link each other by relative path.
    • Each template README links the public docs instead of the private firewall repo.
    • The top-level README lists every template.
    • The CloudFormation README links the Terraform siblings.

Notes

  • Full history stays in the source repos. This repo's linear-history rule rules out a subtree merge. After this merges, I'll add a "moved" notice to each source repo's README and archive it.
  • Image defaults stay socketdev/socket-registry-firewall:latest, as before. The release bot doesn't pin these templates.
  • The sizing-guide links resolve once SocketDev/docs#146 merges.

Test plan

  • terraform fmt -check passes on all three templates

  • terraform init -backend=false && terraform validate passes on all three (run on throwaway copies)

  • terraform console gives the expected worker counts for the new expressions:

    Input Workers
    cpu = 4.0 (Azure) 4
    cpu = 0.5 (Azure) 1
    4096 CPU units (Fargate) 4
    256 CPU units (Fargate) 1
    "4" (Cloud Run) 4
    "500m" (Cloud Run) 1
  • CONFIG_FILE is honored by the image entrypoint (entrypoint.sh: ${CONFIG_FILE:-/app/socket.yml}), so the generated nginx block takes effect on Container Apps and Cloud Run

  • terraform apply in a sandbox subscription or project for each platform


Note

Medium Risk
Default scale and generated firewall config change what new applies provision; existing stacks only change on the next terraform apply if they rely on defaults, with higher cost and different nginx behavior.

Overview
Consolidates the AWS ECS Fargate, Azure Container Apps, and GCP Cloud Run Socket Registry Firewall Terraform modules into terraform/ under this repo, and updates the root and CloudFormation READMEs with a platform template table, sizing guidance, and cross-links between deployment options.

Raises baseline sizing on all three modules to match the enterprise sizing guide: 3 replicas/instances at 4 vCPU and 8 GiB each (up from smaller prior defaults). Example terraform.tfvars and variable defaults are updated accordingly, with Redis tier notes added where relevant.

Tightens generated socket.yml in each module: nginx.worker_processes is derived from allocated CPU (per-platform expression), plus worker_connections, worker_rlimit_nofile, and socket.api_read_timeout: 60. Azure and Cloud Run liveness probe timeouts increase to 10s to avoid restarts under load. Azure adds moved blocks for Key Vault secret resources that now use count.

Template READMEs link to each other, the Helm chart, CloudFormation, and public Socket docs instead of the old standalone repos.

Reviewed by Cursor Bugbot for commit ac8e307. Configure here.

…oud Run

Move the per-platform Terraform templates from the socketdev-demo org so every Registry Firewall deployment template lives in this repo. Files are unchanged in this commit; follow-up commits update links and sizing defaults.

Sources (socketdev-demo, copied verbatim):
- socketdev-demo/socket-firewall-aws-ecs-fargate @ fb0eccb -> terraform/aws-ecs-fargate
- socketdev-demo/socket-firewall-azure-container-apps @ 91ddad4 -> terraform/azure-container-apps
- socketdev-demo/socket-firewall-gcp-cloud-run @ 7126b69 -> terraform/gcp-cloud-run
Default to 3 replicas of 4 vCPU and 8 GiB, set nginx workers from the CPU allocation with 4096 connections and an 8192 open-file limit, set socket.api_read_timeout to 60, raise liveness probe timeouts to 10 s, add Redis sizing notes, and point READMEs at their new paths and the public docs.
Add a templates table covering Helm, CloudFormation, and the three Terraform templates, link the sizing guide, and link the Terraform siblings from the CloudFormation README.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Domain tokens corrupt package tarball URLs
    • Azure and ECS now set path_routing.domain to a single hostname and put extra names (including localhost) in allowed_domain so rewritten package URLs stay valid.
  • ✅ Fixed: Ingress protocol mismatches container port
    • Azure Container Apps ingress now targets HTTP port 8080, matching TLS termination at the platform instead of forwarding cleartext to the TLS listener on 8443.

Create PR

Or push these changes by commenting:

@cursor push 99bea5bac4
Preview (99bea5bac4)
diff --git a/terraform/aws-ecs-fargate/main.tf b/terraform/aws-ecs-fargate/main.tf
--- a/terraform/aws-ecs-fargate/main.tf
+++ b/terraform/aws-ecs-fargate/main.tf
@@ -49,8 +49,11 @@
     }
     path_routing = {
       enabled = true
-      domain  = "${aws_lb.firewall.dns_name} localhost"
-      routes  = local.routes
+      # Must be a single hostname. Multiple space-separated tokens corrupt
+      # the URLs the firewall rewrites into npm metadata responses.
+      domain         = aws_lb.firewall.dns_name
+      allowed_domain = ["localhost"]
+      routes         = local.routes
     }
   })
 }

diff --git a/terraform/azure-container-apps/main.tf b/terraform/azure-container-apps/main.tf
--- a/terraform/azure-container-apps/main.tf
+++ b/terraform/azure-container-apps/main.tf
@@ -45,6 +45,11 @@
     registry = name
   }]
 
+  # path_routing.domain is substituted into rewritten package URLs, so it
+  # must be one hostname. Extra tokens from var.domain (cert SANs) and
+  # localhost belong in allowed_domain.
+  domain_tokens = compact(split(" ", var.domain))
+
   socket_yml = yamlencode(merge(
     {
       ports = {
@@ -71,8 +76,12 @@
       }
       path_routing = {
         enabled = true
-        domain  = "${var.domain} localhost"
-        routes  = local.routes
+        domain  = local.domain_tokens[0]
+        allowed_domain = distinct(concat(
+          slice(local.domain_tokens, 1, length(local.domain_tokens)),
+          ["localhost"],
+        ))
+        routes = local.routes
       }
     },
     var.debug_logging_enabled ? {
@@ -366,8 +375,10 @@
 
   ingress {
     external_enabled = false
-    target_port      = 8443
-    transport        = "http"
+    # Ingress terminates TLS and forwards HTTP. Send that to the HTTP
+    # listener (8080); 8443 is nginx's TLS port and rejects cleartext.
+    target_port = 8080
+    transport   = "http"
 
     traffic_weight {
       percentage      = 100

You can send follow-ups to the cloud agent here.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit ac8e307. Configure here.

path_routing = {
enabled = true
domain = "${var.domain} localhost"
routes = local.routes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Domain tokens corrupt package tarball URLs

High Severity

path_routing.domain is built as a space-separated string that always appends localhost, and on Azure it can include extra Front Door hostnames as well. The firewall rewrites registry metadata using that whole value, so tarball URLs become invalid and npm install fails. The Cloud Run template already treats this field as a single hostname for that reason.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ac8e307. Configure here.

ingress {
external_enabled = false
target_port = 8443
transport = "http"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ingress protocol mismatches container port

High Severity

Container Apps ingress uses transport = "http" and target_port 8443, so Envoy forwards cleartext HTTP to the firewall's TLS listener. Client requests through the FQDN fail, while HTTPS probes still pass because they reach the container directly instead of going through ingress.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ac8e307. Configure here.

# ── Ingress (internal only) ─────────────────────────────────────────────

ingress {
external_enabled = false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With internal_load_balancer_enabled = true (line 282) and external_enabled = false here, the app ingress is scoped to the Container Apps environment, not the VNet. Microsoft's ingress table for an internal environment with internal app ingress: "Reachable only from other apps in the same environment. Clients elsewhere in the virtual network receive an HTTP 404 response." No variable in the template controls external_enabled and there is no environment-level HTTP route config, so nothing re-exposes it.

Combined with the http transport to the TLS listener on 8443 that Bugbot flagged at line 370, the revision reports healthy (the HTTPS probes at lines 471-502 hit the container directly) while nobody in the VNet can reach it, including the README's "Verify the deployment" step (README lines 99-105). Bugbot's autofix only moves target_port to 8080 and keeps external_enabled = false, so it does not clear the 404.

The ingress block is unchanged from upstream socketdev-demo at 91ddad4; the same upstream commit was already imported on migrate-azure-container-apps-terraform, where f8af311 (2026-09-05, "make ingress reachable from the VNet") sets external_enabled = true and target_port = 8080 for exactly this reason. This PR does not carry it.

Repro (documented behavior, not run here): apply with the three required inputs, then from a VM in the VNet run curl -k --resolve <fqdn>:443:<environment static IP> https://<fqdn>/health (the template creates no private DNS zone for the environment's default domain, so without --resolve the name does not resolve from the VM). The ingress answers 404 while az containerapp revision list shows the revision healthy.

routes = [for name, upstream in var.registries : {
path = "/${name}"
upstream = upstream
registry = name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registry = name passes the registries map key straight through as the firewall's ecosystem selector (same pattern at aws-ecs-fargate/main.tf:28 and gcp-cloud-run/main.tf:53). The config-tool dispatches on that string with no normalization and, for any value it does not recognize, emits its streaming fallback location (path_routing/__init__.py:781-783 at firewall main), whose only Lua calls are capture_trace_id(), check_bearer_token() and streaming_proxy.proxy_request(<upstream base URL>). No parser or Socket check is attached, so no SOCKET_DECISION line can be produced for the route, and the config-tool prints no warning.

The fallback also never forwards the client's path: proxy_request requests exactly the path of the URL it is handed, so every request on the route becomes GET /artifactory/api/npm/npm-remote upstream regardless of what the client asked for.

This README's own Artifactory example (lines 74-88, key "repository/npm-remote"), the comment at lines 33-40 and terraform.tfvars.example lines 38-42 all produce exactly that shape. The registry_overrides lookup from 7a55a02 on migrate-azure-container-apps-terraform handled this for the Azure module and is not in this PR; the AWS and GCP modules never had it.

Repro: run the firewall config-tool on the README's registries block and inspect the emitted location ~ ^/repository/npm-remote(/|$): it contains only the three calls above, versus npm_parser.proxy_metadata/proxy_tarball/proxy_default when the route has registry = "npm". End to end, npm install <pkg> --registry https://<fqdn>/repository/npm-remote fails to resolve the package while the deployment boots cleanly and the logs show no SOCKET_DECISION lines.

variable "firewall_image" {
description = "Docker image for the Socket Registry Firewall"
type = string
default = "socketdev/socket-registry-firewall:latest"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking. The image default is socketdev/socket-registry-firewall:latest here, in aws-ecs-fargate/variables.tf:56, gcp-cloud-run/variables.tf:21 and all three tfvars examples. The PR body notes this is unchanged, but the two templates already in this repo are pinned to 2.11.1 and chart-checks.yaml gates those pins; nothing gates terraform/, and this repo already pinned this same Azure template once for this reason (ae2dab1 on migrate-azure-container-apps-terraform, to 2.4.1).

Cloud Run resolves the tag to a digest when the revision is created; ECS does the same at the start of each service deployment (versionConsistency defaults to enabled on Fargate, which this template uses); Container Apps revisions are immutable and Microsoft advises against static tags like latest. So the running version is whatever latest pointed at when the last deployment or revision was created, and any later terraform apply that touches the task definition or template silently rolls to a different firewall build.

These templates already depend on firewall behavior that changed under them: since v1.1.345 (2026-06-26) the firewall rewrites package URLs to the configured path_routing.domain string instead of the request Host, which is what turned the two-token domain Bugbot flagged into broken tarball URLs.

Repro on ECS: curl -k https://<alb>/health returns SocketFirewall/<version> - Health OK; after a firewall release, run aws ecs update-service --force-new-deployment without changing firewall_image and curl again.

@Andre153 Andre Coetzee (Andre153) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sizing changes are sound: fmt/validate pass, the generated socket.yml keys all reach the firewall, and the worker expressions behave on edge inputs. Before merge, the Azure template needs the ingress fix and the registry-key mapping that already exist on migrate-azure-container-apps-terraform: the first makes the deployment unreachable from the VNet, the second makes the documented Artifactory example a route that serves nothing and scans nothing. The :latest default is out of line with the rest of the repo.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants