Can't connect to webrtc-star with ssl

Hi guys,

I’m having a problem setting up a webrtc-star with ssl. I’ve put it to run in an AWS without SSL, just like explained here https://github.com/libp2p/js-libp2p-webrtc-star#rendezvous-server-aka-signalling-server and it works correctly. But then, when deploying a website and connecting with https, it fails connecting to webrtc-star because it’s not secure.

websocket.js:120 Mixed Content: The page at '(my-website-url)' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://(aws-machine-ip):9090/socket.io/?EIO=3&transport=websocket'. This request has been blocked; this endpoint must be available over WSS.

Probably not setting up this correctly, so maybe I should ask how to do it instead.
I’ll be very thankful for any help you guys can give me :pray:

Cheers.

The browser will require SSL for websockets. The simplest thing to do would be to setup SSL with nginx and proxy to the signaling server. Instead of pointing to an IP address for your signaling server, you’ll need to setup a domain name for the cert.

1 Like

Here is my addition to @jacobheun’s answer. Due to browser’s security policies you couldn’t establish unencrypted connection from secure context (e.g. page loaded via HTTPS). You can read about it at MDN Secure Context. So you should make your AWS server to use SSL and then replace ws:// with wss://.

1 Like

I’m using this configuration with NGINX. I have my host name in an environment variable, but you can just hard code it in there if it makes your life easier. Replace ${DNSHOSTNAME} with your domain name and change 5892 to whatever port you’re running webrtc-star on. You will have to have a legitimate SSL certificate (using e.g. letsencrypt) that matches your domain name. I don’t think there’s any way you will get your browser to connect over wss with a self-signed cert. If you do figure out a way to do that, please let me know…

upstream websocket {
server ${DNSHOSTNAME}:5892;
}

server {
listen 8443 ssl;
server_name ${DNSHOSTNAME};
ssl_certificate /etc/nginx/certs/certbundled.crt;
ssl_certificate_key /etc/nginx/certs/private.key;

location /p2p {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade ‘Websocket’;
proxy_set_header Connection ‘Upgrade’;
proxy_set_header Host ${DNSHOSTNAME};
}
}

1 Like

Thank you so much everyone for your kind help so far.

So, looks like I’m close indeed.
The major problem seem to be with the self-signed certificates I’ve created. I had a look around and it seems like that to create valid certificates with letsencrypt for example, we have to use the DNS. I’ve tried with the already assigned AWS DNS but apparently it’s not possible because it was considered insecure to be used in some hacks.

So, the only solution is to get a DNS, linking to my AWS instance, right? Or do you have other suggestions?

Hi,

I eventually made it work.
I think I could write a tutorial about it.

But I’m currently having a problem.

This is the result from my server.

But on the webpage (both HTTP and https) I get WebSocket connection to 'wss://(my-url)/socket.io/?EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 400.

I’ve used @danrempe NGINX config with certbot certificates.
Any idea?

EDIT

Sorry, there’s no problem. It was the “apostrophe” ( ’ ) at the example :man_facepalming:
Thank you so much for everything.

@danrempe I’m having the same problem.

I added to the nginx configuration the following part:

upstream websocket {
    ip_hash;
    server localhost:13579;
}

