51 lines
1.8 KiB
Go
51 lines
1.8 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux && !android
|
|
|
|
package ipnlocal
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
|
|
"tailscale.com/net/netns"
|
|
)
|
|
|
|
func init() {
|
|
initListenConfig = initListenConfigTun
|
|
}
|
|
|
|
// initListenConfigTun binds the peerapi listener to the tunnel interface
|
|
// with SO_BINDTODEVICE.
|
|
//
|
|
// Linux delivers a packet addressed to the node's own Tailscale IP via
|
|
// INPUT from any interface it arrives on (weak host model), so a
|
|
// LAN-adjacent attacker could otherwise complete a TCP handshake with
|
|
// the kernel-level listener, confirming that this machine's MAC
|
|
// address belongs to a given tailnet identity (the peerapi port is
|
|
// derived from the Tailscale IP). Binding the listener to the tunnel
|
|
// interface drops those handshakes at socket lookup. macOS and iOS
|
|
// already bind this listener to the tunnel interface with IP_BOUND_IF,
|
|
// and Windows is protected by its strong host model.
|
|
//
|
|
// No legitimate traffic is lost. Connections to the node's Tailscale
|
|
// IP from the local host are delivered via the tunnel interface, peer
|
|
// connections arrive that way in builds without userspace netstack,
|
|
// and with netstack compiled in (the usual case) peer connections are
|
|
// terminated in userspace and never reach the kernel listener at all.
|
|
// In netstack mode there is no tunnel interface and the listener binds
|
|
// all addresses; the only connections that reach it are loopback dials
|
|
// from netstack and from the local host, so loopback is the right
|
|
// device there.
|
|
func initListenConfigTun(config *net.ListenConfig, addr netip.Addr, tunIfIndex int) error {
|
|
device := "lo"
|
|
if tunIfIndex != 0 {
|
|
iface, err := net.InterfaceByIndex(tunIfIndex)
|
|
if err == nil {
|
|
device = iface.Name
|
|
}
|
|
}
|
|
return netns.SetListenConfigInterfaceName(config, device)
|
|
}
|