Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Peer whitelist #6565

Closed
obo20 opened this issue Aug 12, 2019 · 17 comments
Closed

Peer whitelist #6565

obo20 opened this issue Aug 12, 2019 · 17 comments
Labels
kind/feature A new feature

Comments

@obo20
Copy link

obo20 commented Aug 12, 2019

Feature Request: A way to whitelist the peers that your node can connect to.

This would ideally work similarly to the way that IPFS-cluster has a whitelist of cluster peers that you can connect to, except at an IPFS level instead of a cluster level.

@obo20 obo20 added the kind/feature A new feature label Aug 12, 2019
@lanzafame
Copy link
Contributor

This would ideally work similarly to the way that IPFS-cluster has a whitelist of cluster peers that you can connect to, except at an IPFS level instead of a cluster level.

Caveat, IPFS Cluster does not have a connection based whitelist but a rpc authorization whitelist. All other limiting of connections between IPFS Cluster peers occurs via the fact that they are on a separate libp2p network, aka private network.

In the case of IPFS, would the peer with the whitelist be able to create connections to peers outside of its whitelist? What benefit does this provide over a straight private network or what is the use case that private networks don't solve but this does?

@obo20
Copy link
Author

obo20 commented Aug 14, 2019

@lanzafame The peer with the whitelist would not be able to create connections to peers outside of its whitelist.

The problem with private IPFS networks is that if the private secret gets exposed at all, any peer that knows of the secret can connect. By having the ability to whitelist peers, private networks would be able to have much more control over who can participate in the network.

@Stebalien
Copy link
Member

Depends on libp2p/go-maddr-filter#15. In general, I'm all for this. Ideally it would use IPNS over pubsub to distribute authenticated access control lists.

@lanzafame
Copy link
Contributor

By having the ability to whitelist peers, private networks would be able to have much more control over who can participate in the network.

So does that mean that this feature would only apply to private networks? I am struggling to understand how this would be able to work in conjunction with the public network. Note, I am just trying to determine the scope of this feature, not in anyway implying it isn't needed.

@Stebalien
Copy link
Member

I believe this is meant to be a separate feature (although you could also share a swarm key). Having a whitelist like this would allow peers to form private clusters where membership can easily be revoked.

@obo20
Copy link
Author

obo20 commented Aug 23, 2019

@Stebalien - Yes, that's the thought process behind things. This conversation might also be fairly relevant to the conversation happening in this issue: #6097

@obo20
Copy link
Author

obo20 commented Oct 17, 2019

From speaking with @raulk this seems to already be a feature in libp2p via the "Filters" functionality: https://godoc.org/github.com/libp2p/go-libp2p#Filters

It sounds like there might be a simple solution here of just exposing this functionality via the config file.

@olizilla
Copy link
Member

We could make use of PeerId based filtering when testing go-ipfs... i want to replay gateway traffic from a production node to a test node, and have the test node not connect to the prod node. The IP addresses of the production nodes can change, while the peerIDs remain stable, so it'd be nice to be able to filter on a Peer ID rather than an IP.

@KEINOS
Copy link

KEINOS commented Apr 20, 2022

+1 for this whitelist/allow list feature.

Even though I want to contribute extensively to the IPFS public network, I also want to block some inconvenient PeerIDs.

I have noticed that every time I launch a new peer through the Docker container, my home router receives a port scan. However, this does not happen when I launch the same image from my office. This occurs even if the daemon is started locally.

So, there is a sense of port scanning based on the IP address range. Whether it is an IP of an ISP for a corporation or an IP of an ISP for home users.

Port scans are routine and unavoidable. However, when comparing the IP address of the scanner with the peer connected, by ipfs id <peer id> one by one, there is a high probability that both IP addresses match. Mainly from China and Africa, but not all.

We are currently blocking them with a blacklist/deny list on our firewall from time to time, but I have a feeling we will probably hit the limit in the near future. Since the peer disappears in a few days.

It would be useful to simply be able to set the Allow Peer List of the home node by referencing the Peer List of the office node, which is relatively stable.


P.S.
Is there any way to measure if a PeerID is reliable/trustworthy? For example, the number of files in that IPNS, how long the PeerID has existed in the public IPFS swarm, PeerIDs signed and verifiable with public keys from GitHub, GitLab, etc. Maybe it's off-topic, sorry.

@Winterhuman
Copy link
Contributor

Winterhuman commented Apr 20, 2022

@KEINOS Peer whitelisting is already possible using Private Networks. Bare in mind that this would also prevent connecting to the default bootstrap nodes, you may want to look at Peering if you want a looser way of managing this.

Peer blacklisting isn't yet implemented as far as I'm aware though.

@KEINOS
Copy link

KEINOS commented Apr 21, 2022

@Winterhuman

Indeed. Private networks are the best option if we want strict control over who joins the network. My understanding is that it is allow-to-join rather than allow-to-connect.

But the thing is that I didn't want to keep it private. And to contribute caching to the IPFS public network.

In doing so, I wanted to exclude naughty PeerIDs from the connecting peer list. But deny listing would be weaselly, so I come to +1 to this AllowList feature. =)

Until the AllowList is implemented, my plan is to use one of my peers for observation and from there create a list of PeerIDs that have made similar contributions over time. This is because I personally believe that the PeerIDs that have been around for a long time are more reliable.

@Jorropo
Copy link
Contributor

Jorropo commented Oct 27, 2023

You can use resource manager for this, setting defaults values of allowed inbound connections to 0 and setting it to 1 for whitelisted peer: https://github.com/ipfs/kubo/blob/4f303d3208babbe7f5bb40a312a24d73dbf9c9dd/docs/libp2p-resource-management.md#user-supplied-override-limits

