streamlocal: stage reverse forwarding sockets safely - #13
Open
parsnips wants to merge 1 commit into
Open
Conversation
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Pass the authenticated user's UID and GID to gliderssh so it can set ownership while the socket is protected inside its staging directory. Remove the post-bind path-based chown helpers. This depends on github.com/tailscale/gliderssh#13. Use the PR branch through a temporary module replacement until that change merges. Plan 9 still reaches gliderssh's unsupported-platform rejection before numeric user ID parsing.
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 22:35
e87d739 to
e099dbd
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 22:47
e099dbd to
994440b
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 22:51
994440b to
0feaa56
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 23:00
0feaa56 to
1a63220
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 23:02
1a63220 to
a144a96
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
2 times, most recently
from
September 3, 2026 23:17
1d3ea85 to
d90f506
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 23:32
d90f506 to
014f1f2
Compare
Create reverse-forwarding sockets inside a private staging directory. Apply permissions and optional ownership through a pinned directory descriptor, then move the socket into the pinned destination directory. This prevents users who can write to the destination directory from replacing the socket path while a privileged server changes its mode or owner. Signed-off-by: michael parsons <michael@twisp.com>
parsnips
force-pushed
the
secure-streamlocal-bind
branch
from
September 3, 2026 23:33
014f1f2 to
8564a82
Compare
parsnips
added a commit
to parsnips/tailscale
that referenced
this pull request
Sep 3, 2026
Reverse forwarding sockets are created by tailscaled, so their default ownership prevents the authenticated local user from connecting to them. Pass the user's UID and GID to gliderssh. It creates the socket in a private staging directory, sets its mode and owner through pinned directory descriptors, and then moves it into the requested directory. This prevents path replacement races during chmod and chown. Keep Plan 9's unsupported-platform rejection ahead of numeric UID and GID parsing, and reject IDs that cannot fit in int on 32-bit systems. This depends on github.com/tailscale/gliderssh#13. The temporary module replacement can be removed after that PR merges. Updates tailscale#21098 Signed-off-by: michael parsons <michael@twisp.com>
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.
Reverse-forwarding sockets can be created in user-writable directories. Configuring the socket by path after bind lets that user replace the path before a privileged chmod or chown.
Create each socket in a private 0700 staging directory beside the destination. Pin both directories with file descriptors, apply mode and optional ownership with descriptor-relative calls, then rename the socket into the pinned destination directory. This removes the path replacement window and also fixes the existing path-based chmod race.
The new BindOwner option lets privileged callers assign socket ownership before the socket becomes visible. Unsupported platforms retain the existing fallback behavior.
Tests:
go test ./...go vet ./...TestReverseUnixForwardingintegration test in the Ubuntu container