mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-27 10:47:35 +00:00
net/socks5/tssocks, wgengine: permit SOCKS through subnet routers/exit nodes
Fixes #1970 Change-Id: Ibef45e8796e1d9625716d72539c96d1dbf7b1f76 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
3181bbb8e4
commit
bb91cfeae7
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/net/socks5"
|
"tailscale.com/net/socks5"
|
||||||
"tailscale.com/net/tsaddr"
|
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/netmap"
|
"tailscale.com/types/netmap"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
@ -27,7 +26,7 @@ import (
|
|||||||
//
|
//
|
||||||
// If ns is non-nil, it is used for dialing when needed.
|
// If ns is non-nil, it is used for dialing when needed.
|
||||||
func NewServer(logf logger.Logf, e wgengine.Engine, ns *netstack.Impl) *socks5.Server {
|
func NewServer(logf logger.Logf, e wgengine.Engine, ns *netstack.Impl) *socks5.Server {
|
||||||
d := &dialer{ns: ns}
|
d := &dialer{ns: ns, eng: e}
|
||||||
e.AddNetworkMapCallback(d.onNewNetmap)
|
e.AddNetworkMapCallback(d.onNewNetmap)
|
||||||
return &socks5.Server{
|
return &socks5.Server{
|
||||||
Logf: logf,
|
Logf: logf,
|
||||||
@ -37,7 +36,8 @@ func NewServer(logf logger.Logf, e wgengine.Engine, ns *netstack.Impl) *socks5.S
|
|||||||
|
|
||||||
// dialer is the Tailscale SOCKS5 dialer.
|
// dialer is the Tailscale SOCKS5 dialer.
|
||||||
type dialer struct {
|
type dialer struct {
|
||||||
ns *netstack.Impl
|
ns *netstack.Impl
|
||||||
|
eng wgengine.Engine
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
dns netstack.DNSMap
|
dns netstack.DNSMap
|
||||||
@ -69,11 +69,22 @@ func (d *dialer) DialContext(ctx context.Context, network, addr string) (net.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *dialer) useNetstackForIP(ip netaddr.IP) bool {
|
func (d *dialer) useNetstackForIP(ip netaddr.IP) bool {
|
||||||
if d.ns == nil {
|
if d.ns == nil || !d.ns.ProcessLocalIPs {
|
||||||
|
// If netstack isn't used at all (nil), then obviously don't use it.
|
||||||
|
//
|
||||||
|
// But the ProcessLocalIPs check is more subtle: it really means
|
||||||
|
// whether we should use netstack for incoming traffic to ourselves.
|
||||||
|
// It's only ever true if we're running in full netstack mode (no TUN),
|
||||||
|
// so we can also use it as a proxy here for whether TUN is available.
|
||||||
|
// If it's false, there's tun and OS routes to things we need,
|
||||||
|
// so we don't want to dial with netstack.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// TODO(bradfitz): this isn't exactly right.
|
// Otherwise, we're in netstack mode, so dial via netstack if there's
|
||||||
// We should also support subnets when the
|
// any peer handling that IP (including exit nodes).
|
||||||
// prefs are configured as such.
|
//
|
||||||
return tsaddr.IsTailscaleIP(ip)
|
// Otherwise assume it's something else (e.g. dialing
|
||||||
|
// google.com:443 via SOCKS) that the caller can dial directly.
|
||||||
|
_, ok := d.eng.PeerForIP(ip)
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.Wra
|
|||||||
// like:
|
// like:
|
||||||
// open-conn-track: timeout opening (100.115.73.60:52501 => 17.125.252.5:443); no associated peer node
|
// open-conn-track: timeout opening (100.115.73.60:52501 => 17.125.252.5:443); no associated peer node
|
||||||
if runtime.GOOS == "ios" && flow.Dst.Port() == 443 && !tsaddr.IsTailscaleIP(flow.Dst.IP()) {
|
if runtime.GOOS == "ios" && flow.Dst.Port() == 443 && !tsaddr.IsTailscaleIP(flow.Dst.IP()) {
|
||||||
if _, _, err := e.peerForIP(flow.Dst.IP()); err != nil {
|
if _, ok := e.PeerForIP(flow.Dst.IP()); !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,15 +157,12 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Diagnose why it might've timed out.
|
// Diagnose why it might've timed out.
|
||||||
n, _, err := e.peerForIP(flow.Dst.IP())
|
pip, ok := e.PeerForIP(flow.Dst.IP())
|
||||||
if err != nil {
|
if !ok {
|
||||||
e.logf("open-conn-track: timeout opening %v; peerForIP: %v", flow, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n == nil {
|
|
||||||
e.logf("open-conn-track: timeout opening %v; no associated peer node", flow)
|
e.logf("open-conn-track: timeout opening %v; no associated peer node", flow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
n := pip.Node
|
||||||
if n.DiscoKey.IsZero() {
|
if n.DiscoKey.IsZero() {
|
||||||
e.logf("open-conn-track: timeout opening %v; peer node %v running pre-0.100", flow, n.Key.ShortString())
|
e.logf("open-conn-track: timeout opening %v; peer node %v running pre-0.100", flow, n.Key.ShortString())
|
||||||
return
|
return
|
||||||
|
@ -1271,25 +1271,20 @@ func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
|
|
||||||
func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {
|
func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {
|
||||||
res := &ipnstate.PingResult{IP: ip.String()}
|
res := &ipnstate.PingResult{IP: ip.String()}
|
||||||
peer, self, err := e.peerForIP(ip)
|
pip, ok := e.PeerForIP(ip)
|
||||||
if err != nil {
|
if !ok {
|
||||||
e.logf("ping(%v): %v", ip, err)
|
|
||||||
res.Err = err.Error()
|
|
||||||
cb(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if peer == nil {
|
|
||||||
e.logf("ping(%v): no matching peer", ip)
|
e.logf("ping(%v): no matching peer", ip)
|
||||||
res.Err = "no matching peer"
|
res.Err = "no matching peer"
|
||||||
cb(res)
|
cb(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self {
|
if pip.IsSelf {
|
||||||
res.Err = fmt.Sprintf("%v is local Tailscale IP", ip)
|
res.Err = fmt.Sprintf("%v is local Tailscale IP", ip)
|
||||||
res.IsLocalIP = true
|
res.IsLocalIP = true
|
||||||
cb(res)
|
cb(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
peer := pip.Node
|
||||||
|
|
||||||
pingType := "disco"
|
pingType := "disco"
|
||||||
if useTSMP {
|
if useTSMP {
|
||||||
@ -1424,46 +1419,35 @@ func (e *userspaceEngine) WhoIsIPPort(ipport netaddr.IPPort) (tsIP netaddr.IP, o
|
|||||||
return tsIP, false
|
return tsIP, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// peerForIP returns the Node in the wireguard config
|
// PeerForIP returns the Node in the wireguard config
|
||||||
// that's responsible for handling the given IP address.
|
// that's responsible for handling the given IP address.
|
||||||
//
|
//
|
||||||
// If none is found in the wireguard config but one is found in
|
// If none is found in the wireguard config but one is found in
|
||||||
// the netmap, it's described in an error.
|
// the netmap, it's described in an error.
|
||||||
//
|
//
|
||||||
// If none is found in either place, (nil, nil) is returned.
|
|
||||||
//
|
//
|
||||||
// peerForIP acquires both e.mu and e.wgLock, but neither at the same
|
// peerForIP acquires both e.mu and e.wgLock, but neither at the same
|
||||||
// time.
|
// time.
|
||||||
func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, isSelf bool, err error) {
|
func (e *userspaceEngine) PeerForIP(ip netaddr.IP) (ret PeerForIP, ok bool) {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
nm := e.netMap
|
nm := e.netMap
|
||||||
e.mu.Unlock()
|
e.mu.Unlock()
|
||||||
if nm == nil {
|
if nm == nil {
|
||||||
return nil, false, errors.New("no network map")
|
return ret, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for exact matches before looking for subnet matches.
|
// Check for exact matches before looking for subnet matches.
|
||||||
var bestInNMPrefix netaddr.IPPrefix
|
// TODO(bradfitz): add maps for these. on NetworkMap?
|
||||||
var bestInNM *tailcfg.Node
|
|
||||||
for _, p := range nm.Peers {
|
for _, p := range nm.Peers {
|
||||||
for _, a := range p.Addresses {
|
for _, a := range p.Addresses {
|
||||||
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
||||||
return p, false, nil
|
return PeerForIP{Node: p, Route: a}, true
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, cidr := range p.AllowedIPs {
|
|
||||||
if !cidr.Contains(ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if bestInNMPrefix.IsZero() || cidr.Bits() > bestInNMPrefix.Bits() {
|
|
||||||
bestInNMPrefix = cidr
|
|
||||||
bestInNM = p
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, a := range nm.Addresses {
|
for _, a := range nm.Addresses {
|
||||||
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
||||||
return nm.SelfNode, true, nil
|
return PeerForIP{Node: nm.SelfNode, IsSelf: true, Route: a}, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1489,17 +1473,11 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, isSelf bool
|
|||||||
if !bestKey.IsZero() {
|
if !bestKey.IsZero() {
|
||||||
for _, p := range nm.Peers {
|
for _, p := range nm.Peers {
|
||||||
if p.Key == bestKey {
|
if p.Key == bestKey {
|
||||||
return p, false, nil
|
return PeerForIP{Node: p, Route: best}, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bestInNM == nil {
|
return ret, false
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
if bestInNMPrefix.Bits() == 0 {
|
|
||||||
return nil, false, errors.New("exit node found but not enabled")
|
|
||||||
}
|
|
||||||
return nil, false, fmt.Errorf("node %q found, but not using its %v route", bestInNM.ComputedNameWithHost, bestInNMPrefix)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type closeOnErrorPool []func()
|
type closeOnErrorPool []func()
|
||||||
|
@ -146,6 +146,11 @@ func (e *watchdogEngine) GetResolver() (r *resolver.Resolver, ok bool) {
|
|||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
func (e *watchdogEngine) PeerForIP(ip netaddr.IP) (ret PeerForIP, ok bool) {
|
||||||
|
e.watchdog("PeerForIP", func() { ret, ok = e.wrap.PeerForIP(ip) })
|
||||||
|
return ret, ok
|
||||||
|
}
|
||||||
|
|
||||||
func (e *watchdogEngine) Wait() {
|
func (e *watchdogEngine) Wait() {
|
||||||
e.wrap.Wait()
|
e.wrap.Wait()
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,20 @@ type someHandle struct{ _ byte }
|
|||||||
// ErrNoChanges is returned by Engine.Reconfig if no changes were made.
|
// ErrNoChanges is returned by Engine.Reconfig if no changes were made.
|
||||||
var ErrNoChanges = errors.New("no changes made to Engine config")
|
var ErrNoChanges = errors.New("no changes made to Engine config")
|
||||||
|
|
||||||
|
// PeerForIP is the type returned by Engine.PeerForIP.
|
||||||
|
type PeerForIP struct {
|
||||||
|
// Node is the matched node. It's always non-nil when
|
||||||
|
// Engine.PeerForIP returns ok==true.
|
||||||
|
Node *tailcfg.Node
|
||||||
|
|
||||||
|
// IsSelf is whether the Node is the local process.
|
||||||
|
IsSelf bool
|
||||||
|
|
||||||
|
// Route is the route that matched the IP provided
|
||||||
|
// to Engine.PeerForIP.
|
||||||
|
Route netaddr.IPPrefix
|
||||||
|
}
|
||||||
|
|
||||||
// Engine is the Tailscale WireGuard engine interface.
|
// Engine is the Tailscale WireGuard engine interface.
|
||||||
type Engine interface {
|
type Engine interface {
|
||||||
// Reconfig reconfigures WireGuard and makes sure it's running.
|
// Reconfig reconfigures WireGuard and makes sure it's running.
|
||||||
@ -62,6 +76,10 @@ type Engine interface {
|
|||||||
// The returned error is ErrNoChanges if no changes were made.
|
// The returned error is ErrNoChanges if no changes were made.
|
||||||
Reconfig(*wgcfg.Config, *router.Config, *dns.Config, *tailcfg.Debug) error
|
Reconfig(*wgcfg.Config, *router.Config, *dns.Config, *tailcfg.Debug) error
|
||||||
|
|
||||||
|
// PeerForIP returns the node to which the provided IP routes,
|
||||||
|
// if any. If none is found, (nil, nil) is returned.
|
||||||
|
PeerForIP(netaddr.IP) (_ PeerForIP, ok bool)
|
||||||
|
|
||||||
// GetFilter returns the current packet filter, if any.
|
// GetFilter returns the current packet filter, if any.
|
||||||
GetFilter() *filter.Filter
|
GetFilter() *filter.Filter
|
||||||
|
|
||||||
@ -141,10 +159,12 @@ type Engine interface {
|
|||||||
// RegisterIPPortIdentity registers a given node (identified by its
|
// RegisterIPPortIdentity registers a given node (identified by its
|
||||||
// Tailscale IP) as temporarily having the given IP:port for whois lookups.
|
// Tailscale IP) as temporarily having the given IP:port for whois lookups.
|
||||||
// The IP:port is generally a localhost IP and an ephemeral port, used
|
// The IP:port is generally a localhost IP and an ephemeral port, used
|
||||||
// while proxying connections to localhost.
|
// while proxying connections to localhost when tailscaled is running
|
||||||
|
// in netstack mode.
|
||||||
RegisterIPPortIdentity(netaddr.IPPort, netaddr.IP)
|
RegisterIPPortIdentity(netaddr.IPPort, netaddr.IP)
|
||||||
|
|
||||||
// UnregisterIPPortIdentity removes a temporary IP:port registration.
|
// UnregisterIPPortIdentity removes a temporary IP:port registration
|
||||||
|
// made previously by RegisterIPPortIdentity.
|
||||||
UnregisterIPPortIdentity(netaddr.IPPort)
|
UnregisterIPPortIdentity(netaddr.IPPort)
|
||||||
|
|
||||||
// WhoIsIPPort looks up an IP:port in the temporary registrations,
|
// WhoIsIPPort looks up an IP:port in the temporary registrations,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user