Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Include a letsencrypt client in the node for the WebSocket listening endpoint #6911

Closed
tomaka opened this issue Aug 18, 2020 · 15 comments
Closed

Comments

@tomaka
Copy link
Contributor

tomaka commented Aug 18, 2020

We do want to provide support for light clients running in a browser.

The only two ways a browser can communicate with the outside is through HTTP, WebSockets, or WebRTC. WebRTC uses signalling servers, which in turn use HTTP or WebSockets. In other words, everything ultimately requires the browser to make an HTTP or WebSockets request.

Unfortunately, browsers are more and more annoying strict when it comes to enforcing proper security. Non-secure HTTP and non-secure WebSockets are more and more forbidden.
The only sensible solution way forward is to actually provide valid certificates to browsers that connect to Substrate nodes. The most straight-forward way is by embedding a letsencrypt client.

As I'm personally not very well-versed into letsencrypt, it is unclear to me which domain exactly we'd use, if there are restrictions, and so on.

@tomaka
Copy link
Contributor Author

tomaka commented Aug 18, 2020

A notable alternative could be to provide a signalling server with a proper certificate, and add support for WebRTC in the nodes instead.

@expenses
Copy link
Contributor

So I think that using a generic ACME library like https://crates.io/crates/acme-lib would be best, that way we can be generic over the certificate provider.

@expenses
Copy link
Contributor

My idea from element was to have a registered domain name, let's say substrate-nodes.com. This domain name could be used used to generate certificates for <peer_id>.substrate-nodes.com. We'd need something like this:

  1. A node sends a message to a server (centralized, unfortunately. Maybe there's a way of decentralizing this?) that has access to the DNS records of substrate-nodes.com. This message tells the server to add a new record of <peer_id>.substrate-nodes.com.
  2. The node orders a new certificate for <peer_id>.substrate.nodes.com. It responds to the http challenge that is send to the subdomain.
  3. The node uses the new certificate for secure websockets.

@s3krit
Copy link
Contributor

s3krit commented Aug 18, 2020

@expenses I think this solution would work in general but there are a few caveats imo:

  1. A node sends a message to a server (centralized, unfortunately. Maybe there's a way of decentralizing this?) that has access to the DNS records of substrate-nodes.com. This message tells the server to add a new record of <peer_id>.substrate-nodes.com.

What we'd essentially be doing here is running our own nameserver where arbitrary users would be able to register subdomains. That's not crazy, there are plenty of services on the 'net that do that already. But this is a service we would have to run and maintain, and make sure it's not abused ('hey register me, my node id is "substrate-stinks"'). Again, not impossible but not trivial and introduces significant operational overhead.

  1. The node orders a new certificate for <peer_id>.substrate.nodes.com. It responds to the http challenge that is send to the subdomain.

Depending on DNS propagation, it could take a little time for that challenge to get sent to the right IP. Though for new subdomains you're talking seconds rather than minutes or hours, so not really a problem.

If we would be using something like LetsEncrypt, we'd also likely run into rate limits very quickly - LetsEncrypt only allow 50 certs per week (you can work around that with multi-domain certs). Though I wonder if we could avoid issuing certs at all by using a wildcard cert - the question is, are we wanting to use certificates here to actually enhance security, or are we doing it to just get around the restrictions put in place by browsers (not a facetious question, I really don't know)?

@expenses
Copy link
Contributor

@s3krit We have security provided by cryptography and chain economics already, so we don't need certificates to provide that. We really just want to get around these browser restrictions in the least hacky way possible.

@s3krit
Copy link
Contributor

s3krit commented Aug 18, 2020

@expenses I suspected that might be the case, just wanted to clarify :P

@expenses
Copy link
Contributor

@s3krit If you used a wildcard cert, wouldn't you need to give the private key to every node? Which would mean you run into this issue: https://letsencrypt.org/docs/certificates-for-localhost/#for-native-apps-talking-to-web-apps

This is considered a compromise of your private key, and your Certificate Authority (CA) is required to revoke your certificate if they become aware of it. Many native apps have had their certificates revoked for shipping their private key.

@s3krit
Copy link
Contributor

s3krit commented Aug 18, 2020

Good point. While I don't think this would actually be a risk if it were set up correctly, I wasn't aware that CAs were required to revoke certificates for which the private key is exposed.

@burdges
Copy link

burdges commented Aug 19, 2020

Are we only trying to communicate with 127.0.0.1 here? I'll assume no..

We ultimately authenticate data originating from the chain with grandpa, but we do want encryption too since these connections contents link user IP addresses with on-chain data, right?

We could bundle letsencrypt with nodes somehow, which then obtains a certificates for some transport layer key attached to the node's IP address. We then also do a secondary authentication of this transport layer key from the chain too inside whatever code trusts it, assuming the browser lets you query details about the connection.

In principle, we might hack merkle roots from the chain into some certificate and then verify them by miss-useing a hash based signature scheme, but I think https://tools.ietf.org/html/draft-vangeest-x509-hash-sigs-03 does not really exist yet, and we'd run into these certificate issuing limits fast.

@tomaka
Copy link
Contributor Author

tomaka commented Aug 19, 2020

@burdges We don't want to do anything smart with these certificates. The objective is purely that the browser stops being a pain in the ass and lets us connect to nodes.

@2075
Copy link
Contributor

2075 commented Sep 19, 2020

maybe a docker-compose with a small nginx / le setup could solve this more easy and basic info would be stored in a .env / .env.sample to make it usable in individual setups.

@Stefie
Copy link
Contributor

Stefie commented Sep 21, 2020

@2075 We want to bundle the light client into a node module and people without technical knowledge should be able to seamlessly use the light client when interacting with a website that's using the module, so unfortunately relying on docker is not an option.

@tomaka
Copy link
Contributor Author

tomaka commented Oct 12, 2020

Only the full nodes need the letsencrypt client, so a docker container could be an option.
But even for full nodes/validators, it would be better to make it as easy as possible, especially considering that they have no reason to open their WebSocket listening endpoint except pure goodwill.

@tomaka
Copy link
Contributor Author

tomaka commented Oct 29, 2020

cc libp2p/go-libp2p#188

@tomaka
Copy link
Contributor Author

tomaka commented Oct 30, 2020

This letsencrypt idea is very experimental, and, in my opinion, will bring a lot of troubles. It's an idea without any precise design, and in my opinion this would be the start of a trail full of issues.

Instead I'd suggest we go another route, and I'm closing in favour of paritytech/polkadot-sdk#547

@tomaka tomaka closed this as completed Oct 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants