ipn/ipnlocal: discard node keys that have been rotated out
A non-signing node can be allowed to re-sign its new node keys following key renewal/rotation (e.g. via `tailscale up --force-reauth`). To be able to do this, node's TLK is written into WrappingPubkey field of the initial SigDirect signature, signed by a signing node. The intended use of this field implies that, for each WrappingPubkey, we typically expect to have at most one active node with a signature tracing back to that key. Multiple valid signatures referring to the same WrappingPubkey can occur if a client's state has been cloned, but it's something we explicitly discourage and don't support: https://tailscale.com/s/clone This change propagates rotation details (wrapping public key, a list of previous node keys that have been rotated out) to netmap processing, and adds tracking of obsolete node keys that, when found, will get filtered out. Updates tailscale/corp#19764 Signed-off-by: Anton Tolchanov <anton@tailscale.com>
This commit is contained in:
committed by
Anton Tolchanov
parent
42cfbf427c
commit
01847e0123
+16
-5
@@ -668,25 +668,36 @@ func (a *Authority) Inform(storage Chonk, updates []AUM) error {
|
||||
// NodeKeyAuthorized checks if the provided nodeKeySignature authorizes
|
||||
// the given node key.
|
||||
func (a *Authority) NodeKeyAuthorized(nodeKey key.NodePublic, nodeKeySignature tkatype.MarshaledSignature) error {
|
||||
_, err := a.NodeKeyAuthorizedWithDetails(nodeKey, nodeKeySignature)
|
||||
return err
|
||||
}
|
||||
|
||||
// NodeKeyAuthorized checks if the provided nodeKeySignature authorizes
|
||||
// the given node key, and returns RotationDetails if the signature is
|
||||
// a valid rotation signature.
|
||||
func (a *Authority) NodeKeyAuthorizedWithDetails(nodeKey key.NodePublic, nodeKeySignature tkatype.MarshaledSignature) (*RotationDetails, error) {
|
||||
var decoded NodeKeySignature
|
||||
if err := decoded.Unserialize(nodeKeySignature); err != nil {
|
||||
return fmt.Errorf("unserialize: %v", err)
|
||||
return nil, fmt.Errorf("unserialize: %v", err)
|
||||
}
|
||||
if decoded.SigKind == SigCredential {
|
||||
return errors.New("credential signatures cannot authorize nodes on their own")
|
||||
return nil, errors.New("credential signatures cannot authorize nodes on their own")
|
||||
}
|
||||
|
||||
kID, err := decoded.authorizingKeyID()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := a.state.GetKey(kID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("key: %v", err)
|
||||
return nil, fmt.Errorf("key: %v", err)
|
||||
}
|
||||
|
||||
return decoded.verifySignature(nodeKey, key)
|
||||
if err := decoded.verifySignature(nodeKey, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decoded.rotationDetails()
|
||||
}
|
||||
|
||||
// KeyTrusted returns true if the given keyID is trusted by the tailnet
|
||||
|
||||
Reference in New Issue
Block a user