1 Commits
Author SHA1 Message Date
holdmysocks 726b9b99c4 Only pipe connections addressed to the console; 0.5.2
The fallback handler piped a tailnet connection to localhost by its port
alone. Only connections to the console's own addresses reach it today, so
nothing was exposed, but the handler now checks the destination address
itself instead of relying on that.

The README explains why the console does not offer itself as an exit node.
2026-10-04 10:40:23 -04:00
5 changed files with 46 additions and 3 deletions

No files matched your search

+8 -1
View File
@@ -31,7 +31,14 @@ VPN here. It runs inside one process:
directly. They can through a *local forward* (see
[game streaming](#game-streaming-moonlight-to-sunshine) and
[configuration](#configuration)).
- No exit node, subnet routing, Tailscale SSH, Taildrop or Funnel.
- No subnet routing, Tailscale SSH, Taildrop or Funnel.
- The console cannot use an exit node, and it does not offer itself as one.
Offering one is doable (it needs no tunnel device), but the PS5 would make
a terrible exit node: every packet would pass through this one low-priority
process, so it would be slow, and it would fall away whenever the console
goes into rest mode, reboots or loses its jailbreak, taking the internet of
every device using it with it. Use a PC, a server or a router on your
tailnet instead.
## Requirements
+1 -1
View File
@@ -52,7 +52,7 @@ These folders are not in the repository.
```powershell
# C launcher + Go program + home screen icon helper -> out\tailscale.elf
.\tools\build-payload.ps1 -GoDir tsd -Name tailscale -Version 0.4.1 -HomeIcon
.\tools\build-payload.ps1 -GoDir tsd -Name tailscale -Version 0.5.2 -HomeIcon
```
`-HomeIcon` also builds `appicon\` into `out\appicon.elf` and embeds it in
+1 -1
View File
@@ -1,5 +1,5 @@
# Build a Go program for the PS5 and wrap it in the launcher payload.
# .\tools\build-payload.ps1 -GoDir tsd -Name tailscale -Version 0.4.1 -HomeIcon
# .\tools\build-payload.ps1 -GoDir tsd -Name tailscale -Version 0.5.2 -HomeIcon
# .\tools\build-payload.ps1 -GoDir probe-go -Name probe [-DebugLoader] [-Watchdog 120] [-Send]
param(
[Parameter(Mandatory = $true)][string]$GoDir,
+14
View File
@@ -20,6 +20,14 @@ import (
// connection is declined, so the peer sees an ordinary "connection refused"
// rather than a connection that opens and closes.
func (d *daemon) forwardToLocalhost(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
ip4, ip6 := d.srv.TailscaleIPs()
if !addressedTo(dst.Addr(), ip4, ip6) {
// Only connections to the console's own tailnet addresses are for
// its services. Nothing else arrives today, but if this node ever
// advertised routes, a connection to any address on a port that is
// open here must not end up at the console's service.
return nil, false
}
port := dst.Port()
if port == d.webPort {
// The status page is served on the tailnet connection itself rather
@@ -53,6 +61,12 @@ func (d *daemon) forwardToLocalhost(src, dst netip.AddrPort) (handler func(net.C
}, true
}
// addressedTo reports whether dst is one of the node's own addresses.
func addressedTo(dst netip.Addr, own ...netip.Addr) bool {
dst = dst.Unmap()
return dst.IsValid() && slices.Contains(own, dst)
}
// pipe copies in both directions until both sides are done.
func pipe(a, b net.Conn) {
done := make(chan struct{}, 2)
+22
View File
@@ -3,9 +3,31 @@ package main
import (
"io"
"net"
"net/netip"
"testing"
)
func TestAddressedTo(t *testing.T) {
ip4, ip6 := netip.MustParseAddr("100.64.0.1"), netip.MustParseAddr("fd7a:115c:a1e0::1")
for addr, want := range map[string]bool{
"100.64.0.1": true,
"::ffff:100.64.0.1": true,
"fd7a:115c:a1e0::1": true,
"100.64.0.2": false,
"93.184.216.34": false,
"127.0.0.1": false,
"2606:4700:4700::64": false,
} {
if got := addressedTo(netip.MustParseAddr(addr), ip4, ip6); got != want {
t.Errorf("addressedTo(%s) = %v, want %v", addr, got, want)
}
}
// Before the node has its addresses nothing is for it.
if addressedTo(netip.Addr{}, netip.Addr{}, netip.Addr{}) {
t.Error("an invalid address matched")
}
}
// pipe must pass a half-close through: the ELF loader protocol and FTP data
// connections both rely on the reader seeing EOF while the other direction
// stays open.