diff --git a/cmd/tailscale/cli/status.go b/cmd/tailscale/cli/status.go index 54476f1b0..782c4d8ef 100644 --- a/cmd/tailscale/cli/status.go +++ b/cmd/tailscale/cli/status.go @@ -169,6 +169,9 @@ func runStatus(ctx context.Context, args []string) error { } for _, peer := range st.Peers() { ps := st.Peer[peer] + if ps.ShareeNode { + continue + } active := peerActive(ps) if statusArgs.active && !active { continue diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index 703dca26c..06559ce44 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -64,6 +64,12 @@ type PeerStatus struct { LastHandshake time.Time // with local wireguard KeepAlive bool + // ShareeNode indicates this node exists in the netmap because + // it's owned by a shared-to user and that node might connect + // to us. These nodes should be hidden by "tailscale status" + // etc by default. + ShareeNode bool `json:",omitempty"` + // InNetworkMap means that this peer was seen in our latest network map. // In theory, all of InNetworkMap and InMagicSock and InEngine should all be true. InNetworkMap bool @@ -218,6 +224,9 @@ func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) { if st.KeepAlive { e.KeepAlive = true } + if st.ShareeNode { + e.ShareeNode = true + } } type StatusUpdater interface { diff --git a/ipn/local.go b/ipn/local.go index 1bc5b3e9d..a27384224 100644 --- a/ipn/local.go +++ b/ipn/local.go @@ -221,6 +221,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) { KeepAlive: p.KeepAlive, Created: p.Created, LastSeen: lastSeen, + ShareeNode: p.Hostinfo.ShareeNode, }) } } diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index d766a55b3..e4a16176b 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -287,6 +287,7 @@ type Hostinfo struct { DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone 11 Pro") Hostname string // name of the host the client runs on ShieldsUp bool `json:",omitempty"` // indicates whether the host is blocking incoming connections + ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user GoArch string `json:",omitempty"` // the host's GOARCH value (of the running binary) RoutableIPs []wgcfg.CIDR `json:",omitempty"` // set of IP ranges this client can route RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 01473d0ed..d89607dd4 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -106,6 +106,7 @@ var _HostinfoNeedsRegeneration = Hostinfo(struct { DeviceModel string Hostname string ShieldsUp bool + ShareeNode bool GoArch string RoutableIPs []wgcfg.CIDR RequestTags []string diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 8a2e1e5ab..3f8cbcf69 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -23,9 +23,12 @@ func fieldsOf(t reflect.Type) (fields []string) { func TestHostinfoEqual(t *testing.T) { hiHandles := []string{ - "IPNVersion", "FrontendLogID", "BackendLogID", "OS", "OSVersion", - "DeviceModel", "Hostname", "ShieldsUp", "GoArch", "RoutableIPs", - "RequestTags", "Services", "NetInfo", + "IPNVersion", "FrontendLogID", "BackendLogID", + "OS", "OSVersion", "DeviceModel", "Hostname", + "ShieldsUp", "ShareeNode", + "GoArch", + "RoutableIPs", "RequestTags", + "Services", "NetInfo", } if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) { t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n", @@ -169,6 +172,11 @@ func TestHostinfoEqual(t *testing.T) { &Hostinfo{Services: []Service{Service{Proto: TCP, Port: 1234, Description: "foo"}}}, true, }, + { + &Hostinfo{ShareeNode: true}, + &Hostinfo{}, + false, + }, } for i, tt := range tests { got := tt.a.Equal(tt.b)