Skip to content

docs(cloud-security): GCP serverless scopes — Cloud Run + Cloud Functions - #329

Draft
maximelb wants to merge 3 commits into
masterfrom
solve/s2-gcp-serverless-scopes-s2gcprun
Draft

docs(cloud-security): GCP serverless scopes — Cloud Run + Cloud Functions#329
maximelb wants to merge 3 commits into
masterfrom
solve/s2-gcp-serverless-scopes-s2gcprun

Conversation

@maximelb

Copy link
Copy Markdown
Contributor

What was asked

Cloud Run and Cloud Functions inventory is now collected for Google Cloud connections, together
with a verdict on whether each workload can be invoked from the internet with no authentication.
Document the scopes that verdict needs — before a customer discovers them as a silent denial.

What was done

All in docs/cloud-security/provider-setup/gcp.md.

  • The required baseline already covers it. roles/viewer + roles/iam.securityReviewer
    grant both the list and the getIamPolicy reads, so nothing changes for a customer following
    the standard setup. Said explicitly, so the two new rows are not misread as new requirements.
  • Two optional rowsroles/run.viewer and roles/cloudfunctions.viewer — for the
    least-privilege alternative (roles/browser plus per-service viewers), where they are what
    turns serverless coverage on. Added to the setup script too.
  • Both halves of each grant are called out. Without getIamPolicy we can list a service but
    cannot tell whether anyone on the internet can invoke it, and we report no verdict rather
    than guess — so serverless exposure findings are absent, not empty, which is a different
    thing from "you have none".
  • The 2nd-gen trap. A 2nd-gen Cloud Function runs on Cloud Run and its invoker permission
    lives on the underlying Cloud Run service (roles/run.invoker), not on the function
    (roles/cloudfunctions.invoker is the 1st-gen role). So a grant with
    roles/cloudfunctions.viewer but not roles/run.viewer lists 2nd-gen functions while
    leaving every one of them without a public-access verdict.
  • New serverless row in the preflight-check table, and two troubleshooting rows — including
    the symptom that looks like good news: services listed, none ever flagged public, because
    getIamPolicy is denied.
  • run.googleapis.com + cloudfunctions.googleapis.com added to the enable list; the page intro
    now mentions serverless alongside compute.

Testing

  • Verified against the public Google Cloud IAM and Cloud Functions documentation: the permission
    names (run.services.list, run.services.getIamPolicy, cloudfunctions.functions.list,
    cloudfunctions.functions.getIamPolicy), the role names, and the 2nd-gen invoker behaviour
    (the Cloud Functions access-control page documents granting roles/run.invoker to allUsers
    via gcloud run services add-iam-policy-binding).
  • Markdown-only change: table rows plus three admonitions and two script lines. No numbered lists
    touched, so the list-numbering check is unaffected. mkdocs is not installed locally, so the
    rendered-site checks run in CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L7CkEf1fEa9Y8dBn5Utrx1

…ions

Cloud Run and Cloud Functions inventory is now collected, with a verdict on
whether each workload can be invoked from the internet with no authentication.
This documents the scopes that verdict needs, before anyone discovers them as a
silent denial.

- The required baseline (roles/viewer + roles/iam.securityReviewer) already
  covers it, so nothing changes for a customer following the standard setup;
  said explicitly so the two new rows are not read as new requirements.
- roles/run.viewer + roles/cloudfunctions.viewer are added to the OPTIONAL table
  (and the setup script) for the least-privilege alternative, where they are what
  turns serverless coverage on.
- Both halves of each grant are called out: without getIamPolicy we can list a
  service but cannot tell whether anyone on the internet can invoke it, and we
  report NO verdict rather than guess — so findings are absent, not empty, which
  is a different thing from "you have none".
- The 2nd-gen trap is documented: a 2nd-gen function's invoker permission lives
  on its underlying Cloud Run service (roles/run.invoker), not on the function
  (roles/cloudfunctions.invoker is 1st-gen), so a grant with
  cloudfunctions.viewer but not run.viewer lists 2nd-gen functions while leaving
  every one of them without a public-access verdict.
- New `serverless` row in the preflight-check table and two troubleshooting rows,
  including the symptom that looks like good news: services listed, none ever
  flagged public, because getIamPolicy is denied.
- run.googleapis.com + cloudfunctions.googleapis.com added to the enable list,
  and the page intro now mentions serverless.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L7CkEf1fEa9Y8dBn5Utrx1
lcbill
lcbill previously approved these changes Jul 30, 2026
Comment thread docs/cloud-security/provider-setup/gcp.md
@maximelb

Copy link
Copy Markdown
Contributor Author

Open finding (details inline):

  • gcp.md L102 + L266 — both describe "listed but no public-access verdict" when getIamPolicy is denied; the collector actually returns partial coverage on its only type, which suppresses the serverless inventory entirely (bulk.go skips uncovered sub-scopes for upserts too). Raised on the host PR; the copy needs to match whichever behaviour lands.

No public-repo hygiene issues found (no customer names, internal repo names, internal URLs, or org ids); the role/permission table and the gen2-run.invoker note match the collector.

A Cloud Run service set to "internal and Cloud Load Balancing" cannot be reached at
its own run.app URL but CAN be published to the internet through an external load
balancer in front of it. We do not collect load balancers yet, so it is inventoried
without being reported as internet-facing — a customer needs to know that, rather than
read the absence as safety.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L7CkEf1fEa9Y8dBn5Utrx1
lcbill
lcbill previously approved these changes Jul 30, 2026
Comment thread docs/cloud-security/provider-setup/gcp.md Outdated
The 2nd-gen note asserted that a grant with cloudfunctions.viewer but not
run.viewer leaves 2nd-gen functions without a verdict. The underlying fact is
solid — a 2nd-gen function's invoker permission lives on the Cloud Run service —
but whether roles/cloudfunctions.viewer already carries the Cloud Run reads is a
claim about predefined-role contents that could not be confirmed, and a doc should
not assert what it cannot check.

Reworded to state the observable mechanism and point at 'provider test' as the
authority: its serverless check exercises both APIs and reports each separately,
so an operator learns which half is missing instead of reasoning about role
contents. The least-privilege advice is unchanged (grant both), which is correct
either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L7CkEf1fEa9Y8dBn5Utrx1
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