mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 09:32:08 +00:00
ipn/localapi,client/tailscale,cmd/derper: add WhoIs lookup by nodekey, use in derper
Fixes #12465 Change-Id: I9b7c87315a3d2b2ecae2b8db9e94b4f5a1eef74a Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
72c8f7700b
commit
6908fb0de3
@@ -966,6 +966,26 @@ func peerStatusFromNode(ps *ipnstate.PeerStatus, n tailcfg.NodeView) {
|
||||
}
|
||||
}
|
||||
|
||||
// WhoIsNodeKey returns the peer info of given public key, if it exists.
|
||||
func (b *LocalBackend) WhoIsNodeKey(k key.NodePublic) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
// TODO(bradfitz): add nodeByKey like nodeByAddr instead of walking peers.
|
||||
if b.netMap == nil {
|
||||
return n, u, false
|
||||
}
|
||||
if self := b.netMap.SelfNode; self.Valid() && self.Key() == k {
|
||||
return self, b.netMap.UserProfiles[self.User()], true
|
||||
}
|
||||
for _, n := range b.peers {
|
||||
if n.Key() == k {
|
||||
u, ok = b.netMap.UserProfiles[n.User()]
|
||||
return n, u, ok
|
||||
}
|
||||
}
|
||||
return n, u, false
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -448,6 +448,7 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
|
||||
// by the localapi WhoIs method.
|
||||
type localBackendWhoIsMethods interface {
|
||||
WhoIs(netip.AddrPort) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool)
|
||||
WhoIsNodeKey(key.NodePublic) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool)
|
||||
PeerCaps(netip.Addr) tailcfg.PeerCapMap
|
||||
}
|
||||
|
||||
@@ -456,9 +457,21 @@ func (h *Handler) serveWhoIsWithBackend(w http.ResponseWriter, r *http.Request,
|
||||
http.Error(w, "whois access denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
var (
|
||||
n tailcfg.NodeView
|
||||
u tailcfg.UserProfile
|
||||
ok bool
|
||||
)
|
||||
var ipp netip.AddrPort
|
||||
if v := r.FormValue("addr"); v != "" {
|
||||
if ip, err := netip.ParseAddr(v); err == nil {
|
||||
if strings.HasPrefix(v, "nodekey:") {
|
||||
var k key.NodePublic
|
||||
if err := k.UnmarshalText([]byte(v)); err != nil {
|
||||
http.Error(w, "invalid nodekey in 'addr' parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
n, u, ok = b.WhoIsNodeKey(k)
|
||||
} else if ip, err := netip.ParseAddr(v); err == nil {
|
||||
ipp = netip.AddrPortFrom(ip, 0)
|
||||
} else {
|
||||
var err error
|
||||
@@ -468,11 +481,13 @@ func (h *Handler) serveWhoIsWithBackend(w http.ResponseWriter, r *http.Request,
|
||||
return
|
||||
}
|
||||
}
|
||||
if ipp.IsValid() {
|
||||
n, u, ok = b.WhoIs(ipp)
|
||||
}
|
||||
} else {
|
||||
http.Error(w, "missing 'addr' parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
n, u, ok := b.WhoIs(ipp)
|
||||
if !ok {
|
||||
http.Error(w, "no match for IP:port", http.StatusNotFound)
|
||||
return
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tsd"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/logid"
|
||||
"tailscale.com/util/slicesx"
|
||||
@@ -99,26 +100,46 @@ func TestSetPushDeviceToken(t *testing.T) {
|
||||
}
|
||||
|
||||
type whoIsBackend struct {
|
||||
whoIs func(ipp netip.AddrPort) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool)
|
||||
peerCaps map[netip.Addr]tailcfg.PeerCapMap
|
||||
whoIs func(ipp netip.AddrPort) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool)
|
||||
whoIsNodeKey func(key.NodePublic) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool)
|
||||
peerCaps map[netip.Addr]tailcfg.PeerCapMap
|
||||
}
|
||||
|
||||
func (b whoIsBackend) WhoIs(ipp netip.AddrPort) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
|
||||
return b.whoIs(ipp)
|
||||
}
|
||||
|
||||
func (b whoIsBackend) WhoIsNodeKey(k key.NodePublic) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
|
||||
return b.whoIsNodeKey(k)
|
||||
}
|
||||
|
||||
func (b whoIsBackend) PeerCaps(ip netip.Addr) tailcfg.PeerCapMap {
|
||||
return b.peerCaps[ip]
|
||||
}
|
||||
|
||||
// Tests that the WhoIs handler accepts either IPs or IP:ports.
|
||||
// Tests that the WhoIs handler accepts IPs, IP:ports, or nodekeys.
|
||||
//
|
||||
// From https://github.com/tailscale/tailscale/pull/9714 (a PR that is effectively a bug report)
|
||||
func TestWhoIsJustIP(t *testing.T) {
|
||||
//
|
||||
// And https://github.com/tailscale/tailscale/issues/12465
|
||||
func TestWhoIsArgTypes(t *testing.T) {
|
||||
h := &Handler{
|
||||
PermitRead: true,
|
||||
}
|
||||
for _, input := range []string{"100.101.102.103", "127.0.0.1:123"} {
|
||||
|
||||
match := func() (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
|
||||
return (&tailcfg.Node{
|
||||
ID: 123,
|
||||
Addresses: []netip.Prefix{
|
||||
netip.MustParsePrefix("100.101.102.103/32"),
|
||||
},
|
||||
}).View(),
|
||||
tailcfg.UserProfile{ID: 456, DisplayName: "foo"},
|
||||
true
|
||||
}
|
||||
|
||||
const keyStr = "nodekey:5c8f86d5fc70d924e55f02446165a5dae8f822994ad26bcf4b08fd841f9bf261"
|
||||
for _, input := range []string{"100.101.102.103", "127.0.0.1:123", keyStr} {
|
||||
rec := httptest.NewRecorder()
|
||||
t.Run(input, func(t *testing.T) {
|
||||
b := whoIsBackend{
|
||||
@@ -129,14 +150,14 @@ func TestWhoIsJustIP(t *testing.T) {
|
||||
t.Fatalf("backend called with %v; want %v", ipp, want)
|
||||
}
|
||||
}
|
||||
return (&tailcfg.Node{
|
||||
ID: 123,
|
||||
Addresses: []netip.Prefix{
|
||||
netip.MustParsePrefix("100.101.102.103/32"),
|
||||
},
|
||||
}).View(),
|
||||
tailcfg.UserProfile{ID: 456, DisplayName: "foo"},
|
||||
true
|
||||
return match()
|
||||
},
|
||||
whoIsNodeKey: func(k key.NodePublic) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
|
||||
if k.String() != keyStr {
|
||||
t.Fatalf("backend called with %v; want %v", k, keyStr)
|
||||
}
|
||||
return match()
|
||||
|
||||
},
|
||||
peerCaps: map[netip.Addr]tailcfg.PeerCapMap{
|
||||
netip.MustParseAddr("100.101.102.103"): map[tailcfg.PeerCapability][]tailcfg.RawMessage{
|
||||
@@ -146,9 +167,12 @@ func TestWhoIsJustIP(t *testing.T) {
|
||||
}
|
||||
h.serveWhoIsWithBackend(rec, httptest.NewRequest("GET", "/v0/whois?addr="+url.QueryEscape(input), nil), b)
|
||||
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("response code %d", rec.Code)
|
||||
}
|
||||
var res apitype.WhoIsResponse
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &res); err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("parsing response %#q: %v", rec.Body.Bytes(), err)
|
||||
}
|
||||
if got, want := res.Node.ID, tailcfg.NodeID(123); got != want {
|
||||
t.Errorf("res.Node.ID=%v, want %v", got, want)
|
||||
|
||||
Reference in New Issue
Block a user