Repository navigation
feat: portal gateway mode - #45
Merged
Merged
Conversation
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.
Problem
TSLink runs one tsnet node per app, and a Tailscale device share covers one device. Someone outside the owner's tailnet therefore needs one share per app, plus one for the portal, and every new app means another invitation. In practice a guest who receives several invitations accepts one and cannot reach the rest.
Change
Portal gateway mode: the guest accepts one share, of the portal node, and the owner then decides which apps each person can use without any further action from the guest.
tslink portal enable --gateway [--gateway-ports] [--gateway-tag]makes the portal node persistent (a non-ephemeral key is requested before minting; existing ephemeral enrolment is reset through a validated helper and a marker proves persistent enrolment) and records its NodeID with ownership proof.:443stays the per-visitor directory.tslink portal route add|remove|listserves a private HTTP or file app on its own TLS port of the portal node, at its root path. Ports come from a range (default 8443-8499) that skips the Fetch Standard bad ports, 80 and 443, and stay stable across restarts. Route changes hot-reload only that listener.ts.netis a public suffix), with a per-route--allow-cross-originopt-out.X-Forwarded-Hostwith the port,Location/Content-Location/Refreshrewritten to the route origin, offline and deny pages that reveal neither upstream addresses nor apps the caller cannot see.tslink add --portal-onlyregisters an app with no node of its own.tslink add --remote https://<node>.<tailnet>.ts.netserves an app on another host: the target is resolved only from the gateway node's own peer list and dialled only through its netstack, never the host network or host resolver, and only while the gateway is tagged; remote upstreams receive no visitor identity headers. Portal-only apps, local and remote, get periodic health probes.people add|update --invitesends at most one device invite per person, for the gateway node; grant changes never send or revoke it; a stale invite left by a gateway identity change can be retired explicitly.portal_enablegains gateway options; newportal_route_add,portal_route_removeandportal_route_list;addgainsportal_only,remoteandportal_port. Over HTTP every route-changing path checks the current portal owner inside the registry write transaction.docs/portal.mdand the changelog).tslink invite listno longer fails when the user-invite list is JSONnull;scripts/systemd-e2e.shanchors on the currentverifySystemdServiceRunning(ctx context.Context)signature again.Docs:
docs/portal.md,docs/people.md,docs/architecture.md,docs/sharing.md,docs/roadmap.md,docs/cli-reference.md,SECURITY.md, and a dated0.2.0changelog section.Validation and evidence
-raceon 9 packages, 6,707 passes and no races; fullscripts/check.shexit 0; 82 mutants, 77 killed, 5 survivors explained as redundant guards.Dial(which can fall back to the host network and resolver), route teardown under the routes lock combined with a lock-taking admission call, credentialed upgrade and persistent restart, dashboard audience scoping, isolation from unrelated invalid services, and an unknown visitor with no people configured.-raceon the changed packages, 5,886 passes and no races; fullscripts/check.shexit 0; 48 mutants, 42 killed, 4 survivors now killed by the round 5 tests, 2 explained as redundant guards; every earlier severe fix re-injected and caught. Round 5 adds only tests and documentation.scripts/check.sh -racepasses; the service-manager guard recorded no real service-manager calls;goreleaser checkpasses.scripts/systemd-e2e.shunmodified givesRESULT: OKwith real systemd supervision, and its negative control fails as designed; a 19-step staged CLI smoke passes.Remaining platform coverage