feat: add incoming and outgoing peer blocking to handle abuse

This commit is contained in:
Derrick Hammer 2024-01-15 10:54:31 -05:00
parent d79455c68c
commit 883f50b198
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
5 changed files with 92 additions and 27 deletions

View File

@ -14,8 +14,9 @@ type NodeConfig struct {
HTTP HTTPConfig HTTP HTTPConfig
} }
type P2PConfig struct { type P2PConfig struct {
Network string Network string
Peers PeersConfig Peers PeersConfig
MaxOutgoingPeerFailures uint
} }
type PeersConfig struct { type PeersConfig struct {

View File

@ -12,7 +12,7 @@ import (
type P2PService interface { type P2PService interface {
Peers() structs.Map Peers() structs.Map
ConnectToNode(connectionUris []*url.URL, retried bool) error ConnectToNode(connectionUris []*url.URL, retried bool, fromPeer net.Peer) error
OnNewPeer(peer net.Peer, verifyId bool) error OnNewPeer(peer net.Peer, verifyId bool) error
OnNewPeerListen(peer net.Peer, verifyId bool) OnNewPeerListen(peer net.Peer, verifyId bool)
GetNodeScore(nodeId *encoding.NodeId) (float64, error) GetNodeScore(nodeId *encoding.NodeId) (float64, error)

View File

@ -45,6 +45,7 @@ type Peer interface {
ConnectionURIs() []*url.URL ConnectionURIs() []*url.URL
IsHandshakeDone() bool IsHandshakeDone() bool
SetHandshakeDone(status bool) SetHandshakeDone(status bool)
GetIP() string
} }
type BasePeer struct { type BasePeer struct {
@ -79,6 +80,10 @@ func (b *BasePeer) ListenForMessages(callback EventCallback, options ListenerOpt
func (b *BasePeer) End() error { func (b *BasePeer) End() error {
panic("must implement in child class") panic("must implement in child class")
} }
func (b *BasePeer) GetIP() string {
//TODO implement me
panic("must implement in child class")
}
func (b *BasePeer) Challenge() []byte { func (b *BasePeer) Challenge() []byte {
return b.challenge return b.challenge

View File

@ -108,7 +108,7 @@ func (a *AnnouncePeers) DecodeMessage(dec *msgpack.Decoder) error {
func (a AnnouncePeers) HandleMessage(node interfaces.Node, peer net.Peer, verifyId bool) error { func (a AnnouncePeers) HandleMessage(node interfaces.Node, peer net.Peer, verifyId bool) error {
if len(a.connectionUris) > 0 { if len(a.connectionUris) > 0 {
err := node.Services().P2P().ConnectToNode([]*url.URL{a.connectionUris[0]}, false) err := node.Services().P2P().ConnectToNode([]*url.URL{a.connectionUris[0]}, false, peer)
if err != nil { if err != nil {
return err return err
} }

View File

@ -33,17 +33,22 @@ var (
const nodeBucketName = "nodes" const nodeBucketName = "nodes"
type P2PImpl struct { type P2PImpl struct {
logger *zap.Logger logger *zap.Logger
nodeKeyPair *ed25519.KeyPairEd25519 nodeKeyPair *ed25519.KeyPairEd25519
localNodeID *encoding.NodeId localNodeID *encoding.NodeId
networkID string networkID string
nodesBucket *bolt.Bucket nodesBucket *bolt.Bucket
node interfaces.Node node interfaces.Node
inited bool inited bool
reconnectDelay structs.Map reconnectDelay structs.Map
peers structs.Map peers structs.Map
peersPending structs.Map peersPending structs.Map
selfConnectionUris []*url.URL selfConnectionUris []*url.URL
outgoingPeerBlocklist structs.Map
incomingPeerBlockList structs.Map
incomingIPBlocklist structs.Map
outgoingPeerFailures structs.Map
maxOutgoingPeerFailures uint
} }
func NewP2P(node interfaces.Node) *P2PImpl { func NewP2P(node interfaces.Node) *P2PImpl {
@ -53,15 +58,19 @@ func NewP2P(node interfaces.Node) *P2PImpl {
} }
service := &P2PImpl{ service := &P2PImpl{
logger: node.Logger(), logger: node.Logger(),
nodeKeyPair: node.Config().KeyPair, nodeKeyPair: node.Config().KeyPair,
networkID: node.Config().P2P.Network, networkID: node.Config().P2P.Network,
node: node, node: node,
inited: false, inited: false,
reconnectDelay: structs.NewMap(), reconnectDelay: structs.NewMap(),
peers: structs.NewMap(), peers: structs.NewMap(),
peersPending: structs.NewMap(), peersPending: structs.NewMap(),
selfConnectionUris: []*url.URL{uri}, selfConnectionUris: []*url.URL{uri},
outgoingPeerBlocklist: structs.NewMap(),
incomingPeerBlockList: structs.NewMap(),
incomingIPBlocklist: structs.NewMap(),
maxOutgoingPeerFailures: node.Config().P2P.MaxOutgoingPeerFailures,
} }
return service return service
@ -92,7 +101,7 @@ func (p *P2PImpl) Start() error {
peer := peer peer := peer
go func() { go func() {
err := p.ConnectToNode([]*url.URL{u}, false) err := p.ConnectToNode([]*url.URL{u}, false, nil)
if err != nil { if err != nil {
p.logger.Error("failed to connect to initial peer", zap.Error(err), zap.String("peer", peer)) p.logger.Error("failed to connect to initial peer", zap.Error(err), zap.String("peer", peer))
} }
@ -123,7 +132,7 @@ func (p *P2PImpl) Init() error {
return nil return nil
} }
func (p *P2PImpl) ConnectToNode(connectionUris []*url.URL, retried bool) error { func (p *P2PImpl) ConnectToNode(connectionUris []*url.URL, retried bool, fromPeer net.Peer) error {
if !p.Node().IsStarted() { if !p.Node().IsStarted() {
return nil return nil
} }
@ -179,6 +188,11 @@ func (p *P2PImpl) ConnectToNode(connectionUris []*url.URL, retried bool) error {
return nil return nil
} }
if p.outgoingPeerFailures.Contains(idString) {
p.logger.Error("outgoing peer is on blocklist", zap.String("node", connectionUri.String()))
return nil
}
reconnectDelay := p.reconnectDelay.GetInt(idString) reconnectDelay := p.reconnectDelay.GetInt(idString)
if reconnectDelay == nil { if reconnectDelay == nil {
delay := 1 delay := 1
@ -195,6 +209,28 @@ func (p *P2PImpl) ConnectToNode(connectionUris []*url.URL, retried bool) error {
if err != nil { if err != nil {
if retried { if retried {
p.logger.Error("failed to connect, too many retries", zap.String("node", connectionUri.String()), zap.Error(err)) p.logger.Error("failed to connect, too many retries", zap.String("node", connectionUri.String()), zap.Error(err))
counter := uint(0)
if p.outgoingPeerFailures.Contains(idString) {
tmp := *p.outgoingPeerFailures.GetInt(idString)
counter = uint(tmp)
}
counter++
p.outgoingPeerFailures.Put(idString, counter)
if counter >= p.maxOutgoingPeerFailures {
fromPeerId, err := fromPeer.Id().ToString()
if err != nil {
return err
}
p.incomingPeerBlockList.Put(idString, fromPeerId)
err = fromPeer.End()
if err != nil {
return err
}
}
return nil return nil
} }
retried = true retried = true
@ -211,7 +247,11 @@ func (p *P2PImpl) ConnectToNode(connectionUris []*url.URL, retried bool) error {
time.Sleep(time.Duration(delayDeref) * time.Second) time.Sleep(time.Duration(delayDeref) * time.Second)
return p.ConnectToNode(connectionUris, retried) return p.ConnectToNode(connectionUris, retried, fromPeer)
}
if p.outgoingPeerFailures.Contains(idString) {
p.outgoingPeerFailures.Remove(idString)
} }
peer, err := net.CreateTransportPeer(scheme, &net.TransportPeerConfig{ peer, err := net.CreateTransportPeer(scheme, &net.TransportPeerConfig{
@ -256,6 +296,25 @@ func (p *P2PImpl) OnNewPeer(peer net.Peer, verifyId bool) error {
pid = "unknown" pid = "unknown"
} }
pip := peer.GetIP()
if p.incomingIPBlocklist.Contains(pid) {
p.logger.Error("peer is on identity blocklist", zap.String("peer", pid))
err := peer.End()
if err != nil {
return err
}
return nil
}
if p.incomingPeerBlockList.Contains(pip) {
p.logger.Debug("peer is on ip blocklist", zap.String("peer", pid), zap.String("ip", pip))
err := peer.End()
if err != nil {
return err
}
return nil
}
p.logger.Debug("OnNewPeer started", zap.String("peer", pid)) p.logger.Debug("OnNewPeer started", zap.String("peer", pid))
challenge := protocol.GenerateChallenge() challenge := protocol.GenerateChallenge()