feat: add incoming and outgoing peer blocking to handle abuse
This commit is contained in:
parent
d79455c68c
commit
883f50b198
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
105
service/p2p.go
105
service/p2p.go
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue