@caelo-cms/provisioning 0.3.12 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caelo-cms/provisioning",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
6
  "description": "Pulumi-driven provisioning for Caelo CMS — `bunx @caelo-cms/provisioning --provider <self-hosted|gcp|aws|azure>` brings up a TLS-served install with Postgres + storage + secrets manager + the admin + the API gateway.",
@@ -38,7 +38,7 @@
38
38
  "prepublishOnly": "tsc -b --force && tsc --project stacks/gcp/tsconfig.json && tsc --project stacks/gcp-firebase/tsconfig.json"
39
39
  },
40
40
  "dependencies": {
41
- "@caelo-cms/shared": "^0.3.12",
41
+ "@caelo-cms/shared": "^0.3.13",
42
42
  "@clack/prompts": "1.3.0",
43
43
  "@pulumi/aws": "^7",
44
44
  "@pulumi/awsx": "^3",
@@ -465,32 +465,35 @@ const adminSvc = cloudRunService({
465
465
  // to proxy traffic via `rewrites` to Cloud Run. Grant it
466
466
  // `roles/run.invoker` on the gateway so /api/** requests succeed.
467
467
  //
468
- // v0.3.12 — The Firebase Hosting service identity SA
469
- // (`service-<projectnum>@gcp-sa-firebasehosting.iam.gserviceaccount.com`)
470
- // is created LAZILY by Firebase Hosting itself on first deploy
471
- // with a Cloud Run rewrite. There's no public API that materializes
472
- // it on demand — `gcp.projects.ServiceIdentity` and
473
- // `gcloud beta services identity create` both return
474
- // IAM_SERVICE_NOT_CONFIGURED_FOR_IDENTITIES.
468
+ // v0.3.13 — Gateway is PUBLIC by design.
475
469
  //
476
- // Cloud Run v1 IAM (`gcp.cloudrun.IamMember`) eagerly validates SA
477
- // existence at bind time → rejects with "SA does not exist".
478
- // PROJECT-level IAM (`gcp.projects.IAMMember`) does NOT validate —
479
- // it accepts deferred google-managed principals. Binding
480
- // `roles/run.invoker` at the project level grants the Firebase
481
- // Hosting SA invoke rights on every Cloud Run service in the
482
- // project (admin + gateway). Admin is gated by IAP, so this
483
- // effectively grants Firebase invoke-gateway, which is the
484
- // intended behavior.
485
- const projectInfo = gcp.organizations.getProjectOutput({ projectId: project }, opts);
486
- const firebaseHostingSa = pulumi.interpolate`serviceAccount:service-${projectInfo.number}@gcp-sa-firebasehosting.iam.gserviceaccount.com`;
487
-
488
- new gcp.projects.IAMMember(
489
- `${namePrefix}-gateway-firebase-invoker`,
470
+ // Per the gcp-firebase architecture decision: no Cloud Armor on
471
+ // the gateway; in-app rate limits + CAPTCHA on form submission
472
+ // endpoints gate abuse. The original plan was to grant the
473
+ // Firebase Hosting SA `roles/run.invoker` so Firebase rewrites
474
+ // could call the gateway as that SA. But the gcp-sa-firebasehosting
475
+ // SA is created lazily by Firebase on first Cloud-Run-rewrite
476
+ // deploy — there's no public API that materializes it
477
+ // (GenerateServiceIdentity, ServiceIdentity, `gcloud beta services
478
+ // identity create`, project-level IAMMember all return
479
+ // IAM_SERVICE_NOT_CONFIGURED_FOR_IDENTITIES or
480
+ // SA-does-not-exist).
481
+ //
482
+ // The cleaner architecture: skip the SA-specific binding and make
483
+ // the gateway publicly invokable (`allUsers`). Same effective
484
+ // behavior — Firebase rewrites work, and so do direct *.run.app
485
+ // hits (which were already possible without Cloud Armor). The
486
+ // in-app rate-limit middleware + form CAPTCHA + per-endpoint
487
+ // authentication remain the real security boundary, exactly as
488
+ // designed.
489
+ new gcp.cloudrunv2.ServiceIamMember(
490
+ `${namePrefix}-gateway-public-invoker`,
490
491
  {
491
492
  project,
493
+ location: region,
494
+ name: gatewaySvc.name,
492
495
  role: "roles/run.invoker",
493
- member: firebaseHostingSa,
496
+ member: "allUsers",
494
497
  },
495
498
  opts,
496
499
  );
@@ -543,12 +546,14 @@ const adminDomainMapping = provisionAdminDomain
543
546
  // shipped. If your provider build is older than v8.x, run
544
547
  // `gcloud beta run services update <admin-service> --iap` as a
545
548
  // post-up step. The stack provisions the IAM bindings either way.
546
- // v0.3.10 — Cloud Run v1 IAM API (`gcp.cloudrun.IamMember`) rejects
547
- // `roles/iap.httpsResourceAccessor` with "Role is not supported for
548
- // this resource". The v2 IAM API (`gcp.cloudrunv2.ServiceIamMember`)
549
- // is the right binding for native-IAP-on-Cloud-Run, matching
550
- // Google's documented setup: `gcloud run services
551
- // add-iam-policy-binding ... --role=roles/iap.httpsResourceAccessor`.
549
+ // v0.3.13 — For IAP-protected Cloud Run, the correct IAM role
550
+ // for allowlisted-user access is `roles/run.invoker`, NOT
551
+ // `roles/iap.httpsResourceAccessor` (Cloud Run IAM rejects that
552
+ // role on both v1 and v2 APIs with "Role is not supported for
553
+ // this resource"). IAP authenticates the user via Google; after
554
+ // auth passes, IAP forwards the request to Cloud Run, which
555
+ // checks `run.invoker` on the authenticated identity. So
556
+ // granting `run.invoker` to the user email IS the IAP allowlist.
552
557
  for (const principal of iapAllowlist) {
553
558
  new gcp.cloudrunv2.ServiceIamMember(
554
559
  `${namePrefix}-admin-iap-${principal.replace(/[^a-z0-9]/gi, "-").slice(0, 40)}`,
@@ -556,7 +561,7 @@ for (const principal of iapAllowlist) {
556
561
  location: region,
557
562
  project,
558
563
  name: adminSvc.name,
559
- role: "roles/iap.httpsResourceAccessor",
564
+ role: "roles/run.invoker",
560
565
  member: principal,
561
566
  },
562
567
  opts,