cmd/tailscale,ipn: surface TKA-filtered peers in lock status command

Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
Tom DNetto 2022-11-30 13:04:43 -08:00 committed by Tom
parent 731be07777
commit 9a80b8fb10
4 changed files with 97 additions and 0 deletions

View File

@ -213,6 +213,26 @@ func runNetworkLockStatus(ctx context.Context, args []string) error {
}
}
if st.Enabled && len(st.FilteredPeers) > 0 {
fmt.Println()
fmt.Println("The following peers are locked out by tailnet lock & do not have connectivity:")
for _, p := range st.FilteredPeers {
var line strings.Builder
line.WriteString("\t")
line.WriteString(p.Name)
line.WriteString("\t")
for i, addr := range p.TailscaleIPs {
line.WriteString(addr.String())
if i < len(p.TailscaleIPs)-1 {
line.WriteString(", ")
}
}
line.WriteString("\t")
line.WriteString(string(p.StableID))
fmt.Println(line.String())
}
}
return nil
}

View File

@ -14,6 +14,7 @@
"fmt"
"io"
"net/http"
"net/netip"
"os"
"path/filepath"
"time"
@ -21,6 +22,7 @@
"tailscale.com/envknob"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
@ -41,6 +43,7 @@ type tkaState struct {
profile ipn.ProfileID
authority *tka.Authority
storage *tka.FS
filtered []ipnstate.TKAFilteredPeer
}
// tkaFilterNetmapLocked checks the signatures on each node key, dropping
@ -75,12 +78,30 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
// nm.Peers is ordered, so deletion must be order-preserving.
if len(toDelete) > 0 {
peers := make([]*tailcfg.Node, 0, len(nm.Peers))
filtered := make([]ipnstate.TKAFilteredPeer, 0, len(toDelete))
for i, p := range nm.Peers {
if !toDelete[i] {
peers = append(peers, p)
} else {
// Record information about the node we filtered out.
fp := ipnstate.TKAFilteredPeer{
Name: p.Name,
ID: p.ID,
StableID: p.StableID,
TailscaleIPs: make([]netip.Addr, len(p.Addresses)),
}
for i, addr := range p.Addresses {
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) {
fp.TailscaleIPs[i] = addr.Addr()
}
}
filtered = append(filtered, fp)
}
}
nm.Peers = peers
b.tka.filtered = filtered
} else {
b.tka.filtered = nil
}
}
@ -399,6 +420,11 @@ func (b *LocalBackend) NetworkLockStatus() *ipnstate.NetworkLockStatus {
}
}
filtered := make([]*ipnstate.TKAFilteredPeer, len(b.tka.filtered))
for i := 0; i < len(filtered); i++ {
filtered[i] = b.tka.filtered[i].Clone()
}
return &ipnstate.NetworkLockStatus{
Enabled: true,
Head: &head,
@ -406,6 +432,7 @@ func (b *LocalBackend) NetworkLockStatus() *ipnstate.NetworkLockStatus {
NodeKey: nodeKey,
NodeKeySigned: selfAuthorized,
TrustedKeys: outKeys,
FilteredPeers: filtered,
}
}

View File

@ -24,6 +24,8 @@
"tailscale.com/util/dnsname"
)
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=TKAFilteredPeer
// Status represents the entire state of the IPN network.
type Status struct {
// Version is the daemon's long version (see version.Long).
@ -74,6 +76,16 @@ type TKAKey struct {
Votes uint
}
// TKAFilteredPeer describes a peer which was removed from the netmap
// (i.e. no connectivity) because it failed tailnet lock
// checks.
type TKAFilteredPeer struct {
Name string // DNS
ID tailcfg.NodeID
StableID tailcfg.StableNodeID
TailscaleIPs []netip.Addr // Tailscale IP(s) assigned to this node
}
// NetworkLockStatus represents whether network-lock is enabled,
// along with details about the locally-known state of the tailnet
// key authority.
@ -99,6 +111,11 @@ type NetworkLockStatus struct {
// TrustedKeys describes the keys currently trusted to make changes
// to network-lock.
TrustedKeys []TKAKey
// FilteredPeers describes peers which were removed from the netmap
// (i.e. no connectivity) because they failed tailnet lock
// checks.
FilteredPeers []*TKAFilteredPeer
}
// NetworkLockUpdate describes a change to network-lock state.

View File

@ -0,0 +1,33 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by tailscale.com/cmd/cloner; DO NOT EDIT.
package ipnstate
import (
"net/netip"
"tailscale.com/tailcfg"
)
// Clone makes a deep copy of TKAFilteredPeer.
// The result aliases no memory with the original.
func (src *TKAFilteredPeer) Clone() *TKAFilteredPeer {
if src == nil {
return nil
}
dst := new(TKAFilteredPeer)
*dst = *src
dst.TailscaleIPs = append(src.TailscaleIPs[:0:0], src.TailscaleIPs...)
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _TKAFilteredPeerCloneNeedsRegeneration = TKAFilteredPeer(struct {
Name string
ID tailcfg.NodeID
StableID tailcfg.StableNodeID
TailscaleIPs []netip.Addr
}{})