You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 542e95c
Browse filesBrowse the repository at this point in the historyBrowse files
Manage the security-room Cloudflare Access policy from config via a security-team GitHub team (#195)
* Manage the security-room Cloudflare Access policy from config
The Cloudflare Zero Trust Access policy protecting
securityroom.modelcontextprotocol.io was hand-edited in the Cloudflare
dashboard and allowed four GitHub teams: core-maintainers,
lead-maintainers, security-managers and sdk-maintainers. Cloudflare
matches direct GitHub team membership only, so SDK maintainers, who sit
in child teams such as python-sdk, were denied.
Declare the policy in src/config/accessPolicies.ts and sync it with
@pulumi/cloudflare (src/cloudflare.ts, optional like the Discord module
until cloudflare:apiToken is configured). Add a dedicated `security-room`
GitHub team (no parent, so it grants no repository permissions) that the
policy allows alongside core-maintainers, lead-maintainers and
security-managers, and seed it with the per-SDK leads named in the
thread. Validation and tests cover the new config; the deploy and
preview workflows pass CLOUDFLARE_API_TOKEN when the secret exists.
The existing `Maintainers` policy is adopted via the `import` resource
option behind cloudflare:importExistingPolicies, since Pulumi requires
imported inputs to match the live resource; a follow-up flips the flag
off so the include-rule change is applied.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBarRtzEtwT2HvfcEvRbW7
* Rename security-room team to security-team
The team allowed through the Cloudflare Access policy represents the
MCP Security Team as a whole (SDK security leads who coordinate
advisories across SDKs), not only sign-in to the security room, so name
the role and GitHub team `security-team`. It stays parentless so it
inherits no repository permissions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBarRtzEtwT2HvfcEvRbW7
* Name the Cloudflare token for role management, not as a generic API token
The secret and Pulumi config key are dedicated to Access policy role
management (Account > Access: Apps and Policies > Edit only), so name
them CLOUDFLARE_ROLE_MANAGEMENT_TOKEN / cloudflare:roleManagementToken.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBarRtzEtwT2HvfcEvRbW7
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,6 +12,7 @@ Infrastructure as Code for managing access to MCP community resources using Pulu
12
12
-**Google Workspace Groups**: Automatically syncs group memberships for @modelcontextprotocol.io email accounts
13
13
-**Email Groups**: Groups with `isEmailGroup: true` accept emails from anyone (including external users) and notify all members. External posts are moderated for security.
14
14
-**Google Workspace User Accounts**: Provisions @modelcontextprotocol.io accounts for members of roles with `provisionUser: true` (directly, or via a role nested under one through `github.parent` — e.g. SDK teams under `sdk-maintainers`, working groups under `working-groups`)
15
+
-**Cloudflare Access (security-room)**: Syncs the Cloudflare Zero Trust Access policy that decides who can sign in to `securityroom.modelcontextprotocol.io` from the roles declared in [`src/config/accessPolicies.ts`](src/config/accessPolicies.ts). See [Cloudflare Access (security-room)](#cloudflare-access-security-room) below.
15
16
-**npm & PyPI Package Publishing Access** (declared, not applied): Expected registry access is declared in [`src/config/packageAccess.ts`](src/config/packageAccess.ts) and drift against the live npm registry is detected by CI — but changes are applied manually by a maintainer. See [npm & PyPI Package Publishing Access](#npm--pypi-package-publishing-access) below for why and how.
16
17
17
18
### Opting in to a Google Workspace account (maintainers)
@@ -31,6 +32,24 @@ If you're a maintainer — explicitly or implicitly (SDK maintainers, working gr
31
32
32
33
Once merged, Pulumi provisions the account. An admin will share your initial password (retrievable via `pulumi stack output --show-secrets newGWSUserPasswords`).
33
34
35
+
## Cloudflare Access (security-room)
36
+
37
+
[securityroom.modelcontextprotocol.io](https://securityroom.modelcontextprotocol.io) is protected by Cloudflare Zero Trust Access with GitHub as the identity provider. The reusable Access policy `Maintainers` that grants sign-in is managed from this repo by [`src/cloudflare.ts`](src/cloudflare.ts), driven by [`src/config/accessPolicies.ts`](src/config/accessPolicies.ts):
38
+
39
+
- Each entry in `ACCESS_POLICIES` lists the roles (from `roles.ts`) whose GitHub team may sign in. Pulumi renders one `github-organization` include rule per team on the policy. Nothing else about the Access application (domain, identity providers, session settings) is managed here.
40
+
-**Cloudflare matches direct team membership only.** A member of `python-sdk` does not satisfy a rule for its parent team `sdk-maintainers`. That is why the policy allows the `security-team` team (the `SECURITY_TEAM` role: the MCP Security Team of SDK security leads, which has no parent team and grants no repository permissions) alongside `core-maintainers`, `lead-maintainers` and `security-managers`.
41
+
-**To add someone to the MCP Security Team** (and grant sign-in): add `ROLE_IDS.SECURITY_TEAM` to their `memberOf` in [`src/config/users.ts`](src/config/users.ts). **To allow another team**: add its role to the policy's `roles` in `accessPolicies.ts` (validation checks the role exists and has a GitHub team).
42
+
- After access is granted, a user who was previously denied must revoke the "Cloudflare Access" OAuth app under their GitHub settings (Applications → Authorized OAuth Apps) and sign in again, otherwise Cloudflare keeps using the cached team list from their earlier login.
43
+
44
+
### One-time setup
45
+
46
+
1. In the Cloudflare dashboard for the **MCP Domain Account**, create a token dedicated to Access policy role management. Give it a descriptive name so it does not read as a generic API token, e.g. `mcp-access: Access policy role management`, and scope it to only **Account → Access: Apps and Policies → Edit** on the MCP Domain Account. Do not reuse this token for anything else.
47
+
2. Add it as the GitHub Actions secret `CLOUDFLARE_ROLE_MANAGEMENT_TOKEN` in the `production` environment (repository settings → Environments → production). The deploy workflow passes it to Pulumi as `cloudflare:roleManagementToken`. Until the secret exists, the Cloudflare module logs "Cloudflare integration disabled: roleManagementToken not configured" and creates nothing, so previews stay green.
48
+
3. The account ID and GitHub identity-provider ID are non-secret and live in [`Pulumi.prod.yaml`](Pulumi.prod.yaml).
49
+
4.**Adopting the existing policy.** Pulumi's `import` resource option only succeeds when the program's inputs match the live resource, so adoption is two deploys:
50
+
- With `cloudflare:importExistingPolicies: "true"` in `Pulumi.prod.yaml`, the first deploy imports the existing `Maintainers` policy (by its `cloudflarePolicyId`) as-is, ignoring its rule lists.
51
+
- Then set the flag to `"false"` in a follow-up PR; its preview shows exactly the include-rule changes that the next deploy applies. Leave the flag off from then on.
52
+
34
53
## npm & PyPI Package Publishing Access
35
54
36
55
Publishing access to the `modelcontextprotocol` npm organization and to the MCP PyPI projects is **config-as-code with human-applied changes** — deliberately outside the Pulumi resource graph:
@@ -94,6 +113,9 @@ The following secrets must be configured in GitHub Actions for automated deploym
94
113
- Used to decrypt encrypted values in Pulumi stack configuration
95
114
- Keep this secure - if lost, you cannot decrypt your Pulumi state
96
115
116
+
-**`CLOUDFLARE_ROLE_MANAGEMENT_TOKEN`** (optional, `production` environment): Cloudflare token dedicated to Access policy role management, scoped only to **Account → Access: Apps and Policies → Edit** on the MCP Domain Account (not a general-purpose API token)
117
+
- Used to manage the Cloudflare Access policy for `securityroom.modelcontextprotocol.io` (see [Cloudflare Access (security-room)](#cloudflare-access-security-room))
118
+
97
119
## Initial Setup
98
120
99
121
If setting up this infrastructure for the first time:
0 commit comments