github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/connection/connection_gater.go (about) 1 package connection 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/libp2p/go-libp2p/core/control" 8 "github.com/libp2p/go-libp2p/core/network" 9 "github.com/libp2p/go-libp2p/core/peer" 10 "github.com/multiformats/go-multiaddr" 11 "github.com/rs/zerolog" 12 13 "github.com/onflow/flow-go/module" 14 "github.com/onflow/flow-go/network/p2p" 15 p2plogging "github.com/onflow/flow-go/network/p2p/logging" 16 "github.com/onflow/flow-go/utils/logging" 17 ) 18 19 var _ p2p.ConnectionGater = (*ConnGater)(nil) 20 21 // ConnGaterOption allow the connection gater to be configured with a list of PeerFilter funcs for a specific conn gater callback. 22 // In the current implementation of the ConnGater the following callbacks can be configured with peer filters. 23 // * InterceptPeerDial - peer filters can be configured with WithOnInterceptPeerDialFilters which will allow or disallow outbound connections. 24 // * InterceptSecured - peer filters can be configured with WithOnInterceptSecuredFilters which will allow or disallow inbound connections after libP2P security handshake. 25 type ConnGaterOption func(*ConnGater) 26 27 // WithOnInterceptPeerDialFilters sets peer filters for outbound connections. 28 func WithOnInterceptPeerDialFilters(filters []p2p.PeerFilter) ConnGaterOption { 29 return func(c *ConnGater) { 30 c.onInterceptPeerDialFilters = filters 31 } 32 } 33 34 // WithOnInterceptSecuredFilters sets peer filters for inbound secured connections. 35 func WithOnInterceptSecuredFilters(filters []p2p.PeerFilter) ConnGaterOption { 36 return func(c *ConnGater) { 37 c.onInterceptSecuredFilters = filters 38 } 39 } 40 41 // ConnGater is the implementation of the libp2p connmgr.ConnectionGater interface 42 // It provides node allowlisting by libp2p peer.ID which is derived from the node public networking key 43 type ConnGater struct { 44 sync.RWMutex 45 onInterceptPeerDialFilters []p2p.PeerFilter 46 onInterceptSecuredFilters []p2p.PeerFilter 47 48 // disallowListOracle is consulted upon every incoming or outgoing connection attempt, and the connection is only 49 // allowed if the remote peer is not on the disallow list. 50 // A ConnGater must have a disallowListOracle set, and if one is not set the ConnGater will panic. 51 disallowListOracle p2p.DisallowListOracle 52 53 // identityProvider provides the identity of a node given its peer ID for logging purposes only. 54 // It is not used for allowlisting or filtering. We use the onInterceptPeerDialFilters and onInterceptSecuredFilters 55 // to determine if a node should be allowed to connect. 56 identityProvider module.IdentityProvider 57 log zerolog.Logger 58 } 59 60 func NewConnGater(log zerolog.Logger, identityProvider module.IdentityProvider, opts ...ConnGaterOption) *ConnGater { 61 cg := &ConnGater{ 62 log: log.With().Str("component", "connection_gater").Logger(), 63 identityProvider: identityProvider, 64 } 65 66 for _, opt := range opts { 67 opt(cg) 68 } 69 70 return cg 71 } 72 73 // InterceptPeerDial - a callback which allows or disallows outbound connection 74 func (c *ConnGater) InterceptPeerDial(p peer.ID) bool { 75 lg := c.log.With().Str("peer_id", p2plogging.PeerId(p)).Logger() 76 77 disallowListCauses, disallowListed := c.disallowListOracle.IsDisallowListed(p) 78 if disallowListed { 79 lg.Warn(). 80 Str("disallow_list_causes", fmt.Sprintf("%v", disallowListCauses)). 81 Msg("outbound connection attempt to disallow listed peer is rejected") 82 return false 83 } 84 85 if len(c.onInterceptPeerDialFilters) == 0 { 86 lg.Warn(). 87 Msg("outbound connection established with no intercept peer dial filters") 88 return true 89 } 90 91 identity, ok := c.identityProvider.ByPeerID(p) 92 if !ok { 93 lg = lg.With(). 94 Str("remote_node_id", "unknown"). 95 Str("role", "unknown"). 96 Logger() 97 } else { 98 lg = lg.With(). 99 Hex("remote_node_id", logging.ID(identity.NodeID)). 100 Str("role", identity.Role.String()). 101 Logger() 102 } 103 104 if err := c.peerIDPassesAllFilters(p, c.onInterceptPeerDialFilters); err != nil { 105 // log the filtered outbound connection attempt 106 lg.Warn(). 107 Err(err). 108 Msg("rejected outbound connection attempt") 109 return false 110 } 111 112 lg.Debug().Msg("outbound connection established") 113 return true 114 } 115 116 // InterceptAddrDial is not used. Currently, allowlisting is only implemented by Peer IDs and not multi-addresses 117 func (c *ConnGater) InterceptAddrDial(_ peer.ID, ma multiaddr.Multiaddr) bool { 118 return true 119 } 120 121 // InterceptAccept is not used. Currently, allowlisting is only implemented by Peer IDs and not multi-addresses 122 func (c *ConnGater) InterceptAccept(cm network.ConnMultiaddrs) bool { 123 return true 124 } 125 126 // InterceptSecured a callback executed after the libp2p security handshake. It tests whether to accept or reject 127 // an inbound connection based on its peer id. 128 func (c *ConnGater) InterceptSecured(dir network.Direction, p peer.ID, addr network.ConnMultiaddrs) bool { 129 switch dir { 130 case network.DirInbound: 131 lg := c.log.With(). 132 Str("peer_id", p2plogging.PeerId(p)). 133 Str("remote_address", addr.RemoteMultiaddr().String()). 134 Logger() 135 136 disallowListCauses, disallowListed := c.disallowListOracle.IsDisallowListed(p) 137 if disallowListed { 138 lg.Warn(). 139 Str("disallow_list_causes", fmt.Sprintf("%v", disallowListCauses)). 140 Msg("inbound connection attempt to disallow listed peer is rejected") 141 return false 142 } 143 144 if len(c.onInterceptSecuredFilters) == 0 { 145 lg.Warn().Msg("inbound connection established with no intercept secured filters") 146 return true 147 } 148 149 identity, ok := c.identityProvider.ByPeerID(p) 150 if !ok { 151 lg = lg.With(). 152 Str("remote_node_id", "unknown"). 153 Str("role", "unknown"). 154 Logger() 155 } else { 156 lg = lg.With(). 157 Hex("remote_node_id", logging.ID(identity.NodeID)). 158 Str("role", identity.Role.String()). 159 Logger() 160 } 161 162 if err := c.peerIDPassesAllFilters(p, c.onInterceptSecuredFilters); err != nil { 163 // log the illegal connection attempt from the remote node 164 lg.Error(). 165 Err(err). 166 Str("local_address", addr.LocalMultiaddr().String()). 167 Bool(logging.KeySuspicious, true). 168 Msg("rejected inbound connection") 169 return false 170 } 171 172 lg.Debug().Msg("inbound connection established") 173 return true 174 default: 175 // outbound connection should have been already blocked before this call 176 return true 177 } 178 } 179 180 // InterceptUpgraded decision to continue or drop the connection should have been made before this call 181 func (c *ConnGater) InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason) { 182 return true, 0 183 } 184 185 func (c *ConnGater) peerIDPassesAllFilters(p peer.ID, filters []p2p.PeerFilter) error { 186 for _, allowed := range filters { 187 if err := allowed(p); err != nil { 188 return err 189 } 190 } 191 192 return nil 193 } 194 195 // SetDisallowListOracle sets the disallow list oracle for the connection gater. 196 // If one is set, the oracle is consulted upon every incoming or outgoing connection attempt, and 197 // the connection is only allowed if the remote peer is not on the disallow list. 198 // In Flow blockchain, it is not optional to dismiss the disallow list oracle, and if one is not set 199 // the connection gater will panic. 200 // Also, it follows a dependency injection pattern and does not allow to set the disallow list oracle more than once, 201 // any subsequent calls to this method will result in a panic. 202 // Args: 203 // 204 // oracle: the disallow list oracle to set. 205 // 206 // Returns: 207 // 208 // none 209 // 210 // Panics: 211 // 212 // if the disallow list oracle is already set. 213 func (c *ConnGater) SetDisallowListOracle(oracle p2p.DisallowListOracle) { 214 if c.disallowListOracle != nil { 215 panic("disallow list oracle already set") 216 } 217 c.disallowListOracle = oracle 218 }