Skip to content

bug: the worker can require a signed, unexpired link for a clip or lineup file, and is told to only once the panel signs them - #644

Merged
lukepolo merged 3 commits into
mainfrom
bug/signed-clip-urls
Oct 15, 2026
Merged

lukepolo merged 3 commits into
mainfrom
bug/signed-clip-urls

Conversation

@lukepolo

@lukepolo lukepolo commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

The backblaze proxy can now refuse a file under clips/ unless its link carries a valid exp and sig from the api, so a private clip can no longer be fetched by anyone who has or guesses its URL.

  • The signing key is HMAC-SHA256(S3_SECRET, "5stack-file-url-v1"), the same key the api derives and signs with (5stackgg/api#596). The signature is checked with crypto.subtle.verify before the edge cache; a missing, expired or forged one gets a 403 with Cache-Control: no-store, and a key B2 could resolve into clips/ (dot segments, escapes, empty segments) is held to the same rule.
  • It is enforced only with REQUIRE_SIGNED_CLIPS=1. backblaze-proxy.sh turns that on only when the panel's database, asked through its kubeconfig, has a clip link function that signs and a stored key matching the worker's S3_SECRET (compared by hash).
  • Since a deploy replaces every var, the script first reads the worker's current bindings from Cloudflare. A worker that enforces keeps enforcing when the panel cannot be asked; turning it off takes an explicit yes (default no, names every panel on the worker), and no answer deploys nothing.
  • exp and sig stay out of the cache key, so every signed link to a file shares one edge copy; name, dl, ranges and view counting are unchanged, and keys outside clips/ are served as before.

Part of #544

…at has not expired

The backblaze proxy now refuses a file under clips/ unless its link carries a valid `exp` and `sig` from the api, so a private clip can no longer be fetched by anyone who has or guesses its URL.

- The signing key is HMAC-SHA256(S3_SECRET, "5stack-file-url-v1"), the same key the api derives and signs with.
- The signature is checked with crypto.subtle.verify before the edge cache; a missing, expired or forged one gets a 403 with Cache-Control: no-store.
- A key that B2 could resolve into clips/ (dot segments, escapes, empty segments) is held to the same signature.
- exp and sig stay out of the cache key, so every signed link to a file shares one edge copy; name, dl, ranges and view counting are unchanged.
- Keys outside clips/ (maps, news, events, demos) are served as before.

Part of #544
…is seen signing them

Enforcing in front of an api that does not sign, or signs with another S3_SECRET, refused every clip, and backblaze-proxy.sh deployed the worker with no check.

- The worker refuses unsigned clips/ links only when deployed with REQUIRE_SIGNED_CLIPS=1; otherwise it serves them as before.
- backblaze-proxy.sh sets it only when the panel's database (asked through its kubeconfig) has a clip link function that signs and a stored key matching the worker's S3_SECRET, compared by hash; otherwise it deploys without it and says why.
- Re-running the script after an update, a rollback or an S3_SECRET change re-decides, and the secrets example and wrangler.toml say when to.
- The worker is tested in both modes and the check against a stubbed kubectl; the workflow also runs for the script and its check.

Part of #544
@lukepolo lukepolo changed the title bug: the worker serves a clip or lineup file only on a signed link that has not expired bug: the worker can require a signed, unexpired link for a clip or lineup file, and is told to only once the panel signs them Oct 14, 2026
…without an explicit yes

A deploy replaces every var the worker has, so a run that could not reach the panel, or ran from a panel not yet signing links, silently dropped REQUIRE_SIGNED_CLIPS for every panel on the worker.

- The script reads the worker's current bindings from Cloudflare (GET /accounts/:account/workers/scripts/5stack/settings) before deciding.
- A worker that requires signed links keeps requiring them when the panel's database cannot be asked, and says so.
- When the panel does not sign, or signs with another S3_SECRET, turning it off takes a yes to a question that defaults to no and names every panel on the worker; no answer deploys nothing and exits non-zero. A worker whose state Cloudflare will not report is asked the same.
- Turning it on says every panel using the worker must already sign links.
- Tests cover the Cloudflare read, the worker on/off/unknown by panel state decision table with its answers, and the script block that applies it.

Part of #544
@lukepolo
lukepolo merged commit 7c4acc7 into main Oct 15, 2026
1 check passed
@lukepolo
lukepolo deleted the bug/signed-clip-urls branch October 15, 2026 00:19
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.

1 participant