Repository navigation
Conversation
Unit tests for the token store's pin, authenticateUplink's worker-mismatch, the gateway's refusal and the 403 on the upgrade; an e2e for a second worker with a used token. Fleet tests give each worker its own join token.
The gateway pins a worker join token to the first worker id it accepts (tokens v2: worker_id, unique). Another id on that token, or a pinned id on another token, is refused as worker-mismatch (HTTP 403, worker.rejected). token.list shows workerId. Protocol 55.
Contributor
Author
Review (maintainer's agent, round 1)No blocking findings.
Notes for the maintainer, no change needed in this PR:
|
# Conflicts: # src/contract/AGENTS.md # src/contract/protocol.ts # src/daemon-protocol/index.test.ts # src/daemon/AGENTS.md
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A gateway trusted the worker id a dial claimed in
x-simlock-worker-id, and a new uplink under a live id replaced the old link. So one leakedworkerjoin token let anyone take over any worker's link and its relayed traffic.Now the gateway pins a join token to the first worker id it accepts. Another worker id on that token is refused as
worker-mismatch(HTTP 403,worker.rejected). A worker id that another live token is pinned to is refused the same way, so a fresh token cannot take over a paired worker either. To re-pair, runtoken revokethentoken create.Behaviour change for existing fleets: an existing join token gets pinned to the first worker that reconnects after the upgrade. Workers that share one token today will see all but one refused. The docs already said one token per worker.
Changes
src/auth/token-store.ts: tokens owner version 2 addsworker_id TEXTwith a unique index.pinWorker(id, workerId)runs in one transaction (UPDATE OR IGNORE … WHERE worker_id IS NULL, then a re-read), so of two first joins at once only one wins.TokenRecord.workerId.src/auth/index.ts:authenticateUplink(credential, claimedWorkerId)returnsworker-mismatchfor an empty or missing id, an id other than the pinned one, or an id another token holds.src/ports/uplink.ts,uplink-websocket.ts: new outcomeworker-mismatch; the upgrade answers 401 only forunauthenticated, and 403 for the other refusals.src/gateway/service.ts,src/daemon/main.ts: the claimed worker id is passed toauthenticate.worker.rejected.reasongainsworker-mismatch(WorkerRejectedReasoninsrc/bus). The rule that a new uplink replaces the old one under a live id stays; it now applies only with the token pinned to that id.tokenRecordSchemagainsworkerId?, sotoken.list/GET /v1/tokens/simlock token listshow it.PROTOCOL_VERSION_RANGEgoes from 58 to 59.gateway/joining-workers(new "Pair a token with its worker" section, the 403 log row),gateway/access-control,gateway/managing-workers,gateway/alerts, thetokenCLI reference. Manuals:src/auth,src/gateway(removes the known gap), the protocol value insrc/contractandsrc/daemon.mintJoinTokenine2e/helpersand in the console fixtures, whichstartFleet,joinOldWorkerand the usage spec use).Tests
src/auth/token-store.test.ts: pin once and listworkerId; a version 1 table moves up with its tokens unpinned.src/auth/auth.test.ts: the first id is pinned, the same id is accepted again, another id or an empty one getsworker-mismatch; of two first joins at once, one is accepted; an id pinned to another token getsworker-mismatchuntil that token is revoked.src/gateway/service.test.ts:worker-mismatchadded to the refusal table. Worker B with A's token is refused with oneworker.rejected(worker-mismatch), A's link is untouched, and A's own redial still replaces its link.src/ports/uplink-websocket.test.ts: 401 forunauthenticated, 403 forforbiddenandworker-mismatch.e2e/gateway-fleet.test.ts: a second worker with a used token getsworker-mismatch, andtoken listshows the first worker's id.pnpm test:changedpassed.pnpm test:e2eon the six gateway e2e files passed (41 tests). The console lane in Chromium passed, except for the known flaky test in Console e2e: a worker is drained from Fleet with no dialog, and Undo undrains it fails on the row's drained text #886. The website build andcheck-openapipassed.AND worker_id IS NULLfails the pin and race tests; dropping the unique index fails the other-token test.Assumptions
worker-mismatch, enforced by a unique index ontokens.worker_id. Without it, a leaked unused token could pin itself to a paired worker's id and replace that worker's link. I chose the stricter option. The cost: to give a worker a new token, revoke the old one first. The docs say so.worker-mismatch. A missing header was already a 400.unauthenticatedandforbiddenasworker.rejectedreasons. I did not edit the accepted ADR. The new reason is recorded indocs/internal/EVENTS.mdand in the manuals.Written by an agent.