miroslavpejic85 / mirotalk

🚀 WebRTC - P2P - Simple, Secure, Fast Real-Time Video Conferences Up to 8k and 60fps, compatible with all browsers and platforms.
https://p2p.mirotalk.com
GNU Affero General Public License v3.0
3.03k stars 558 forks source link

Can't get secure SSL/TLS connection expose https #65

Closed gettyhub closed 3 years ago

gettyhub commented 3 years ago

Made changes to server.js and client.js to expose https using letsencrypt certificate self hosting, instead of self signed certificate.

Can't connect with browser. Says cannot negotiate SSL/TLS.

And trying to connect to it from localhost mode from another computer on the same network doesn't ever allow access to microphone/video and doesn't continue into the room.

miroslavpejic85 commented 3 years ago

Hi @gettyhub, welcome!

Did you put the let's encrypt certificates in the ssl folder? And how does let's encrypt generate them, what are the names of the certificates?

Attach me here server.js with all your changes. Thanks

gettyhub commented 3 years ago

Error message is: ERR_SSL_VERSION_OR_CIPHER_MISMATCH from the browser.

Letsencrypt has cert.pem privkey.pem chain.pem fullchain.pem

When I address the files for the certificates it only wants to stay within the directory of where the mirotalk files are.

Is there a way I can run it under my http server to do https? I tried copying the directory under http server, but there is no index file, so it just lists directory contents.

gettyhub commented 3 years ago

server.js:

//const http = require('http');
const https = require('https')
const fs = require ('fs');
const options = {
    key: fs.readFileSync(path.join(__dirname, 'privkey.pem'), 'utf-8'),
    cert: fs.readFileSync(path.join(__dirname,'cert.pem'), 'utf-8'),    
};
// const server = http.createServer(app);
const server = https.createServer(app);
const { Server } = require('socket.io');
const io = new Server().listen(server);

const ngrok = require('ngrok');
const yamlJS = require('yamljs');
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = yamlJS.load(path.join(__dirname + '/api/swagger.yaml'));
const { v4: uuidV4 } = require('uuid');

const port = process.env.PORT || 3000; // must be the same to client.js signalingServerPort

// const localHost = 'http://' + 'localhost' + ':' + port; // http
const localHost = 'https://' + 'sitename' + ':' + port;
miroslavpejic85 commented 3 years ago

I not try it with let's encrypt, but try this:

Put all file generated in mirotalk/ssl folder, then

server.js:

const localHost = 'https://' + 'localhost' + ':' + port;

client.js

function getSignalingServer() {
    return 'https://' + 'localhost' + ':' + signalingServerPort;
}

Eventually try to change port and signalingServerPort to 443

And one important thing, You need a host that support node.js.

Have the niode.js installed on v 12.X or 14.X.

Then install all dependencies by npm install and then start it with npm start.

Where are you looking to run it?

gettyhub commented 3 years ago

The web server is already using 443.

My nodejs supplied by distribution is 16.x.

I used sitename as it would be part of https most likely instead of localhost. It would have to resolve against the sitename and not localhost. But it seemed to work over the local network, but still no audio/video permission leading into joining the room from anywhere except localhost. I guess I can talk to myself.

Using openssl s_client -connect sitename:3000

returns that there is no SSL connection or certificates

CONNECTED(00000003)
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 321 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
gettyhub commented 3 years ago

Already have a voice only chat running, but people are enamored by the video chat.

No point in seeing everybody in their bedrooms on a video chat and the bandwidth is wasted doing that.

miroslavpejic85 commented 3 years ago

I read that you use node.js 16.X (uninstall it), as I told you, try with version 12.X or 14.X. Let me know if anything changes ;) If all configured correctly and if you use one of mentioned node.js version, should work.

gettyhub commented 3 years ago

Looks like I may have a build script for node.js 14.x in the off hand section for the distribution. Looks like my other chat uses node.js also.

May take awhile to build it from source.

gettyhub commented 3 years ago

Ok, running 14.x, but no change on the ssl. I don't need to rebuild anything do I?

miroslavpejic85 commented 3 years ago

Ok I think let's encrypt need also the chain.pem, so add the last line to the const options:

