Skip to content

streamlocal: stage reverse forwarding sockets safely - #13

Open
parsnips wants to merge 1 commit into
tailscale:masterfrom
parsnips:secure-streamlocal-bind
Open

streamlocal: stage reverse forwarding sockets safely#13
parsnips wants to merge 1 commit into
tailscale:masterfrom
parsnips:secure-streamlocal-bind

Conversation

@parsnips

@parsnips parsnips commented Sep 3, 2026

Copy link
Copy Markdown

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 ./...
  • Cross-compiled darwin/amd64, darwin/arm64, freebsd/amd64, openbsd/amd64, linux/amd64, linux/386, plan9/amd64, windows/amd64, android/arm64, netbsd/amd64, and dragonfly/amd64
  • Tailscale TestReverseUnixForwarding integration test in the Ubuntu container

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
parsnips force-pushed the secure-streamlocal-bind branch from e87d739 to e099dbd Compare September 3, 2026 22:35
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
parsnips force-pushed the secure-streamlocal-bind branch from e099dbd to 994440b Compare September 3, 2026 22:47
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
parsnips force-pushed the secure-streamlocal-bind branch from 994440b to 0feaa56 Compare September 3, 2026 22:51
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
parsnips force-pushed the secure-streamlocal-bind branch from 0feaa56 to 1a63220 Compare September 3, 2026 23:00
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
parsnips force-pushed the secure-streamlocal-bind branch from 1a63220 to a144a96 Compare September 3, 2026 23:02
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
parsnips force-pushed the secure-streamlocal-bind branch 2 times, most recently from 1d3ea85 to d90f506 Compare September 3, 2026 23:17
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
parsnips force-pushed the secure-streamlocal-bind branch from d90f506 to 014f1f2 Compare September 3, 2026 23:32
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
parsnips force-pushed the secure-streamlocal-bind branch from 014f1f2 to 8564a82 Compare September 3, 2026 23:33
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>
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