mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +00:00
cmd, node, p2p: implement whitelist and blacklist for peers (#1331)
This commit is contained in:
parent
0ceeb24fb3
commit
4ec6e8cd58
5 changed files with 133 additions and 5 deletions
|
|
@ -61,6 +61,8 @@ var (
|
||||||
utils.IdentityFlag,
|
utils.IdentityFlag,
|
||||||
utils.UnlockedAccountFlag,
|
utils.UnlockedAccountFlag,
|
||||||
utils.PasswordFileFlag,
|
utils.PasswordFileFlag,
|
||||||
|
utils.PeersWhitelistFlag,
|
||||||
|
utils.PeersBlacklistFlag,
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.BootnodesV4Flag,
|
utils.BootnodesV4Flag,
|
||||||
utils.BootnodesV5Flag,
|
utils.BootnodesV5Flag,
|
||||||
|
|
|
||||||
|
|
@ -639,6 +639,18 @@ var (
|
||||||
Value: 30303,
|
Value: 30303,
|
||||||
Category: flags.NetworkingCategory,
|
Category: flags.NetworkingCategory,
|
||||||
}
|
}
|
||||||
|
PeersWhitelistFlag = &cli.StringFlag{
|
||||||
|
Name: "peers-whitelist",
|
||||||
|
Usage: "Comma separated NodeID or enode URLs for peer whitelist (only connect to them)",
|
||||||
|
Value: "",
|
||||||
|
Category: flags.NetworkingCategory,
|
||||||
|
}
|
||||||
|
PeersBlacklistFlag = &cli.StringFlag{
|
||||||
|
Name: "peers-blacklist",
|
||||||
|
Usage: "Comma separated NodeID or enode URLs for peer blacklist (will not connect to them)",
|
||||||
|
Value: "",
|
||||||
|
Category: flags.NetworkingCategory,
|
||||||
|
}
|
||||||
BootnodesFlag = &cli.StringFlag{
|
BootnodesFlag = &cli.StringFlag{
|
||||||
Name: "bootnodes",
|
Name: "bootnodes",
|
||||||
Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)",
|
Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)",
|
||||||
|
|
@ -954,6 +966,73 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setWhiteBlackListPeers(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
|
CheckExclusive(ctx, PeersWhitelistFlag, PeersBlacklistFlag)
|
||||||
|
|
||||||
|
// setup whitelist for peers
|
||||||
|
if ctx.IsSet(PeersWhitelistFlag.Name) {
|
||||||
|
urls := SplitAndTrim(ctx.String(PeersWhitelistFlag.Name))
|
||||||
|
cfg.WhitePeers = make(map[discover.NodeID]struct{}, len(urls))
|
||||||
|
for _, url := range urls {
|
||||||
|
if url != "" {
|
||||||
|
node1, err1 := discover.HexID(url)
|
||||||
|
if err1 == nil {
|
||||||
|
cfg.WhitePeers[node1] = struct{}{}
|
||||||
|
log.Info("Add peer to whitelist", "id", node1.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node2, err2 := discover.ParseNode(url)
|
||||||
|
if err2 == nil {
|
||||||
|
cfg.WhitePeers[node2.ID] = struct{}{}
|
||||||
|
log.Info("Add peer to whitelist", "enode", url, "id", node2.ID.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Crit("Invalid peer id for whitelist", "url", url, "err1", err1, "err2", err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup blacklist for peers
|
||||||
|
if ctx.IsSet(PeersBlacklistFlag.Name) {
|
||||||
|
urls := SplitAndTrim(ctx.String(PeersBlacklistFlag.Name))
|
||||||
|
cfg.BlackPeers = make(map[discover.NodeID]struct{}, len(urls))
|
||||||
|
for _, url := range urls {
|
||||||
|
if url != "" {
|
||||||
|
node1, err1 := discover.HexID(url)
|
||||||
|
if err1 == nil {
|
||||||
|
cfg.BlackPeers[node1] = struct{}{}
|
||||||
|
log.Info("Add peer to blacklsit", "id", node1.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node2, err2 := discover.ParseNode(url)
|
||||||
|
if err2 == nil {
|
||||||
|
cfg.BlackPeers[node2.ID] = struct{}{}
|
||||||
|
log.Info("Add peer to blacklsit", "enode", url, "id", node2.ID.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Crit("Invalid peer id for blacklist", "url", url, "err1", err1, "err2", err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeBlackPeers removes bootstrap nodes which is in peers blacklist
|
||||||
|
func removeBlackPeers(cfg *p2p.Config) {
|
||||||
|
if len(cfg.BlackPeers) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredNodes := make([]*discover.Node, 0, len(cfg.BootstrapNodes))
|
||||||
|
for _, node := range cfg.BootstrapNodes {
|
||||||
|
if _, ok := cfg.BlackPeers[node.ID]; ok {
|
||||||
|
log.Info("Remove black peer", "enode", node.String(), "id", node.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filteredNodes = append(filteredNodes, node)
|
||||||
|
}
|
||||||
|
cfg.BootstrapNodes = filteredNodes
|
||||||
|
}
|
||||||
|
|
||||||
// setBootstrapNodes creates a list of bootstrap nodes from the command line
|
// setBootstrapNodes creates a list of bootstrap nodes from the command line
|
||||||
// flags, reverting to pre-configured ones if none have been specified.
|
// flags, reverting to pre-configured ones if none have been specified.
|
||||||
// Priority order for bootnodes configuration:
|
// Priority order for bootnodes configuration:
|
||||||
|
|
@ -1249,6 +1328,8 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
setNAT(ctx, cfg)
|
setNAT(ctx, cfg)
|
||||||
setListenAddress(ctx, cfg)
|
setListenAddress(ctx, cfg)
|
||||||
setBootstrapNodes(ctx, cfg)
|
setBootstrapNodes(ctx, cfg)
|
||||||
|
setWhiteBlackListPeers(ctx, cfg)
|
||||||
|
removeBlackPeers(cfg)
|
||||||
// setBootstrapNodesV5(ctx, cfg)
|
// setBootstrapNodesV5(ctx, cfg)
|
||||||
|
|
||||||
if ctx.IsSet(MaxPeersFlag.Name) {
|
if ctx.IsSet(MaxPeersFlag.Name) {
|
||||||
|
|
|
||||||
24
node/api.go
24
node/api.go
|
|
@ -65,6 +65,18 @@ func (api *adminAPI) AddPeer(url string) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("invalid enode: %v", err)
|
return false, fmt.Errorf("invalid enode: %v", err)
|
||||||
}
|
}
|
||||||
|
// only accept the node which is in peer whitelist if the list is not empty
|
||||||
|
if len(server.WhitePeers) > 0 {
|
||||||
|
if _, ok := server.WhitePeers[node.ID]; !ok {
|
||||||
|
return false, fmt.Errorf("peer is not in whitelist: %v, ID: %s", url, node.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reject the node which is in peer blacklist
|
||||||
|
if len(server.BlackPeers) > 0 {
|
||||||
|
if _, ok := server.BlackPeers[node.ID]; ok {
|
||||||
|
return false, fmt.Errorf("peer is in blacklist: %v, ID: %s", url, node.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
server.AddPeer(node)
|
server.AddPeer(node)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
@ -96,6 +108,18 @@ func (api *adminAPI) AddTrustedPeer(url string) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("invalid enode: %v", err)
|
return false, fmt.Errorf("invalid enode: %v", err)
|
||||||
}
|
}
|
||||||
|
// only accept the node which is in peer whitelist if the list is not empty
|
||||||
|
if len(server.WhitePeers) > 0 {
|
||||||
|
if _, ok := server.WhitePeers[node.ID]; !ok {
|
||||||
|
return false, fmt.Errorf("trusted peer is not in whitelist: %v, ID: %s", url, node.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reject the node which is in peer blacklist
|
||||||
|
if len(server.BlackPeers) > 0 {
|
||||||
|
if _, ok := server.BlackPeers[node.ID]; ok {
|
||||||
|
return false, fmt.Errorf("trusted peer is in blacklist: %v, ID: %s", url, node.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
server.AddTrustedPeer(node)
|
server.AddTrustedPeer(node)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ const (
|
||||||
DiscSelf
|
DiscSelf
|
||||||
DiscReadTimeout
|
DiscReadTimeout
|
||||||
DiscPairPeerStop
|
DiscPairPeerStop
|
||||||
DiscSubprotocolError = 0x10
|
DiscNonWhitelistedPeer
|
||||||
|
DiscBlacklistedPeer
|
||||||
|
DiscSubprotocolError = DiscReason(0x10)
|
||||||
)
|
)
|
||||||
|
|
||||||
var discReasonToString = [...]string{
|
var discReasonToString = [...]string{
|
||||||
|
|
@ -89,11 +91,13 @@ var discReasonToString = [...]string{
|
||||||
DiscSelf: "connected to self",
|
DiscSelf: "connected to self",
|
||||||
DiscReadTimeout: "read timeout",
|
DiscReadTimeout: "read timeout",
|
||||||
DiscPairPeerStop: "pair peer connection stop",
|
DiscPairPeerStop: "pair peer connection stop",
|
||||||
|
DiscNonWhitelistedPeer: "disconnect non-whitelisted peer",
|
||||||
|
DiscBlacklistedPeer: "disconnect blacklisted peer",
|
||||||
DiscSubprotocolError: "subprotocol error",
|
DiscSubprotocolError: "subprotocol error",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DiscReason) String() string {
|
func (d DiscReason) String() string {
|
||||||
if len(discReasonToString) <= int(d) || int(d) < 0 {
|
if len(discReasonToString) <= int(d) || int(d) < 0 || discReasonToString[int(d)] == "" {
|
||||||
return fmt.Sprintf("unknown disconnect reason %d", d)
|
return fmt.Sprintf("unknown disconnect reason %d", d)
|
||||||
}
|
}
|
||||||
return discReasonToString[int(d)]
|
return discReasonToString[int(d)]
|
||||||
|
|
@ -107,7 +111,7 @@ func discReasonForError(err error) DiscReason {
|
||||||
if reason, ok := err.(DiscReason); ok {
|
if reason, ok := err.(DiscReason); ok {
|
||||||
return reason
|
return reason
|
||||||
}
|
}
|
||||||
if err == errProtocolReturned {
|
if errors.Is(err, errProtocolReturned) {
|
||||||
return DiscQuitting
|
return DiscQuitting
|
||||||
}
|
}
|
||||||
peerError, ok := err.(*peerError)
|
peerError, ok := err.(*peerError)
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,11 @@ type Config struct {
|
||||||
// Name sets the node name of this server.
|
// Name sets the node name of this server.
|
||||||
Name string `toml:"-"`
|
Name string `toml:"-"`
|
||||||
|
|
||||||
|
// Whitelist for peers
|
||||||
|
WhitePeers map[discover.NodeID]struct{}
|
||||||
|
// Blacklist for peers.
|
||||||
|
BlackPeers map[discover.NodeID]struct{}
|
||||||
|
|
||||||
// BootstrapNodes are used to establish connectivity
|
// BootstrapNodes are used to establish connectivity
|
||||||
// with the rest of the network.
|
// with the rest of the network.
|
||||||
BootstrapNodes []*discover.Node
|
BootstrapNodes []*discover.Node
|
||||||
|
|
@ -892,7 +897,19 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
|
||||||
srv.log.Trace("Failed RLPx handshake", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err)
|
srv.log.Trace("Failed RLPx handshake", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
clog := srv.log.New("id", c.id, "addr", c.fd.RemoteAddr(), "conn", c.flags)
|
clog := srv.log.New("id", c.id.String(), "addr", c.fd.RemoteAddr(), "conn", c.flags)
|
||||||
|
if len(srv.WhitePeers) > 0 {
|
||||||
|
if _, ok := srv.WhitePeers[c.id]; !ok {
|
||||||
|
clog.Debug("Reject non-whitelisted peer")
|
||||||
|
return DiscNonWhitelistedPeer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(srv.BlackPeers) > 0 {
|
||||||
|
if _, ok := srv.BlackPeers[c.id]; ok {
|
||||||
|
clog.Debug("Reject blacklisted peer")
|
||||||
|
return DiscBlacklistedPeer
|
||||||
|
}
|
||||||
|
}
|
||||||
// For dialed connections, check that the remote public key matches.
|
// For dialed connections, check that the remote public key matches.
|
||||||
if dialDest != nil && c.id != dialDest.ID {
|
if dialDest != nil && c.id != dialDest.ID {
|
||||||
clog.Trace("Dialed identity mismatch", "want", c, dialDest.ID)
|
clog.Trace("Dialed identity mismatch", "want", c, dialDest.ID)
|
||||||
|
|
@ -921,7 +938,7 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
|
||||||
}
|
}
|
||||||
// If the checks completed successfully, runPeer has now been
|
// If the checks completed successfully, runPeer has now been
|
||||||
// launched by run.
|
// launched by run.
|
||||||
clog.Trace("connection set up", "inbound", dialDest == nil)
|
clog.Debug("Setup connection")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue