Skip to content

Compare changes

Choose two branches to see what one has that the other does not, then open a pull request for it.

Open a pull request

2 commits

6 files+43−110/6 viewed
+1−0
1+worktrees/*
+4−1
66 "Bash(npx wrangler d1 migrations list:*)",
77 "Bash(npx wrangler secret put:*)",
88 "Bash(npx wrangler tail:*)",
9− "Bash(npm run deploy:*)"
9+ "Bash(npm run deploy:*)",
10+ "Bash(bash */ship-deployments.sh)",
11+ "Bash(CLOUDFLARE_API_TOKEN= npx wrangler:*)",
12+ "Bash(npx wrangler:*)"
1013 ]
1114 }
1215 }
+6−4
3535 - Application name: `sudo`.
3636 - Session duration: short, such as 8 hours.
3737 - Public hostname: `sudo.g1t.sh` (path empty, so it covers everything).
38−2. **Add a policy:** action *Allow*, include *Emails* → the owner's address
39− (the same addresses as `STAFF_EMAILS`). Add more staff here *and* in
40− `STAFF_EMAILS`; either one alone is not enough.
38+2. **Add a policy** (`g1t staff`): action *Allow*, include *Emails* → the
39+ owner's address, and *Emails ending in* → `g1t.sh` for everyone with a
40+ g1t address (the same entries as `STAFF_EMAILS`, where a domain is
41+ written `@g1t.sh`). Add more staff here *and* in `STAFF_EMAILS`; either
42+ one alone is not enough.
4143 3. Save, then open the application's **Overview** (or *Basic information*)
4244 and copy the **Application Audience (AUD) tag**.
4345 4. Find the **team domain** under **Zero Trust → Settings → Custom pages**
4850 "vars": {
4951 "ACCESS_TEAM_DOMAIN": "<team>.cloudflareaccess.com",
5052 "ACCESS_AUD": "<the AUD tag>",
51− "STAFF_EMAILS": "syntaqx@gmail.com"
53+ "STAFF_EMAILS": "syntaqx@gmail.com, @g1t.sh"
5254 }
5355 ```
5456
+14−0
66 authorize,
77 clearKeyCache,
88 isSameOrigin,
9+ isStaff,
10+ parseStaff,
911 readSettings,
1012 verifyAccessJwt,
1113 } from "./access.ts";
204206 assert.equal(isSameOrigin(post({ origin: "https://sudo.g1t.sh", "sec-fetch-site": "cross-site" })), false);
205207 assert.equal(isSameOrigin(post({})), false);
206208 });
209+
210+test("everyone at a staff domain is staff, and nobody at a lookalike", () => {
211+ const staff = parseStaff("syntaqx@gmail.com, @g1t.sh");
212+ assert.deepEqual(staff, ["syntaqx@gmail.com", "@g1t.sh"]);
213+ assert.equal(isStaff("syntaqx@gmail.com", staff), true);
214+ assert.equal(isStaff("ada@g1t.sh", staff), true);
215+ assert.equal(isStaff("ada@xg1t.sh", staff), false);
216+ assert.equal(isStaff("ada@g1t.sh.evil.com", staff), false);
217+ assert.equal(isStaff("someone@gmail.com", staff), false);
218+ assert.equal(isStaff("@g1t.sh", staff), false);
219+ assert.equal(isStaff("a@b@g1t.sh", staff), false);
220+});
+15−3
2626 teamDomain: string;
2727 /** The Access application's Audience (AUD) tag. */
2828 aud: string;
29− /** Lowercased staff emails. */
29+ /** Lowercased staff emails, and `@domain` for everyone at a domain. */
3030 staff: string[];
3131 };
3232
6262 return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.cloudflareaccess\.com$/.test(host) ? host : null;
6363 }
6464
65+/**
66+ * The staff list: emails, and `@g1t.sh` for everyone with an address at
67+ * that domain. Access proves the address belongs to whoever signed in.
68+ */
6569 export function parseStaff(raw: string): string[] {
6670 return raw
6771 .split(/[,\s]+/)
6872 .map((email) => email.trim().toLowerCase())
69− .filter((email) => /^[^@\s]+@[^@\s]+$/.test(email));
73+ .filter((email) => /^[^@\s]*@[a-z0-9.-]+\.[a-z]{2,}$/.test(email));
74+}
75+
76+/** Whether a signed-in email is on the staff list: exactly, or by its whole domain. */
77+export function isStaff(email: string, staff: string[]): boolean {
78+ const at = email.lastIndexOf("@");
79+ if (at <= 0 || email.indexOf("@") !== at) return false;
80+ const domain = email.slice(at);
81+ return staff.some((entry) => (entry.startsWith("@") ? entry === domain : entry === email));
7082 }
7183
7284 export type AccessClaims = {
234246 if (!verified.ok) return verified;
235247 const email = typeof verified.claims.email === "string" ? verified.claims.email.trim().toLowerCase() : "";
236248 if (!email) return { ok: false, reason: "token has no email" };
237− if (!settings.staff.includes(email)) return { ok: false, reason: "not staff", email };
249+ if (!isStaff(email, settings.staff)) return { ok: false, reason: "not staff", email };
238250 return { ok: true, email };
239251 }
240252
+3−3
1616 "services": [{ "binding": "BILLING", "service": "g1t-billing" }],
1717 "vars": {
1818 // The Zero Trust team domain, such as `g1t.cloudflareaccess.com`.
19− "ACCESS_TEAM_DOMAIN": "",
19+ "ACCESS_TEAM_DOMAIN": "syntaqx.cloudflareaccess.com",
2020 // The Access application's Audience (AUD) tag.
21− "ACCESS_AUD": "",
21+ "ACCESS_AUD": "5479fcf84130fc7ae68c207ae69a81b2aa17d97714443c6f1dddd058c530b8cf",
2222 // Who may use sudo, comma separated. Access lets them in; this
2323 // decides again, in case the Access policy is ever widened.
24− "STAFF_EMAILS": "syntaqx@gmail.com"
24+ "STAFF_EMAILS": "syntaqx@gmail.com, @g1t.sh"
2525 },
2626 "observability": { "enabled": true },
2727 "upload_source_maps": true