Skip to content

Commit

Permalink
Avoid using local and private IPs for autopeering (opt-in for private…
Browse files Browse the repository at this point in the history
… networks)

Only use the bootstrap peers for autopeering and moved them to the main config
  • Loading branch information
alexsporn committed Apr 22, 2024
1 parent bada59b commit 4e9d3d3
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 93 deletions.
96 changes: 24 additions & 72 deletions components/p2p/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ import (
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic"
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
"github.com/multiformats/go-multiaddr"
mamask "github.com/whyrusleeping/multiaddr-filter"
"go.uber.org/dig"

"github.com/iotaledger/hive.go/app"
Expand All @@ -21,7 +19,6 @@ import (
"github.com/iotaledger/hive.go/db"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/daemon"
"github.com/iotaledger/iota-core/pkg/network"
"github.com/iotaledger/iota-core/pkg/network/p2p"
Expand Down Expand Up @@ -218,7 +215,7 @@ func provide(c *dig.Container) error {
libp2p.NATPortMap(),
libp2p.DisableRelay(),
// Define a custom address factory to inject external addresses to the DHT advertisements.
libp2p.AddrsFactory(publicOnlyAddresses(ParamsP2P.Autopeering.ExternalMultiAddresses)),
libp2p.AddrsFactory(externalAddresses(ParamsP2P.Autopeering.ExternalMultiAddresses, ParamsP2P.Autopeering.AllowLocalIPs)),
)
if err != nil {
Component.LogFatalf("unable to initialize libp2p host: %s", err)
Expand Down Expand Up @@ -246,27 +243,11 @@ func provide(c *dig.Container) error {
}

return c.Provide(func(inDeps p2pManagerDeps) network.Manager {
peersMultiAddresses, err := getMultiAddrsFromString(ParamsPeers.BootstrapPeers)
if err != nil {
Component.LogFatalf("Failed to parse bootstrapPeers param: %s", err)
}

for _, multiAddr := range peersMultiAddresses {
bootstrapPeer, err := network.NewPeerFromMultiAddr(multiAddr)
if err != nil {
Component.LogFatalf("Failed to parse bootstrap peer multiaddress: %s", err)
}

if err := inDeps.PeerDB.UpdatePeer(bootstrapPeer); err != nil {
Component.LogErrorf("Failed to update bootstrap peer: %s", err)
}
}

onBlockSentCallback := func() {
inDeps.P2PMetrics.OutgoingBlocks.Add(1)
}

return p2p.NewManager(Component.Logger, inDeps.Host, inDeps.PeerDB, ParamsP2P.Autopeering.MaxPeers, onBlockSentCallback)
return p2p.NewManager(Component.Logger, inDeps.Host, inDeps.PeerDB, ParamsP2P.Autopeering.MaxPeers, ParamsP2P.Autopeering.AllowLocalIPs, onBlockSentCallback)
})
}

Expand Down Expand Up @@ -307,7 +288,7 @@ func run() error {
if err := Component.Daemon().BackgroundWorker(Component.Name, func(ctx context.Context) {
defer deps.NetworkManager.Shutdown()

if err := deps.NetworkManager.Start(ctx, deps.Protocol.LatestAPI().ProtocolParameters().NetworkName()); err != nil {
if err := deps.NetworkManager.Start(ctx, deps.Protocol.LatestAPI().ProtocolParameters().NetworkName(), bootstrapPeers()); err != nil {
Component.LogFatalf("Failed to start p2p manager: %s", err)
}

Expand All @@ -322,6 +303,24 @@ func run() error {
return nil
}

func bootstrapPeers() []peer.AddrInfo {
peersMultiAddresses, err := getMultiAddrsFromString(ParamsP2P.Autopeering.BootstrapPeers)
if err != nil {
Component.LogFatalf("Failed to parse bootstrapPeers param: %s", err)
}

addrInfos := make([]peer.AddrInfo, 0, len(peersMultiAddresses))
for _, multiAddr := range peersMultiAddresses {
addrInfo, err := peer.AddrInfoFromP2pAddr(multiAddr)
if err != nil {
Component.LogFatalf("Failed to parse bootstrap peer multiaddress: %s", err)
}
addrInfos = append(addrInfos, *addrInfo)
}

return addrInfos
}

func getMultiAddrsFromString(peers []string) ([]multiaddr.Multiaddr, error) {
peersMultiAddresses := make([]multiaddr.Multiaddr, 0, len(peers))

Expand Down Expand Up @@ -356,36 +355,7 @@ func connectConfigKnownPeers() {
}
}

// Based on https://github.com/ipfs/kubo/blob/master/config/profile.go
// defaultServerFilters has is a list of IPv4 and IPv6 prefixes that are private, local only, or unrouteable.
// according to https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
// and https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
var reservedFilters = []string{
"/ip4/0.0.0.0/ipcidr/32",
"/ip4/10.0.0.0/ipcidr/8",
"/ip4/100.64.0.0/ipcidr/10",
"/ip4/127.0.0.0/ipcidr/8",
"/ip4/169.254.0.0/ipcidr/16",
"/ip4/172.16.0.0/ipcidr/12",
"/ip4/192.0.0.0/ipcidr/24",
"/ip4/192.0.2.0/ipcidr/24",
"/ip4/192.168.0.0/ipcidr/16",
"/ip4/192.31.196.0/ipcidr/24",
"/ip4/192.52.193.0/ipcidr/24",
"/ip4/198.18.0.0/ipcidr/15",
"/ip4/198.51.100.0/ipcidr/24",
"/ip4/203.0.113.0/ipcidr/24",
"/ip4/240.0.0.0/ipcidr/4",

"/ip6/::1/ipcidr/64",
"/ip6/100::/ipcidr/64",
"/ip6/2001:2::/ipcidr/48",
"/ip6/2001:db8::/ipcidr/32",
"/ip6/fc00::/ipcidr/7",
"/ip6/fe80::/ipcidr/10",
}

func publicOnlyAddresses(additionalMultiaddresses []string) p2pbhost.AddrsFactory {
func externalAddresses(additionalMultiaddresses []string, allowLocalNetworks bool) network.AddressFilter {
var externalMultiAddrs []multiaddr.Multiaddr

// Add the external multi addresses to the list of addresses to be announced.
Expand All @@ -400,27 +370,9 @@ func publicOnlyAddresses(additionalMultiaddresses []string) p2pbhost.AddrsFactor
}
}

// Create a filter that blocks localhost and reserved addresses.
filters := multiaddr.NewFilters()
for _, addr := range reservedFilters {
f, err := mamask.NewMask(addr)
if err != nil {
Component.LogPanicf("unable to parse ip mask filter %s: %s", addr, err)
}
filters.AddFilter(*f, multiaddr.ActionDeny)
}
publicFilter := network.PublicOnlyAddressesFilter(allowLocalNetworks)

return func(addresses []multiaddr.Multiaddr) []multiaddr.Multiaddr {
filteredAddresses := lo.Filter(append(addresses, externalMultiAddrs...), func(m multiaddr.Multiaddr) bool {
blocked := filters.AddrBlocked(m)
if blocked {
Component.LogTracef("Filtered out address %s", m)
}
return !blocked
})

Component.LogTracef("Announcing addresses: %v", filteredAddresses)

return filteredAddresses
return publicFilter(append(addresses, externalMultiAddrs...))
}
}
8 changes: 6 additions & 2 deletions components/p2p/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ type ParametersP2P struct {
// MaxPeers defines the max number of auto-peer connections. Set to 0 to disable auto-peering.
MaxPeers int `default:"5" usage:"the max number of auto-peer connections. Set to 0 to disable auto-peering."`

// Defines the peers to be used as discovery for other peers (CLI).
BootstrapPeers []string `default:"" usage:"peers to be used as discovery for other peers"`

// AllowLocalIPs defines if local IPs are allowed to be used for autopeering.
AllowLocalIPs bool `default:"false" usage:"allow local IPs to be used for autopeering"`

// ExternalMultiAddress defines additional p2p multiaddresses to be advertised via DHT.
ExternalMultiAddresses []string `default:"" usage:"external reacheable multi addresses advertised to the network"`
}
Expand All @@ -45,8 +51,6 @@ type ParametersPeers struct {
Peers []string `default:"" usage:"the static peers this node should retain a connection to (CLI)"`
// Defines the aliases of the static peers (must be the same length like CfgP2PPeers) (CLI).
PeerAliases []string `default:"" usage:"the aliases of the static peers (must be the same amount like \"p2p.peers\""`
// Defines the peers to be used as discovery for other peers (CLI).
BootstrapPeers []string `default:"" usage:"peers to be used as discovery for other peers (CLI)"`
}

var (
Expand Down
2 changes: 2 additions & 0 deletions config_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"identityPrivateKey": "",
"autopeering": {
"maxPeers": 5,
"bootstrapPeers": [],
"allowLocalIPs": false,
"externalMultiAddresses": []
},
"db": {
Expand Down
12 changes: 8 additions & 4 deletions documentation/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ Example:

### <a id="p2p_autopeering"></a> Autopeering

| Name | Description | Type | Default value |
| ---------------------- | -------------------------------------------------------------------------- | ----- | ------------- |
| maxPeers | The max number of auto-peer connections. Set to 0 to disable auto-peering. | int | 5 |
| externalMultiAddresses | External reacheable multi addresses advertised to the network | array | |
| Name | Description | Type | Default value |
| ---------------------- | -------------------------------------------------------------------------- | ------- | ------------- |
| maxPeers | The max number of auto-peer connections. Set to 0 to disable auto-peering. | int | 5 |
| bootstrapPeers | Peers to be used as discovery for other peers | array | |
| allowLocalIPs | Allow local IPs to be used for autopeering | boolean | false |
| externalMultiAddresses | External reacheable multi addresses advertised to the network | array | |

### <a id="p2p_db"></a> Database

Expand All @@ -137,6 +139,8 @@ Example:
"identityPrivateKey": "",
"autopeering": {
"maxPeers": 5,
"bootstrapPeers": [],
"allowLocalIPs": false,
"externalMultiAddresses": []
},
"db": {
Expand Down
70 changes: 70 additions & 0 deletions pkg/network/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package network

import (
"fmt"

"github.com/multiformats/go-multiaddr"
mamask "github.com/whyrusleeping/multiaddr-filter"

"github.com/iotaledger/hive.go/lo"
)

// Based on https://github.com/ipfs/kubo/blob/master/config/profile.go
// defaultServerFilters has is a list of IPv4 and IPv6 prefixes that are private, local only, or unrouteable.
// according to https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
// and https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
var reservedFilters = []string{
"/ip4/0.0.0.0/ipcidr/32",
"/ip4/100.64.0.0/ipcidr/10",
"/ip4/127.0.0.0/ipcidr/8",
"/ip4/169.254.0.0/ipcidr/16",
"/ip4/192.0.0.0/ipcidr/24",
"/ip4/192.0.2.0/ipcidr/24",
"/ip4/192.31.196.0/ipcidr/24",
"/ip4/192.52.193.0/ipcidr/24",
"/ip4/198.18.0.0/ipcidr/15",
"/ip4/198.51.100.0/ipcidr/24",
"/ip4/203.0.113.0/ipcidr/24",
"/ip4/240.0.0.0/ipcidr/4",

"/ip6/::/ipcidr/128",
"/ip6/::1/ipcidr/128",
"/ip6/100::/ipcidr/64",
"/ip6/2001:2::/ipcidr/48",
"/ip6/2001:db8::/ipcidr/32",
}

var localNetworks = []string{
"/ip4/10.0.0.0/ipcidr/8",
"/ip4/172.16.0.0/ipcidr/12",
"/ip4/192.168.0.0/ipcidr/16",

"/ip6/fc00::/ipcidr/7",
"/ip6/fe80::/ipcidr/10",
}

type AddressFilter = func([]multiaddr.Multiaddr) []multiaddr.Multiaddr

func PublicOnlyAddressesFilter(allowLocalNetworks bool) AddressFilter {
// Create a filter that blocks localhost and reserved addresses.
filters := multiaddr.NewFilters()

filtersToApply := reservedFilters
if !allowLocalNetworks {
filtersToApply = append(filtersToApply, localNetworks...)
}

for _, addr := range filtersToApply {
f, err := mamask.NewMask(addr)
if err != nil {
panic(fmt.Sprintf("unable to parse ip mask filter %s: %s", addr, err))
}
filters.AddFilter(*f, multiaddr.ActionDeny)
}

return func(addresses []multiaddr.Multiaddr) []multiaddr.Multiaddr {
return lo.Filter(addresses, func(m multiaddr.Multiaddr) bool {
return !filters.AddrBlocked(m)
})
}
}
2 changes: 1 addition & 1 deletion pkg/network/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ type Manager interface {

P2PHost() host.Host

Start(ctx context.Context, networkID string) error
Start(ctx context.Context, networkID string, bootstrapPeers []peer.AddrInfo) error
Shutdown()
}
39 changes: 32 additions & 7 deletions pkg/network/p2p/autopeering/autopeering.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
dht "github.com/libp2p/go-libp2p-kad-dht"
"github.com/libp2p/go-libp2p/core/discovery"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/p2p/discovery/routing"
"github.com/libp2p/go-libp2p/p2p/discovery/util"
Expand All @@ -33,20 +34,22 @@ type Manager struct {
ctx context.Context
stopFunc context.CancelFunc
routingDiscovery *routing.RoutingDiscovery
addrFilter network.AddressFilter

advertiseLock sync.Mutex
advertiseCtx context.Context
advertiseCancel context.CancelFunc
}

// NewManager creates a new autopeering manager.
func NewManager(maxPeers int, networkManager network.Manager, host host.Host, peerDB *network.DB, logger log.Logger) *Manager {
func NewManager(maxPeers int, networkManager network.Manager, host host.Host, peerDB *network.DB, addressFilter network.AddressFilter, logger log.Logger) *Manager {
return &Manager{
maxPeers: maxPeers,
networkManager: networkManager,
host: host,
peerDB: peerDB,
logger: logger.NewChildLogger("Autopeering"),
addrFilter: addressFilter,
}
}

Expand All @@ -55,7 +58,7 @@ func (m *Manager) MaxNeighbors() int {
}

// Start starts the autopeering manager.
func (m *Manager) Start(ctx context.Context, networkID string) (err error) {
func (m *Manager) Start(ctx context.Context, networkID string, bootstrapPeers []peer.AddrInfo) (err error) {
//nolint:contextcheck
m.startOnce.Do(func() {
// We will use /iota/networkID/kad/1.0.0 for the DHT protocol.
Expand All @@ -64,7 +67,15 @@ func (m *Manager) Start(ctx context.Context, networkID string) (err error) {
extension := protocol.ID(fmt.Sprintf("/%s", networkID))
m.namespace = fmt.Sprintf("%s%s/%s", prefix, extension, network.CoreProtocolID)
dhtCtx, dhtCancel := context.WithCancel(ctx)
kademliaDHT, innerErr := dht.New(dhtCtx, m.host, dht.Mode(dht.ModeServer), dht.ProtocolPrefix(prefix), dht.ProtocolExtension(extension))
kademliaDHT, innerErr := dht.New(
dhtCtx,
m.host,
dht.Mode(dht.ModeServer),
dht.ProtocolPrefix(prefix),
dht.ProtocolExtension(extension),
dht.AddressFilter(m.addrFilter),
dht.BootstrapPeers(bootstrapPeers...),
)
if innerErr != nil {
err = innerErr
dhtCancel()
Expand Down Expand Up @@ -105,7 +116,8 @@ func (m *Manager) Start(ctx context.Context, networkID string) (err error) {
onGossipNeighborRemovedHook := m.networkManager.OnNeighborRemoved(func(_ network.Neighbor) {
m.startAdvertisingIfNeeded()
})
onGossipNeighborAddedHook := m.networkManager.OnNeighborAdded(func(_ network.Neighbor) {
onGossipNeighborAddedHook := m.networkManager.OnNeighborAdded(func(neighbor network.Neighbor) {
m.logger.LogInfof("Gossip layer successfully connected with the peer %s", neighbor.Peer())
m.stopAdvertisingItNotNeeded()
})

Expand Down Expand Up @@ -253,13 +265,26 @@ func (m *Manager) discoverAndDialPeers() {
continue
}

m.logger.LogDebugf("Found peer: %s", peerAddrInfo)
peerInfo := m.filteredPeerAddrInfo(&peerAddrInfo)
if len(peerInfo.Addrs) == 0 {
m.logger.LogWarnf("Filtered out peer %s because it has no public reachable addresses", peerAddrInfo)
continue
}

m.logger.LogInfof("Found peer: %s", peerInfo)

peer := network.NewPeerFromAddrInfo(&peerAddrInfo)
if err := m.networkManager.DialPeer(m.ctx, peer); err != nil {
p := network.NewPeerFromAddrInfo(peerInfo)
if err := m.networkManager.DialPeer(m.ctx, p); err != nil {
m.logger.LogWarnf("Failed to dial peer %s: %s", peerAddrInfo, err)
} else {
peersToFind--
}
}
}

func (m *Manager) filteredPeerAddrInfo(peerAddrInfo *peer.AddrInfo) *peer.AddrInfo {
return &peer.AddrInfo{
ID: peerAddrInfo.ID,
Addrs: m.addrFilter(peerAddrInfo.Addrs),
}
}
Loading

0 comments on commit 4e9d3d3

Please sign in to comment.