const options = {
    key: fs.readFileSync(path.join(__dirname, 'privkey.pem'), 'utf-8'),
    cert: fs.readFileSync(path.join(__dirname,'cert.pem'), 'utf-8'),
    ca: fs.readFileSync(path.join(__dirname,'chain.pem'), 'utf-8'), 
};
gettyhub commented 3 years ago

From letsencrypt:

`This directory contains your keys and certificates.

privkey.pem : the private key for your certificate. fullchain.pem: the certificate file used in most server software. chain.pem : used for OCSP stapling in Nginx >=1.3.7. cert.pem : will break many server configurations, and should not be used without reading further documentation (see link below).

WARNING: DO NOT MOVE THESE FILES! Certbot expects these files to remain in this location in order to function properly!

We recommend not moving these files. For more information, see the Certbot User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates. `

gettyhub commented 3 years ago

Nothing.

Switched between 16 and 14 and no change either.

miroslavpejic85 commented 3 years ago

Not even adding this line of code?

    ca: fs.readFileSync(path.join(__dirname,'chain.pem'), 'utf-8'), 

If not, as soon as I have more time, I'll try it with let's encrypt for you. Between, try to see if you discover something new ;)

PS: You can use also ngrok to test MiroTalk outside of Your Network, starting it from your Local PC. More details here

Thanks

gettyhub commented 3 years ago

It didn't work even with that line.

Your demo servers use a different certificate.

miroslavpejic85 commented 3 years ago

Hey @gettyhub, You don't need to expose it in https. Just use Ngnix and Let's encrypt.

Put Ngnix in front, then something like this:

sudo vim /etc/nginx/sites-enabled/default

# HTTP — redirect all traffic to HTTPS
server {
        listen 80;
        listen [::]:80 default_server ipv6only=on;
        return 301 https://$host$request_uri;
}

# HTTPS — proxy all requests to the Node app
server {
    # Enable HTTP/2
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name YOUR-SERVER-DOMAIN;
    # Use the Let’s Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/YOUR-SERVER-DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/YOUR-SERVER-DOMAIN/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    ....
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:3000/;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
...
}

sudo nginx -t
service nginx restart

In this way in theory all traffic is Secured by Nginx-Let's encrypt and Ngnix proxed traffic to proxy_pass http://localhost:3000/; that is the entry point of the app ;)

Do some test and let me know if ok, I'm not tested it.

In my demo MiroTalk is not exposed in HTTPS all this is done by Railway - Heroku... that in theory have these settings that I wrote you ;)

Thanks

gettyhub commented 3 years ago

That’s similar to what I was trying to do with the other voice chat only except it was using web sockets and never could get it to route inside 443 for the extra ports involved. Only way I could see to get it through 443, was to use the web socket as a web server. And port 3000 is still technically exposed.

And there is a reverse proxy method to do the same thing in Apache. I could run nginx off to the side to test to see if it will go.

miroslavpejic85 commented 3 years ago

So can I consider this issue resolved or not? I gave you some inputs and possible solutions

ghost commented 3 years ago

Hi, miroslavpejic85. I'm new to all this github comments and contributing to projects.

I have a question though. Have you perhaps forked the project and had a look if you can fix the problem on you own machine. Perhaps you could see or find a small mistake that gettyhub missed. I'm not 100% sure. I'm still new to all this and not sure if forking is a common practice.

miroslavpejic85 commented 3 years ago

Hi @Th3DevG1ant, welcome! I think with these settings: https://github.com/miroslavpejic85/mirotalk/issues/65#issuecomment-907588280 You will be able to expose correctly MiroTalk on SSL, thanks to Nginx - Let's Encrypt without touching, the current code at all. Do some tests and then in case update here if done or what you find difficulty in ;) Regards

gettyhub commented 3 years ago

Maybe this would help:

https://advancedweb.hu/how-to-use-lets-encrypt-with-node-js-and-express/

Await might require some modification.

https://www.stefanjudis.com/today-i-learned/top-level-await-is-available-in-node-js-modules/

I'm getting into a redirect loop with nginx.

gettyhub commented 3 years ago

Ok I've got the https through the node js now. You missed a line

const server = https.createServer (options, app);

