github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/validator/authorized_sender_validator.go (about) 1 package validator 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/libp2p/go-libp2p/core/peer" 8 "github.com/rs/zerolog" 9 10 "github.com/onflow/flow-go/model/flow" 11 "github.com/onflow/flow-go/network" 12 "github.com/onflow/flow-go/network/channels" 13 "github.com/onflow/flow-go/network/codec" 14 "github.com/onflow/flow-go/network/message" 15 "github.com/onflow/flow-go/network/p2p" 16 p2plogging "github.com/onflow/flow-go/network/p2p/logging" 17 ) 18 19 var ( 20 ErrSenderEjected = errors.New("validation failed: sender is an ejected node") 21 ErrIdentityUnverified = errors.New("validation failed: could not verify identity of sender") 22 ) 23 24 type GetIdentityFunc func(peer.ID) (*flow.Identity, bool) 25 26 // AuthorizedSenderValidator performs message authorization validation. 27 type AuthorizedSenderValidator struct { 28 log zerolog.Logger 29 slashingViolationsConsumer network.ViolationsConsumer 30 getIdentity GetIdentityFunc 31 } 32 33 // NewAuthorizedSenderValidator returns a new AuthorizedSenderValidator 34 func NewAuthorizedSenderValidator(log zerolog.Logger, slashingViolationsConsumer network.ViolationsConsumer, getIdentity GetIdentityFunc) *AuthorizedSenderValidator { 35 return &AuthorizedSenderValidator{ 36 log: log.With().Str("component", "authorized_sender_validator").Logger(), 37 slashingViolationsConsumer: slashingViolationsConsumer, 38 getIdentity: getIdentity, 39 } 40 } 41 42 // PubSubMessageValidator wraps Validate and returns PubSubMessageValidator callback that returns pubsub.ValidationReject if validation fails and pubsub.ValidationAccept if validation passes. 43 func (av *AuthorizedSenderValidator) PubSubMessageValidator(channel channels.Channel) PubSubMessageValidator { 44 return func(from peer.ID, msg *message.Message) p2p.ValidationResult { 45 _, err := av.Validate(from, msg.Payload, channel, message.ProtocolTypePubSub) 46 if err != nil { 47 return p2p.ValidationReject 48 } 49 50 return p2p.ValidationAccept 51 } 52 } 53 54 // Validate will check if the sender of a message is authorized to send the message. 55 // Using the getIdentity to get the flow identity for the sender, asserting that the sender is a staked node and not ejected. 56 // Otherwise, the message is rejected. The message is also authorized by checking that the sender is allowed to send the message on the channel. 57 // If validation fails the message is rejected, and if the validation error is an expected error, slashing data is also collected. 58 // Authorization config is defined in message.MsgAuthConfig. 59 func (av *AuthorizedSenderValidator) Validate(from peer.ID, payload []byte, channel channels.Channel, protocol message.ProtocolType) (string, error) { 60 // NOTE: Gossipsub messages from unstaked nodes should be rejected by the libP2P node topic validator 61 // before they reach message validators. If a message from a unstaked peer gets to this point 62 // something terrible went wrong. 63 identity, ok := av.getIdentity(from) 64 if !ok { 65 violation := &network.Violation{PeerID: p2plogging.PeerId(from), Channel: channel, Protocol: protocol, Err: ErrIdentityUnverified} 66 av.slashingViolationsConsumer.OnUnAuthorizedSenderError(violation) 67 return "", ErrIdentityUnverified 68 } 69 70 msgCode, err := codec.MessageCodeFromPayload(payload) 71 if err != nil { 72 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), Channel: channel, Protocol: protocol, Err: err} 73 av.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) 74 return "", err 75 } 76 77 msgType, err := av.isAuthorizedSender(identity, channel, msgCode, protocol) 78 switch { 79 case err == nil: 80 return msgType, nil 81 case message.IsUnknownMsgTypeErr(err) || codec.IsErrUnknownMsgCode(err): 82 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), MsgType: msgType, Channel: channel, Protocol: protocol, Err: err} 83 av.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) 84 return msgType, err 85 case errors.Is(err, message.ErrUnauthorizedMessageOnChannel) || errors.Is(err, message.ErrUnauthorizedRole): 86 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), MsgType: msgType, Channel: channel, Protocol: protocol, Err: err} 87 av.slashingViolationsConsumer.OnUnAuthorizedSenderError(violation) 88 return msgType, err 89 case errors.Is(err, ErrSenderEjected): 90 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), MsgType: msgType, Channel: channel, Protocol: protocol, Err: err} 91 av.slashingViolationsConsumer.OnSenderEjectedError(violation) 92 return msgType, err 93 case errors.Is(err, message.ErrUnauthorizedUnicastOnChannel): 94 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), MsgType: msgType, Channel: channel, Protocol: protocol, Err: err} 95 av.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel(violation) 96 return msgType, err 97 case errors.Is(err, message.ErrUnauthorizedPublishOnChannel): 98 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), MsgType: msgType, Channel: channel, Protocol: protocol, Err: err} 99 av.slashingViolationsConsumer.OnUnauthorizedPublishOnChannel(violation) 100 return msgType, err 101 default: 102 // this condition should never happen and indicates there's a bug 103 // don't crash as a result of external inputs since that creates a DoS vector 104 // collect slashing data because this could potentially lead to slashing 105 err = fmt.Errorf("unexpected error during message validation: %w", err) 106 violation := &network.Violation{OriginID: identity.NodeID, Identity: identity, PeerID: p2plogging.PeerId(from), MsgType: msgType, Channel: channel, Protocol: protocol, Err: err} 107 av.slashingViolationsConsumer.OnUnexpectedError(violation) 108 return msgType, err 109 } 110 } 111 112 // isAuthorizedSender performs network authorization validation. This func will assert the following; 113 // 1. The node is not ejected. 114 // 2. Using the message auth config 115 // A. The message is authorized to be sent on channel. 116 // B. The sender role is authorized to send message on channel. 117 // 118 // Expected error returns during normal operations: 119 // - ErrSenderEjected: if identity of sender is ejected from the network 120 // - codec.ErrUnknownMsgCode: if interface for the encoded message code byte is unknown 121 // - message.UnknownMsgTypeErr if message auth config us not found for the msg 122 // - message.ErrUnauthorizedMessageOnChannel if msg is not authorized to be sent on channel 123 // - message.ErrUnauthorizedRole if sender role is not authorized to send msg 124 func (av *AuthorizedSenderValidator) isAuthorizedSender(identity *flow.Identity, channel channels.Channel, msgCode codec.MessageCode, protocol message.ProtocolType) (string, error) { 125 if identity.IsEjected() { 126 return "", ErrSenderEjected 127 } 128 129 // attempt to get the message interface from the message code encoded into the first byte of the message payload 130 // this will be used to get the message auth configuration. 131 msgInterface, what, err := codec.InterfaceFromMessageCode(msgCode) 132 if err != nil { 133 return "", fmt.Errorf("could not extract interface from message code %v: %w", msgCode, err) 134 } 135 136 // get message auth config 137 conf, err := message.GetMessageAuthConfig(msgInterface) 138 if err != nil { 139 return "", fmt.Errorf("could not get authorization config for interface %T: %w", msgInterface, err) 140 } 141 142 // handle special case for cluster prefixed channels 143 if prefix, ok := channels.ClusterChannelPrefix(channel); ok { 144 channel = channels.Channel(prefix) 145 } 146 147 if err := conf.EnsureAuthorized(identity.Role, channel, protocol); err != nil { 148 return what, err 149 } 150 151 return what, nil 152 }