-
Notifications
You must be signed in to change notification settings - Fork 283
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
Document dialing peer without their peer ID #458
Comments
cc @Stebalien (I vaguely remember we talked about this years ago in context of |
I'm honestly surprised to get push back on this. If they are made aware of the risks, let your users do what they want to do. "Who can do more can do less." In nim-libp2p's cases, you have to use a separate procedure to connect to someone without it's PeerId, and it's being made very clear that you don't know it's PeerId. Anyway, I'll respond here. For of all, keep in mind that in some libp2p networks, we don't care that much about PeerId. In nimbus, the peer management looks like this: while gossipSubNotHealthy():
let peer = await discoverRandomPeer()
await peer.connect()
You can trust IPs in some scenarios, "secured network" (as in, VPNs, or local networks with secured access. If someone has physical access to your server, most of the regular security assumptions go out of the window anyway)
Ok so let's see the chain of trust.
So basically, as a user, I have to trust the developer, which will then trust* someone holding a private key for that PeerId. (here, this is actually the same entity developing the application and holding these private key, but that's not always the case) Now, if that private key gets compromised, any DNS provider could redirect the flow to it's own nodes, and the only way to fix it is push a new version of your binary. You have no way to revoke it on the fly etc. Now, let's imagine that you just have
So the chain of trust, and security properties don't change that much, I think. However, you can now cycle your peer ids, revoke & add nodes more easily, etc. Up to a point, where, as show here, you can forego the dedicated bootstrap servers, and instead just put a list of every node you can find in that DNS record.
|
Not pushing back. I just want us to be very clear on the security properties (or lack thereof) that this entails, and it seems like there's some confusion here. Dialing without a peer ID is equivalent to saying that you'll trust any peer ID that is presented to you. The root problem is that there's no authentication at the IP layer, and just because you sent a packet to 1.2.3.4 and got a reply from 1.2.3.4 doesn't mean that it was the server you deployed at 1.2.3.4 that gave the answer. Any attacker sitting on the path between you and 1.2.3.4 could have intercepted the handshake, either by MITM-ing it, or by just pretending to be that server altogether. Now is this always a bad idea? No, if you genuinely don't care about peer IDs, then it's probably fine. Your application protocol will (hopefully!) have some other way of authentication that happens after the libp2p handshake. If you don't have that, any middlebox can just intercept all your connection and see the plaintext of all your communication. |
Just to clarify: In the scenario where you dial
dnssec gives you that you can trust this data (as much as you can trust whoever is holding the private key for each of these PeerId). |
That's true. If you load the peer ID via DNS, then you'll to do DNSSEC and peer ID validation to authenticate the bootstrap node. |
Yes, but see my previous points:
Not trying to push for "one being better than the other", it's just two legitimate use cases, which different properties, but same security guarantees in fine, imo |
I agree that DNSSEC provides you the nice properties you list. My point is that no matter if you do DNS or DNSSEC, you need to verify the peer ID, otherwise an attacker can trivially MITM your connection and you lose all the security properties you're hoping for. |
We specifically added this in kubo for user convenience. However, this feature has no security implications as you're just asking kubo to form a connection to some random peer. If we did support this in libp2p, it would have to be in the same way. I.e., some generic
Please avoid arguments like this:
Basically, "why not" is never a good reason. |
That's how it's done in nim-libp2p: let peerId = await switch2.connect(someMultiaddresses) (In Nim you have to consume the return value of a function so you can't "ignore" the peer id)
Of course, I'm not forcing anyone to add something to their implementation, I was just surprised at the amount of push-back (now clarified as "not push back") for getting it in our implementation.
I think it's clear at this point that this feature, while it can be mis-used, does have real like applications. I thought that was already clear from my original slack messages, hence my response: "If some advanced users wants to do this, in situations where they have no other choice, and knowing that they are getting out of our safe boundaries, why stop them?" |
Lots of good discussion here. Thanks all. I'll add a couple thoughts to the pool as well:
This is similar to the "Castle and Moat" network security model. The idea that you can defer your security concerns to your VPN, is nice, but in practice a bit risky. The best practice is to move off this security model and towards a "Zero Trust" model.
I think if you're using DNSSEC, this is a great way to enable discovering a set of peers and their peer IDs. But the key here is that we must get their peer ids when we resolve the query. This subtlety should be obvious to the user since it has very different security properties. For example, if example.com's dnsaddr resolved to let peerId = await switch2.connect(/dnsaddr/example.com/) but if example.com resolved to Probably what would be a better interface is to discover peers+peerids from a dnsaddr query, then dial those addrs once you know if you have a peer id (or not). While I don't mean or want to block anyone's implementation from doing this if they want, I'm still curious what the use cases are. The three original use cases don't seem worth it to me since they seem to introduce foot guns or scary subtleties. To recap the three use cases:
This is the same as the "castle and moat" security model, which no longer recommended in general.
I think we can still support this use case by using a dnsaddr to discover multiple peers+peerids via dnssec (as mentioned above). You then don't need to dial without a peer id.
This seems interesting, but I agree unlikely. This seems specific/unlikely enough that a we wouldn't have to worry about supporting this as a first class use case. On the flip side, do we gain anything by knowing the peer id before hand? I think so. I think we could try alternate Noise handshakes that could reduce our RTTs. There may be other benefits here too. |
The discussion on whether one should be able to dial a peer without knowing their peer ID has come up many times.
Today two implementations (nim-libp2p and rust-libp2p) support dialing a peer without knowing their peer ID.
Copying use-cases from @Menduist from a recent discussion on Slack:
While I don't think we have nor will find consensus on whether an implementation supports dialing a peer without their peer ID, I do think there is value documenting that implementations may support this and the rational for it.
I think a short paragraph in https://github.com/libp2p/specs/blob/master/connections/README.md would suffice.
@Menduist would you want to tackle this?
The text was updated successfully, but these errors were encountered: