mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 13:48:01 +00:00
cmd/tl-longchain: tool to re-sign nodes with long rotation signatures
In Tailnet Lock, there is an implicit limit on the number of rotation signatures that can be chained before the signature becomes too long. This program helps tailnet admins to identify nodes that have signatures with long chains and prints commands to re-sign those node keys with a fresh direct signature. It's a temporary mitigation measure, and we will remove this tool as we design and implement a long-term approach for rotation signatures. Example output: ``` 2024/08/20 18:25:03 Self: does not need re-signing 2024/08/20 18:25:03 Visible peers with valid signatures: 2024/08/20 18:25:03 Peer xxx2.yy.ts.net. (100.77.192.34) nodeid=nyDmhiZiGA11KTM59, current signature kind=direct: does not need re-signing 2024/08/20 18:25:03 Peer xxx3.yy.ts.net. (100.84.248.22) nodeid=ndQ64mDnaB11KTM59, current signature kind=direct: does not need re-signing 2024/08/20 18:25:03 Peer xxx4.yy.ts.net. (100.85.253.53) nodeid=nmZfVygzkB21KTM59, current signature kind=rotation: chain length 4, printing command to re-sign tailscale lock sign nodekey:530bddbfbe69e91fe15758a1d6ead5337aa6307e55ac92dafad3794f8b3fc661 tlpub:4bf07597336703395f2149dce88e7c50dd8694ab5bbde3d7c2a1c7b3e231a3c2 ``` To support this, the NetworkLockStatus localapi response now includes information about signatures of all peers rather than just the invalid ones. This is not displayed by default in `tailscale lock status`, but will be surfaced in `tailscale lock status --json`. Updates #13185 Signed-off-by: Anton Tolchanov <anton@tailscale.com>
This commit is contained in:

committed by
Anton Tolchanov

parent
7d83056a1b
commit
151b77f9d6
@@ -53,7 +53,7 @@ type tkaState struct {
|
||||
profile ipn.ProfileID
|
||||
authority *tka.Authority
|
||||
storage *tka.FS
|
||||
filtered []ipnstate.TKAFilteredPeer
|
||||
filtered []ipnstate.TKAPeer
|
||||
}
|
||||
|
||||
// tkaFilterNetmapLocked checks the signatures on each node key, dropping
|
||||
@@ -99,7 +99,7 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
|
||||
// nm.Peers is ordered, so deletion must be order-preserving.
|
||||
if len(toDelete) > 0 || len(obsoleteByRotation) > 0 {
|
||||
peers := make([]tailcfg.NodeView, 0, len(nm.Peers))
|
||||
filtered := make([]ipnstate.TKAFilteredPeer, 0, len(toDelete)+len(obsoleteByRotation))
|
||||
filtered := make([]ipnstate.TKAPeer, 0, len(toDelete)+len(obsoleteByRotation))
|
||||
for i, p := range nm.Peers {
|
||||
if !toDelete[i] && !obsoleteByRotation.Contains(p.Key()) {
|
||||
peers = append(peers, p)
|
||||
@@ -108,20 +108,7 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
|
||||
b.logf("Network lock is dropping peer %v(%v) due to key rotation", p.ID(), p.StableID())
|
||||
}
|
||||
// Record information about the node we filtered out.
|
||||
fp := ipnstate.TKAFilteredPeer{
|
||||
Name: p.Name(),
|
||||
ID: p.ID(),
|
||||
StableID: p.StableID(),
|
||||
TailscaleIPs: make([]netip.Addr, p.Addresses().Len()),
|
||||
NodeKey: p.Key(),
|
||||
}
|
||||
for i := range p.Addresses().Len() {
|
||||
addr := p.Addresses().At(i)
|
||||
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) {
|
||||
fp.TailscaleIPs[i] = addr.Addr()
|
||||
}
|
||||
}
|
||||
filtered = append(filtered, fp)
|
||||
filtered = append(filtered, tkaStateFromPeer(p))
|
||||
}
|
||||
}
|
||||
nm.Peers = peers
|
||||
@@ -546,11 +533,17 @@ func (b *LocalBackend) NetworkLockStatus() *ipnstate.NetworkLockStatus {
|
||||
}
|
||||
}
|
||||
|
||||
filtered := make([]*ipnstate.TKAFilteredPeer, len(b.tka.filtered))
|
||||
filtered := make([]*ipnstate.TKAPeer, len(b.tka.filtered))
|
||||
for i := range len(filtered) {
|
||||
filtered[i] = b.tka.filtered[i].Clone()
|
||||
}
|
||||
|
||||
visible := make([]*ipnstate.TKAPeer, len(b.netMap.Peers))
|
||||
for i, p := range b.netMap.Peers {
|
||||
s := tkaStateFromPeer(p)
|
||||
visible[i] = &s
|
||||
}
|
||||
|
||||
stateID1, _ := b.tka.authority.StateIDs()
|
||||
|
||||
return &ipnstate.NetworkLockStatus{
|
||||
@@ -562,10 +555,32 @@ func (b *LocalBackend) NetworkLockStatus() *ipnstate.NetworkLockStatus {
|
||||
NodeKeySignature: nodeKeySignature,
|
||||
TrustedKeys: outKeys,
|
||||
FilteredPeers: filtered,
|
||||
VisiblePeers: visible,
|
||||
StateID: stateID1,
|
||||
}
|
||||
}
|
||||
|
||||
func tkaStateFromPeer(p tailcfg.NodeView) ipnstate.TKAPeer {
|
||||
fp := ipnstate.TKAPeer{
|
||||
Name: p.Name(),
|
||||
ID: p.ID(),
|
||||
StableID: p.StableID(),
|
||||
TailscaleIPs: make([]netip.Addr, 0, p.Addresses().Len()),
|
||||
NodeKey: p.Key(),
|
||||
}
|
||||
for i := range p.Addresses().Len() {
|
||||
addr := p.Addresses().At(i)
|
||||
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) {
|
||||
fp.TailscaleIPs = append(fp.TailscaleIPs, addr.Addr())
|
||||
}
|
||||
}
|
||||
var decoded tka.NodeKeySignature
|
||||
if err := decoded.Unserialize(p.KeySignature().AsSlice()); err == nil {
|
||||
fp.NodeKeySignature = decoded
|
||||
}
|
||||
return fp
|
||||
}
|
||||
|
||||
// NetworkLockInit enables network-lock for the tailnet, with the tailnets'
|
||||
// key authority initialized to trust the provided keys.
|
||||
//
|
||||
|
Reference in New Issue
Block a user