You had the certificates defined, but you didn't include them in the createServer call.

But now still can't get the microphone/video to enable. So this is directly to port 3000 without apache or nginx.

miroslavpejic85 commented 3 years ago

What you mean for: can't get the microphone/video to enable? Are you see errors on the web console?

gettyhub commented 3 years ago

What you mean for: can't get the microphone/video to enable? Are you see errors on the web console?

When I go to your demo site, it asks me for allow video/microphone, but not on my private site. I can see myself in the camera on your site, but not mine. It doesn't go past the page to tell me to allow microphone/video, but it doesn't ask for permission, and it doesn't go on to ask me for a handle if I enable it in site settings.

Some browsers are showing not webrtc capable browser also.

Console errors:

polling-xhr.js:198 GET https://sitesomewhere.org/socket.io/?EIO=4&transport=polling&t=somevalue net::ERR_FAILED

Someroom:1 Access to XMLHttpRequest at 'https://sitesomewhere.org/socket.io/?EIO=4&transport=polling&t=somevalue' from origin 'https://sitesomewhere.org:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
miroslavpejic85 commented 3 years ago

Just for curiosity, if you set a port-forward, something like this, do you have the same error?

Name Protocol Port Wan Port Lan IP Destination
MiroTalk TCP 3000 3000 Your Local IP

For Your Local IP, I mean the local ipv4 IP where MiroTalk is run. What I see the problem is in the connection between client and internal signaling server. When the connection is successful, then you will be asked for the permissions for the audio-video

gettyhub commented 3 years ago

Just for curiosity, if you set a port-forward, something like this, do you have the same error?

Name Protocol Port Wan Port Lan IP Destination MiroTalk TCP 3000 3000 Your Local IP For Your Local IP, I mean the local ipv4 IP where MiroTalk is run. What I see the problem is in the connection between client and internal signaling server. When the connection is successful, then you will be asked for the permissions for the audio-video

I'm not running behind a firewall. So I'm not even using stun or turn. I'm running straight on a VPS. I get to about the same place as if I run it on my local lan. Still doesn't mic/vid up.

miroslavpejic85 commented 3 years ago

I tested and works with self-signed certificate.... but if you use Let's Encrypt it should be the same, it just changes the path where the certificates are.

client.js

const signalingServerPort = 3000; // must be the same to server.js PORT

function getSignalingServer() {
    return 'https://' + 'sitesomewhere.org' + ':' + signalingServerPort;
}

server.js

// const http = require('http');
// const server = http.createServer(app);
// const { Server } = require('socket.io');
// const io = new Server().listen(server);

const https = require('https');
const fs = require('fs');
const options = {
    key: fs.readFileSync(path.join(__dirname, '/ssl/key.pem'), 'utf-8'),
    cert: fs.readFileSync(path.join(__dirname, '/ssl/cert.pem'), 'utf-8'),
};
const server = https.createServer(options, app);
const { Server } = require('socket.io');
const io = new Server().listen(server);

const port = process.env.PORT || 3000; // must be the same to client.js signalingServerPort

const localHost = 'https://' + 'localhost' + ':' + port;
//const localHost = 'http://' + 'localhost' + ':' + port; // http

restart MiroTalk Then: https://sitesomewhere.org:3000/join/test

For Let's Encrypt, the options should be something like:

const options = {
    key: fs.readFileSync(/etc/letsencrypt/live/sitesomewhere.org/privkey.pem), 'utf-8'),
    cert: fs.readFileSync(/etc/letsencrypt/live/sitesomewhere.org/cert.pem), 'utf-8'),
    ca: fs.readFileSync(/etc/letsencrypt/live/sitesomewhere.org/chain.pem), 'utf-8'),   
};

Keep this config, and you will be able to use MiroTalk on secure SSL/TLS...

gettyhub commented 3 years ago

You have localhost in server.js and the site some where in client.js. I was wondering about that. Because typically the certificate has to match the site in some challenge.

I have the site some where in server.js localHost, but apparently client.js is where it matters.

Ok, so I finally have it it looks like I have it.

moh21amed commented 2 years ago

does this still work now

miroslavpejic85 commented 2 years ago

Follow this doc also on how to self host it in https, without touching the source code.