server {
    listen 81;
    server_name ggc.world;

    ssl_certificate /etc/letsencrypt/live/ggc.world-0002/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ggc.world-0002/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/ggc.world/chain.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location /p2p {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade "Websocket";
        proxy_set_header Connection "Upgrade";
        proxy_set_header X-Forwared-For $remote_addr;
        proxy_set_header Host $host;

        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

I spawned a signaling server at localhost:13579 :

(base) marco@pc01:~$ star-signal --port=13579 --host=127.0.0.1
Listening on: http://127.0.0.1:13579

But I get the same error message:
"Mixed Content: The page at ‘https://ggc.world/signup’ was loaded over HTTPS, but attempted to
connect to the insecure WebSocket endpoint ‘ws://0.0.0.0:9090/socket.io/?EIO=3&
transport=websocket’. This request has been blocked; this endpoint must be available over WSS.
"

So… or what I added in nginx configuration is not correct, or I’m missing something else.
Here Can't connect to webrtc-star with ssl I read that I have to replace ws:// with wss:// . But where exactly should I do that?
And what am I doing wrong?

I think you have a couple of issues there.

It kind of looks like you’re running NGINX on a server (ggc.world) but setting up your reverse proxy to try to point to your localhost on 13579. But then your local signaling server is using host=127.0.0.1, which means it will only listen to connections coming from the same machine. Your upstream configuration in NGINX having “localhost” in it also is going to refer to its own localhost. So it’s going to attempt to connect to its own server at port 13579.

If you’re going to test locally, you might be better off actually running NGINX locally as well, and then just updating your hosts file to point your domain name at 127.0.0.1. That setup isn’t very complicated. Another note: for local testing, you don’t really need WSS anyway. Your browser will connect insecurely to a local address, as long as your page isn’t loaded over SSL.

If you want to connect over wss, you need to change it in your libp2p client code. Somewhere around where you’re starting up libp2p, you’re using a peer ID that contains something like:

/dns4/ggc.world/tcp/81/ws/p2p/

Just change the “ws” to “wss”.

Thank you for helping.

I’m developing locally using a local PC as server, so I’m running NGINX locally.
In future I will deploy everything in the cloud. But now I’m running the server locally.
My aim is to develop locally but being able to test everything as it were “not local”.

Trying to grasp your explanation, I “convert” everything to a “local” setup

nginx configuration:

upstream websocket {
    server 127.0.0.1:13579;
}

server {
    listen 8443 ssl;
    #server_name ggc.world; 
    server_name localhost;
    ssl_certificate /etc/letsencrypt/live/ggc.world-0002/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ggc.world-0002/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/ggc.world/chain.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location /p2p {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade "Websocket";
        proxy_set_header Connection "Upgrade";
    }
}

vue file:

<template>
  <div class="hello">
  </div>
</template>

<script>
import Libp2p from 'libp2p'
import Websockets from 'libp2p-websockets'
import WebRTCStar from 'libp2p-webrtc-star'
import { NOISE } from 'libp2p-noise'
import Secio from 'libp2p-secio'
import Mplex from 'libp2p-mplex'
import Boostrap from 'libp2p-bootstrap'

export default {
  name: 'Libp2p',
  data () {
    return {
      // our libp2p node
      libp2p: null,
      status: "",
      output: ""
    }
  },
  created() {
    (async () =>{
      // Create our libp2p node
     this.libp2p = await Libp2p.create({
        addresses: {
          // Add the signaling server address,
          // along with our PeerId to our multiaddrs list
          // libp2p will automatically attempt to dial to the signaling server so that it can
          // receive inbound connections from other peers
          listen: ['/dns4/ggc.world/tcp/81/wss/p2p/']
        },
        modules: {
          transport: [Websockets, WebRTCStar],
          connEncryption: [NOISE, Secio],
          streamMuxer: [Mplex],
          peerDiscovery: [Boostrap]
        },
        config: {
          peerDiscovery: {
            bootstrap: {
              enabled: true,
              list: [
                '/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
                '/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
                '/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
                '/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
                '/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
                '/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64'
              ]
            }
          }
        }
      })

      // Listen for new peers
      this.libp2p.on('peer:discovery', (peerId) => {
        console.log(`Found peer ${peerId.toB58String()}`)
      })

      // Listen for new connections to peers
      this.libp2p.connectionManager.on('peer:connect', (connection) => {
        console.log(`Connected to ${connection.remotePeer.toB58String()}`)
      })

      // Listen for peers disconnecting
      this.libp2p.connectionManager.on('peer:disconnect', (connection) => {
        console.log(`Disconnected from ${connection.remotePeer.toB58String()}`)
      })

      await this.libp2p.start();
    })() 
  }, 
} // end of export default
</script>

I get “error parsing. invalid address”. The same with this address:

listen: ['/dns4/127.0.0.1/tcp/8443/wss/p2p/']

It’s been a while since I’ve used webrtc-star, but your address for it is incomplete. According to the documentation, webrtc-star addresses look like:

/ip4/188.166.203.82/tcp/20000/wss/p2p-webrtc-star/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooo2a

That last bit is your client’s peer ID. You’re indicating that you’re listening at that webrtc-star server. Note that: 1. If you’re going to connect to 127.0.0.1, you’re going to have to put /ip4 on the front instead of /dns4. But 2. Your wss may fail to connect because NGINX is going to present a cert for gcc.world even though you’re connected to an IP address. When I set this up locally, I just edited my hosts file to point my domain name at my local machine, so that the SSL cert matched up correctly. 3. You changed the listen port to 8443 on your NGINX config, but your address still has 81 in it.

Try maybe the hosts file change, and then use an address like:
/dns4/gcc.world/tcp/81/wss/p2p-webrtc-star/p2p/your peer id

See if that works any better.

After modifying the ipfs port for a conflict with the local nginx port, I ran the ipfs daemon locally, on the PC.
I modified the webrtc-star address as :

listen: [’/ipv4/127.0.0.1/tcp/8443/wss/p2p-webrtc-star/p2p/Qm…’]
where Qm… is the id of the local ipfs peer.

The error I get is: “Uncaught (in promise) Error: no protocol with name: ipv4”

I do not understand the point 2 of your explanation:
“2. Your wss may fail to connect because NGINX is going to present a cert for gcc.world even though you’re connected to an IP address. When I set this up locally, I just edited my hosts file to point my domain name at my local machine, so that the SSL cert matched up correctly”
Does it mean that in the nginx configuration:

upstream websocket {
    server 127.0.0.1:13579;
}

server {
    listen 8443 ssl;
    server_name localhost;

    ssl_certificate /etc/letsencrypt/live/ggc.world-0002/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ggc.world-0002/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/ggc.world/chain.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

these SSL certs are not necessary?

Modifying the webrtc-star address as :

listen: ['/dns4/127.0.0.1/tcp/8443/wss/p2p-webrtc-star/p2p/Qm...']

I get this error:
“WebSocket connection to ‘wss://127.0.0.1:8443/socket.io/?EIO=3&transport=websocket’ failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID.
Uncaught (in promise) Error: Transport (WebRTCStar) could not listen on any available address”

I tried to comment the SSL certs in the NGINX configuration, but got “ERR_CONNECTION_CLOSED” :
“WebSocket connection to ‘wss://127.0.0.1:8443/socket.io/?EIO=3&transport=websocket’ failed: Error in connection establishment:net::ERR_CONNECTION_CLOSED
Uncaught (in promise) Error: Transport (WebRTCStar) could not listen on any available address”

It’s /ip4 instead of /ipv4.

You’re going to potentially run into certificate mismatch issues. There are limitations to using SSL on the web, especially with regard to Websockets:

  1. Your browser wants the address you connect to to match the presented certificate. When connecting over https, your browser might let you add an exception if there’s a mismatch. (Depends on the browser). They will also allow you to accept self-signed certs. Neither of these things is likely to work correctly over Websockets. It’s possible you’ll get it to work anyway because Chrome at least treats localhost differently (e.g. less secure) than other addresses, so you may slide by, but you’ll just run into the same problem later when you try to deploy to a real server.

  2. Letsencrypt won’t give you a cert for an IP address (yet) so you need to connect via SSL to a domain name.

  3. For local testing, the easiest thing to do is to go in your hosts file and point a domain name at 127.0.0.1, then install a certificate that matches the domain. Or, you could also choose not to test SSL locally and only do everything over HTTP and WS, in which case all of the problems we’re discussing go away for now until you decide to move your stuff out to a server.

As far as I understand,
if my browser wants the address, that I connect to, to match the presented certificate, and, if
letsencrypt won’t give me yet a cert for an IP address
it means that it’s no viable to configure NGINX in my hosts file as :

server {
    listen 8443 ssl;
    #server_name ggc.world;
    server_name localhost;

    ssl_certificate /etc/letsencrypt/live/ggc.world-0002/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ggc.world-0002/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/ggc.world/chain.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot 

because the certificates are not related to the local address.

About the two possible ways to overcome this hurdle, the easiest way would not to test SSL locally and do everything over HTTP and WS.
But I would like to test as much as possible locally, so I would prefer to modify my NGINX hosts file to point a domain name at 127.0.0.1, and then install a certificate matching the domain.
What specifically should I do to accomplish this step?
In my NGINX hosts file: /etc/nginx/conf.d/default.conf, I have at the moment this:

upstream websocket {
    #server ggc.world:13579;
    server 127.0.0.1:13579;
}

server {
    listen 8443 ssl;
    #server_name ggc.world;
    server_name localhost;

    ssl_certificate /etc/letsencrypt/live/ggc.world-0002/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ggc.world-0002/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/ggc.world/chain.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

May be my question sounds weird, but I ask you anyway, because I would like to understand:
why not pointing to my domain name, ggc.world, which has already installed the matching certs?
Or should I use here another domain name?

server {
    listen 8443 ssl;
    server

Here’s what you want to do, if you want to set this up locally. I tried this, not using webrtc-star, but with another libp2p transport, and it worked fine.

  1. Get a domain name. You can use your existing one if you want, or a different one. Doesn’t matter. Make sure you have a matching cert to go with it before doing any of the other steps in here.
  2. When I say hosts file, I mean the operating system hosts file - windows: C:\windows\system32\drivers\etc\hosts or linux: /etc/hosts.
  3. In the hosts file put 127.0.0.1: your domain name
  4. Use the nginx config from my original post in this thread, replacing $DNSHOSTNAME with your domain name. Make sure your cert files matching your domain name are stored in the right place.
  5. Make your libp2p client point to an address using /dns4/your domain name instead of using a /ip4 address.

That should get you a local test environment.

It’s ok to use your existing domain for this testing if you want because, by modifying your hosts file, you are telling your local machine to connect to itself when you access that domain. And if you have a matching cert for that domain, your browser will be happy with that.

1 Like

I added to /etc/hosts 127.0.0.1 my domain name :

(base) marco@pc01:~$ tail /etc/hosts
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.0.1	peer1.acme.com
127.0.0.1	peer1.budget.com
127.0.0.1	orderer.acme.com
127.0.0.1	postgresql
127.0.0.1	explorer
127.0.0.1	vagrant
127.0.0.1	ggc.world

I modified the websocket’s part of /etc/nginx/conf.d/default.conf as:

upstream websocket {
    server ggc.world:13579;
}

server {
    listen 8443 ssl;
    server_name ggc.world;

    ssl_certificate /etc/letsencrypt/live/ggc.world-0002/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ggc.world-0002/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/ggc.world/chain.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location /p2p {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade "Websocket";
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host ggc.world;
    }
}

This is the vue page :

<template>
  <div class="hello">
  </div>
</template>

<script>

// https://github.com/libp2p/js-libp2p/tree/master/examples/libp2p-in-the-browser

import Libp2p from 'libp2p'
import Websockets from 'libp2p-websockets' 
import WebRTCStar from 'libp2p-webrtc-star'
import { NOISE } from 'libp2p-noise'
import Secio from 'libp2p-secio'
import Mplex from 'libp2p-mplex'
import Boostrap from 'libp2p-bootstrap'

export default {
  name: 'Libp2p',
  data () {
    return {
      // our libp2p node
      libp2p: null,
      status: "",
      output: ""
    }
  },
  created() {
    (async () =>{
      // Create our libp2p node
     this.libp2p = await Libp2p.create({
        addresses: {
          listen: ['/dns4/ggc.world/tcp/8443/wss/p2p-webrtc-star/p2p
   /QmT.....9']
        },
        modules: {
          transport: [Websockets, WebRTCStar],
          connEncryption: [NOISE, Secio],
          streamMuxer: [Mplex],
          peerDiscovery: [Boostrap]
        },
        config: {
          peerDiscovery: {
            bootstrap: {
              enabled: true,
              list: [
                '/dns4/ams-1.bootstrap.libp2p.io/tcp/443/wss/p2p
/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
                '/dns4/lon-1.bootstrap.libp2p.io/tcp/443/wss/p2p  
/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
                '/dns4/sfo-3.bootstrap.libp2p.io/tcp/443/wss/p2p 
/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
                '/dns4/sgp-1.bootstrap.libp2p.io/tcp/443/wss/p2p  
/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
                '/dns4/nyc-1.bootstrap.libp2p.io/tcp/443/wss/p2p  
/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
                '/dns4/nyc-2.bootstrap.libp2p.io/tcp/443/wss/p2p 
/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64'
              ]
            }
          }
        }

      // Listen for new peers
      this.libp2p.on('peer:discovery', (peerId) => {
        console.log(`Found peer ${peerId.toB58String()}`)
      })

      // Listen for new connections to peers
      this.libp2p.connectionManager.on('peer:connect', (connection) => {
        console.log(`Connected to ${connection.remotePeer.toB58String()}`)
      })

      // Listen for peers disconnecting
      this.libp2p.connectionManager.on('peer:disconnect', (connection) => {
        console.log(`Disconnected from ${connection.remotePeer.toB58String()}`)
      })

      await this.libp2p.start();

    })() 

  }, 

} 
</script>

I started the ipfs daemon:

(base) marco@pc01:~$ ipfs daemon
Initializing daemon...
go-ipfs version: 0.5.0
Repo version: 9
System version: amd64/linux
Golang version: go1.13.10
Swarm listening on /ip4/127.0.0.1/tcp/4001
Swarm listening on /ip4/172.17.0.1/tcp/4001
Swarm listening on /ip4/192.168.1.7/tcp/4001
Swarm listening on /ip6/::1/tcp/4001
Swarm listening on /p2p-circuit
Swarm announcing /ip4/127.0.0.1/tcp/4001
Swarm announcing /ip4/172.17.0.1/tcp/4001
Swarm announcing /ip4/192.168.1.7/tcp/4001
Swarm announcing /ip4/37.116.208.13/tcp/45067
Swarm announcing /ip6/::1/tcp/4001
API server listening on /ip4/127.0.0.1/tcp/5001
WebUI: http://127.0.0.1:5001/webui
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/9090
Daemon is ready

I started the signaling server:

(base) marco@pc01:~$ star-signal --port=13579 --host=127.0.0.1
Listening on: http://127.0.0.1:13579

I started the webapp:

 DONE  Compiled successfully in 6358ms                                                                                     10:52:38 AM

  App running at:
  - Local:   http://localhost:8080 
  - Network: http://ggc.world/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

I get this new error:
“WebSocket connection to ‘wss://ggc.world:8443/socket.io/?EIO=3&transport=websocket’ failed: Error during WebSocket handshake: Unexpected response code: 404
Uncaught (in promise) Error: Transport (WebRTCStar) could not listen on any available address”

@danrempe Daniel
any ideas about how to solve this new error?

Looking forward to your kind help