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  }