mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
client, cmd/hello, ipn, wgengine: fix whois for netstack-forwarded connections
Updates #504 Updates #707 Signed-off-by: Naman Sood <mail@nsood.in>
This commit is contained in:
parent
44ab0acbdb
commit
770aa71ffb
@ -56,17 +56,7 @@ func DoLocalRequest(req *http.Request) (*http.Response, error) {
|
|||||||
|
|
||||||
// WhoIs returns the owner of the remoteAddr, which must be an IP or IP:port.
|
// WhoIs returns the owner of the remoteAddr, which must be an IP or IP:port.
|
||||||
func WhoIs(ctx context.Context, remoteAddr string) (*tailcfg.WhoIsResponse, error) {
|
func WhoIs(ctx context.Context, remoteAddr string) (*tailcfg.WhoIsResponse, error) {
|
||||||
var ip string
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/whois?addr="+url.QueryEscape(remoteAddr), nil)
|
||||||
if net.ParseIP(remoteAddr) != nil {
|
|
||||||
ip = remoteAddr
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
ip, _, err = net.SplitHostPort(remoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid remoteAddr %q", remoteAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/whois?ip="+url.QueryEscape(ip), nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,12 @@
|
|||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"tailscale.com/client/tailscale"
|
"tailscale.com/client/tailscale"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -107,6 +107,23 @@ type tmplData struct {
|
|||||||
IP string // "100.2.3.4"
|
IP string // "100.2.3.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tailscaleIP(who *tailcfg.WhoIsResponse) string {
|
||||||
|
if who == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for _, nodeIP := range who.Node.Addresses {
|
||||||
|
if nodeIP.IP.Is4() && nodeIP.IsSingleIP() {
|
||||||
|
return nodeIP.IP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, nodeIP := range who.Node.Addresses {
|
||||||
|
if nodeIP.IsSingleIP() {
|
||||||
|
return nodeIP.IP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func root(w http.ResponseWriter, r *http.Request) {
|
func root(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.TLS == nil && *httpsAddr != "" {
|
if r.TLS == nil && *httpsAddr != "" {
|
||||||
host := r.Host
|
host := r.Host
|
||||||
@ -146,14 +163,13 @@ func root(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
|
||||||
data = tmplData{
|
data = tmplData{
|
||||||
DisplayName: who.UserProfile.DisplayName,
|
DisplayName: who.UserProfile.DisplayName,
|
||||||
LoginName: who.UserProfile.LoginName,
|
LoginName: who.UserProfile.LoginName,
|
||||||
ProfilePicURL: who.UserProfile.ProfilePicURL,
|
ProfilePicURL: who.UserProfile.ProfilePicURL,
|
||||||
MachineName: firstLabel(who.Node.ComputedName),
|
MachineName: firstLabel(who.Node.ComputedName),
|
||||||
MachineOS: who.Node.Hostinfo.OS,
|
MachineOS: who.Node.Hostinfo.OS,
|
||||||
IP: ip,
|
IP: tailscaleIP(who),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
@ -254,14 +254,25 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WhoIs reports the node and user who owns the node with the given IP.
|
// WhoIs reports the node and user who owns the node with the given IP:port.
|
||||||
|
// If the IP address is a Tailscale IP, the provided port may be 0.
|
||||||
// If ok == true, n and u are valid.
|
// If ok == true, n and u are valid.
|
||||||
func (b *LocalBackend) WhoIs(ip netaddr.IP) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
|
func (b *LocalBackend) WhoIs(ipp netaddr.IPPort) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
n, ok = b.nodeByAddr[ip]
|
n, ok = b.nodeByAddr[ipp.IP]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, u, false
|
var ip netaddr.IP
|
||||||
|
if ipp.Port != 0 {
|
||||||
|
ip, ok = b.e.WhoIsIPPort(ipp)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, u, false
|
||||||
|
}
|
||||||
|
n, ok = b.nodeByAddr[ip]
|
||||||
|
if !ok {
|
||||||
|
return nil, u, false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
u, ok = b.netMap.UserProfiles[n.User]
|
u, ok = b.netMap.UserProfiles[n.User]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -67,21 +67,21 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
b := h.b
|
b := h.b
|
||||||
var ip netaddr.IP
|
var ipp netaddr.IPPort
|
||||||
if v := r.FormValue("ip"); v != "" {
|
if v := r.FormValue("addr"); v != "" {
|
||||||
var err error
|
var err error
|
||||||
ip, err = netaddr.ParseIP(r.FormValue("ip"))
|
ipp, err = netaddr.ParseIPPort(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "invalid 'ip' parameter", 400)
|
http.Error(w, "invalid 'addr' parameter", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, "missing 'ip' parameter", 400)
|
http.Error(w, "missing 'addr' parameter", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n, u, ok := b.WhoIs(ip)
|
n, u, ok := b.WhoIs(ipp)
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "no match for IP", 404)
|
http.Error(w, "no match for IP:port", 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := &tailcfg.WhoIsResponse{
|
res := &tailcfg.WhoIsResponse{
|
||||||
|
@ -367,6 +367,11 @@ func (ns *Impl) forwardTCP(client *gonet.TCPConn, wq *waiter.Queue, port uint16)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
backendLocalAddr := server.LocalAddr().(*net.TCPAddr)
|
||||||
|
backendLocalIPPort, _ := netaddr.FromStdAddr(backendLocalAddr.IP, backendLocalAddr.Port, backendLocalAddr.Zone)
|
||||||
|
clientRemoteIP, _ := netaddr.FromStdIP(client.RemoteAddr().(*net.TCPAddr).IP)
|
||||||
|
ns.e.RegisterIPPortIdentity(backendLocalIPPort, clientRemoteIP)
|
||||||
|
defer ns.e.UnregisterIPPortIdentity(backendLocalIPPort)
|
||||||
connClosed := make(chan error, 2)
|
connClosed := make(chan error, 2)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(server, client)
|
_, err := io.Copy(server, client)
|
||||||
@ -406,19 +411,28 @@ func (ns *Impl) acceptUDP(r *udp.ForwarderRequest) {
|
|||||||
func (ns *Impl) forwardUDP(client *gonet.UDPConn, wq *waiter.Queue, clientLocalAddr, clientRemoteAddr tcpip.FullAddress) {
|
func (ns *Impl) forwardUDP(client *gonet.UDPConn, wq *waiter.Queue, clientLocalAddr, clientRemoteAddr tcpip.FullAddress) {
|
||||||
port := clientLocalAddr.Port
|
port := clientLocalAddr.Port
|
||||||
ns.logf("[v2] netstack: forwarding incoming UDP connection on port %v", port)
|
ns.logf("[v2] netstack: forwarding incoming UDP connection on port %v", port)
|
||||||
backendLocalAddr := &net.UDPAddr{Port: int(clientRemoteAddr.Port)}
|
backendListenAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: int(clientRemoteAddr.Port)}
|
||||||
backendRemoteAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: int(port)}
|
backendRemoteAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: int(port)}
|
||||||
backendConn, err := net.ListenUDP("udp4", backendLocalAddr)
|
backendConn, err := net.ListenUDP("udp4", backendListenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ns.logf("netstack: could not bind local port %v: %v, trying again with random port", clientRemoteAddr.Port, err)
|
ns.logf("netstack: could not bind local port %v: %v, trying again with random port", clientRemoteAddr.Port, err)
|
||||||
backendConn, err = net.ListenUDP("udp4", nil)
|
backendListenAddr.Port = 0
|
||||||
|
backendConn, err = net.ListenUDP("udp4", backendListenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ns.logf("netstack: could not connect to local UDP server on port %v: %v", port, err)
|
ns.logf("netstack: could not connect to local UDP server on port %v: %v", port, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
backendLocalAddr := backendConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
backendLocalIPPort, ok := netaddr.FromStdAddr(backendListenAddr.IP, backendLocalAddr.Port, backendLocalAddr.Zone)
|
||||||
|
if !ok {
|
||||||
|
ns.logf("could not get backend local IP:port from %v:%v", backendLocalAddr.IP, backendLocalAddr.Port)
|
||||||
|
}
|
||||||
|
clientRemoteIP, _ := netaddr.FromStdIP(net.ParseIP(clientRemoteAddr.Addr.String()))
|
||||||
|
ns.e.RegisterIPPortIdentity(backendLocalIPPort, clientRemoteIP)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
timer := time.AfterFunc(2*time.Minute, func() {
|
timer := time.AfterFunc(2*time.Minute, func() {
|
||||||
|
ns.e.UnregisterIPPortIdentity(backendLocalIPPort)
|
||||||
ns.logf("netstack: UDP session between %s and %s timed out", clientRemoteAddr, backendRemoteAddr)
|
ns.logf("netstack: UDP session between %s and %s timed out", clientRemoteAddr, backendRemoteAddr)
|
||||||
cancel()
|
cancel()
|
||||||
client.Close()
|
client.Close()
|
||||||
|
@ -125,6 +125,7 @@ type userspaceEngine struct {
|
|||||||
pingers map[wgkey.Key]*pinger // legacy pingers for pre-discovery peers
|
pingers map[wgkey.Key]*pinger // legacy pingers for pre-discovery peers
|
||||||
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
|
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
|
||||||
networkMapCallbacks map[*someHandle]NetworkMapCallback
|
networkMapCallbacks map[*someHandle]NetworkMapCallback
|
||||||
|
tsIPByIPPort map[netaddr.IPPort]netaddr.IP // allows registration of IP:ports as belonging to a certain Tailscale IP for whois lookups
|
||||||
|
|
||||||
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
||||||
}
|
}
|
||||||
@ -1341,6 +1342,31 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
|||||||
e.magicConn.Ping(ip, cb)
|
e.magicConn.Ping(ip, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *userspaceEngine) RegisterIPPortIdentity(ipport netaddr.IPPort, tsIP netaddr.IP) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
if e.tsIPByIPPort == nil {
|
||||||
|
e.tsIPByIPPort = make(map[netaddr.IPPort]netaddr.IP)
|
||||||
|
}
|
||||||
|
e.tsIPByIPPort[ipport] = tsIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *userspaceEngine) UnregisterIPPortIdentity(ipport netaddr.IPPort) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
if e.tsIPByIPPort == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(e.tsIPByIPPort, ipport)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *userspaceEngine) WhoIsIPPort(ipport netaddr.IPPort) (tsIP netaddr.IP, ok bool) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
tsIP, ok = e.tsIPByIPPort[ipport]
|
||||||
|
return tsIP, ok
|
||||||
|
}
|
||||||
|
|
||||||
// diagnoseTUNFailure is called if tun.CreateTUN fails, to poke around
|
// diagnoseTUNFailure is called if tun.CreateTUN fails, to poke around
|
||||||
// the system and log some diagnostic info that might help debug why
|
// the system and log some diagnostic info that might help debug why
|
||||||
// TUN failed. Because TUN's already failed and things the program's
|
// TUN failed. Because TUN's already failed and things the program's
|
||||||
|
@ -120,6 +120,16 @@ func (e *watchdogEngine) DiscoPublicKey() (k tailcfg.DiscoKey) {
|
|||||||
func (e *watchdogEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
func (e *watchdogEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
||||||
e.watchdog("Ping", func() { e.wrap.Ping(ip, cb) })
|
e.watchdog("Ping", func() { e.wrap.Ping(ip, cb) })
|
||||||
}
|
}
|
||||||
|
func (e *watchdogEngine) RegisterIPPortIdentity(ipp netaddr.IPPort, tsIP netaddr.IP) {
|
||||||
|
e.watchdog("RegisterIPPortIdentity", func() { e.wrap.RegisterIPPortIdentity(ipp, tsIP) })
|
||||||
|
}
|
||||||
|
func (e *watchdogEngine) UnregisterIPPortIdentity(ipp netaddr.IPPort) {
|
||||||
|
e.watchdog("UnregisterIPPortIdentity", func() { e.wrap.UnregisterIPPortIdentity(ipp) })
|
||||||
|
}
|
||||||
|
func (e *watchdogEngine) WhoIsIPPort(ipp netaddr.IPPort) (tsIP netaddr.IP, ok bool) {
|
||||||
|
e.watchdog("UnregisterIPPortIdentity", func() { tsIP, ok = e.wrap.WhoIsIPPort(ipp) })
|
||||||
|
return tsIP, ok
|
||||||
|
}
|
||||||
func (e *watchdogEngine) Close() {
|
func (e *watchdogEngine) Close() {
|
||||||
e.watchdog("Close", e.wrap.Close)
|
e.watchdog("Close", e.wrap.Close)
|
||||||
}
|
}
|
||||||
|
@ -137,4 +137,17 @@ type Engine interface {
|
|||||||
// Ping is a request to start a discovery ping with the peer handling
|
// Ping is a request to start a discovery ping with the peer handling
|
||||||
// the given IP and then call cb with its ping latency & method.
|
// the given IP and then call cb with its ping latency & method.
|
||||||
Ping(ip netaddr.IP, cb func(*ipnstate.PingResult))
|
Ping(ip netaddr.IP, cb func(*ipnstate.PingResult))
|
||||||
|
|
||||||
|
// RegisterIPPortIdentity registers a given node (identified by its
|
||||||
|
// 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
|
||||||
|
// while proxying connections to localhost.
|
||||||
|
RegisterIPPortIdentity(netaddr.IPPort, netaddr.IP)
|
||||||
|
|
||||||
|
// UnregisterIPPortIdentity removes a temporary IP:port registration.
|
||||||
|
UnregisterIPPortIdentity(netaddr.IPPort)
|
||||||
|
|
||||||
|
// WhoIsIPPort looks up an IP:port in the temporary registrations,
|
||||||
|
// and returns a matching Tailscale IP, if it exists.
|
||||||
|
WhoIsIPPort(netaddr.IPPort) (netaddr.IP, bool)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user