@Jorropo Jorropo closed this as completed Oct 27, 2023
@sevenrats
Copy link

You can use resource manager for this, setting defaults values of allowed inbound connections to 0 and setting it to 1 for whitelisted peer: https://github.com/ipfs/kubo/blob/4f303d3208babbe7f5bb40a312a24d73dbf9c9dd/docs/libp2p-resource-management.md#user-supplied-override-limits

if this configuration changes on disk, does kubo need to be reloaded? if so, is there a way around this?

@sevenrats
Copy link

sevenrats commented Dec 14, 2023

it would also be great to have an example of how to do this.
is this correct?

// {
//   "System": {
//     "ConnsInbound": 0,
//     "ConnsOutbound": 0
//   },
//   "Peer": {
//     "QmVvtzcZgCkMnSFf2dnrBPXrWuNFWNM9J3MpZQCvWPuVZf": {
//       "ConnsInbound": 1,
//       "ConnsOutbound: 1
//     }
//   }
// }

@morandalex
Copy link

News on that?

@sevenrats
Copy link

sevenrats commented Mar 18, 2024

@morandalex
I was not able to produce the desired result, but I can make you glad you asked. below is the content of core/node/libp2p/filters.go Make sure that peer.dat is IN MEMORY because this code makes no effort not to stat the living heck out of the file. This is a hack, but it works.
The structure of peer.json is

{peerID: UnixTimeStamp} 

where the gater should reject the connection after the timestamp, and the timestamp 0 means "never expire"
probably connection to an expired peer needs to get dropped. this is likely a bug in the code as it exists.

package libp2p

import (
	"github.com/libp2p/go-libp2p/core/connmgr"
	"github.com/libp2p/go-libp2p/core/control"
	"github.com/libp2p/go-libp2p/core/network"
	"github.com/libp2p/go-libp2p/core/peer"

	ma "github.com/multiformats/go-multiaddr"

	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"time"
)

var (
	permitted map[string]string
	filePath  = "/peer.dat"
	lastMTime time.Time
)

// filtersConnectionGater is an adapter that turns multiaddr.Filter into a
// connmgr.ConnectionGater.
type filtersConnectionGater ma.Filters

var _ connmgr.ConnectionGater = (*filtersConnectionGater)(nil)

func getFileMTime() (time.Time, error) {
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return time.Time{}, err
	}
	return fileInfo.ModTime(), nil
}

func loadPermitted() error {

	newMTime, err := getFileMTime()
	if err != nil {
		fmt.Println("ERROR GETTING MTIME")
		fmt.Println(err)
		return err
	}
	if newMTime.After(lastMTime) {
		fmt.Println("THE PEER FILE HAS BEEN MODIFIED SINCE I CHECKED")
		bytes, err := os.ReadFile(filePath)
		if err != nil {
			fmt.Println("THERE WAS AN ERROR READING THE FILE")
			fmt.Println(err)
			return err
		}

		permitted = make(map[string]string) // Zero out the map

		err = json.Unmarshal(bytes, &permitted) // Populate the map with data
		if err != nil {
			fmt.Println("ERROR UNMARSHALLING JSON")
			fmt.Println(err)
			return err
		}
		lastMTime = newMTime
	}
	return nil
}

func isPermitted(peer string) (bool, error) {
	err := loadPermitted()
	if err != nil {
		fmt.Println(err)
		return false, err
	}
	t, ok := permitted[peer]
	if !ok {
		return false, fmt.Errorf("Peer %s is not authorized", peer)
	}
	timestamp, err := strconv.ParseInt(t, 10, 64)
	if err != nil {
		fmt.Println("Error:", err)
		return false, err
	}
	currentTime := time.Now().Unix()
	return timestamp == 0 || timestamp > currentTime, nil
}

func (f *filtersConnectionGater) InterceptAddrDial(p peer.ID, addr ma.Multiaddr) (allow bool) {
	allow, err := isPermitted(p.String())
	if err != nil {
		return false
	}
	if !allow {
		fmt.Printf("InterceptAddrDial is blocking peer %s\n.", p.String())
	}
	return allow && !(*ma.Filters)(f).AddrBlocked(addr)
}

func (f *filtersConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
	allow, err := isPermitted(p.String())
	if err != nil {
		return false
	}
	if !allow {
		fmt.Printf("InterceptPeerDial is blocking peer %s\n.", p.String())
	}
	return allow
}

func (f *filtersConnectionGater) InterceptAccept(connAddr network.ConnMultiaddrs) (allow bool) {
	return !(*ma.Filters)(f).AddrBlocked(connAddr.RemoteMultiaddr())
}

func (f *filtersConnectionGater) InterceptSecured(_ network.Direction, p peer.ID, connAddr network.ConnMultiaddrs) (allow bool) {
	allow, err := isPermitted(p.String())
	if err != nil {
		return false
	}
	if !allow {
		fmt.Printf("InterceptSecured is blocking peer %s\n.", p.String())
	}
	return allow && !(*ma.Filters)(f).AddrBlocked(connAddr.RemoteMultiaddr())
}

func (f *filtersConnectionGater) InterceptUpgraded(_ network.Conn) (allow bool, reason control.DisconnectReason) {
	return true, 0
}

@Jorropo
Copy link
Contributor

Jorropo commented Mar 18, 2024

@sevenrats I missed this #6565 (comment) I didn't thought about hot reloading.
I don't work for ipfs-shipyard anymore so I wont review such PR but I think something like you did would be a great PR. Might want to use notify like the content block list: https://github.com/search?q=repo%3Aipfs-shipyard%2Fnopfs%20fsnotify&type=code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature A new feature
Projects
None yet
Development

No branches or pull requests

9 participants