Ratchet: Is there a real working example of using secure TLS ratchet websocket server?

Created on 7 Feb 2018  路  24Comments  路  Source: ratchetphp/Ratchet

I've been looking and looking without much success. I see hints that there is such a thing but no real documentation or examples. Any help would be greatly appreciated.

Thanks,

Daryl

Most helpful comment

Hello there, I guess i am late posting this one, but I have here a working example with routing also. the code is working well as per the latest ratchet version.

<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
require __DIR__ . '/vendor/autoload.php';

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use Psr\Http\Message\RequestInterface;
use Ratchet\Http\Router;
use Ratchet\Http\HttpServerInterface;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\HttpFoundation\Request;

$routes = new RouteCollection;
$loop = React\EventLoop\Factory::create();

$decorated = new WsServer(new ChatRoom);
$decorated->enableKeepAlive($loop);
$routes->add('chat-room', new Route('/chat-room', array('_controller' => $decorated), array('Origin' => $GLOBALS['Address']), 
    array(), $GLOBALS['Address'], array(), array('GET')));

$decorated = new WsServer(new SingleChat);
$decorated->enableKeepAlive($loop);
$routes->add('single-chat', new Route('/single-chat', array('_controller' => $decorated), array('Origin' => $GLOBALS['Address']), 
    array(), $GLOBALS['Address'], array(), array('GET')));

$decorated = new WsServer(new Ratchet\Server\EchoServer);
$decorated->enableKeepAlive($loop);
$routes->add('echo', new Route('/echo', array('_controller' => $decorated), array('Origin' => $GLOBALS['Address']), 
    array(), $GLOBALS['Address'], array(), array('GET')));


$app = new HttpServer(new Router(new UrlMatcher($routes, new RequestContext)));

$secure_websockets = new \React\Socket\Server('0.0.0.0:8091', $loop);
$secure_websockets = new \React\Socket\SecureServer($secure_websockets, $loop, [
    'local_cert' => 'path/to/certificate.crt',
    'local_pk' => '/path/to/your-key.key',
    'verify_peer' => false
]);

$secure_websockets_server = new \Ratchet\Server\IoServer($app, $secure_websockets, $loop);
$secure_websockets_server->run();

Regards

All 24 comments

Your question should be more specific, but here is an example how to establish secure WebSocket server:

$loop = React\EventLoop\Factory::create();
$pusher = new MyApp\Pusher;

// Listen for the web server to make a ZeroMQ push after an ajax request
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555'); // Binding to 127.0.0.1 means the only client that can connect is itself
$pull->on('message', [$pusher, 'onUpdate']);

// Set up our WebSocket server for clients wanting real-time updates
$webSock = new React\Socket\Server('0.0.0.0:8443', $loop);
$webSock = new React\Socket\SecureServer($webSock, $loop, [
    'local_cert'        => 'C:/xampp/apache/conf/ssl.crt/server.crt', // path to your cert
    'local_pk'          => 'C:/xampp/apache/conf/ssl.key/server.key', // path to your server private key
    'allow_self_signed' => TRUE, // Allow self signed certs (should be false in production)
    'verify_peer' => FALSE
]);
//$webSock->listen(8443, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
        new Ratchet\WebSocket\WsServer(
            new Ratchet\Wamp\WampServer(
                $pusher
            )
        )
    ),
    $webSock
);

$loop->run();

You should run this code via PHP CLI, then you can connect to your websocket via wss://server_name_or_ip:8443

If you get any timeout error while you try to connect you should check your firewall settings.

Hope it helps

Hi SFlame, Thanks for your reply. Sorry for not being more specific; let me remedy that. I have built the simple Ratchet hello world/chat example which works fine. I need to move this chat example to use a secure connection.

I appreciate your sample code and am playing with it now, but I'm not understanding the pusher piece. Is this a library I can install using composer? Googling pusher and websockets seems to lead to pusher.com which appears to require registration. I feel like I'm missing something here and I'm still bootstrapping my understanding of both reactphp and ratchet.

Thanks,

Daryl

Well, it's only my app that using the pusher integration, and it's a requirement for running a websocket server over a secure connection (If you're still interested in the pusher integration, you should check it out http://socketo.me/docs/push - no additional installation needed).

In order to run your app over a secure connection, I think it should be as simple as that (not tested):

require dirname(__DIR__) . '/vendor/autoload.php';

use MyApp\Chat;

$loop = React\EventLoop\Factory::create();
$webSock = new React\Socket\Server('0.0.0.0:8443', $loop);
$webSock = new React\Socket\SecureServer($webSock, $loop, [
    'local_cert'        => 'C:/xampp/apache/conf/ssl.crt/server.crt', // path to your cert
    'local_pk'          => 'C:/xampp/apache/conf/ssl.key/server.key', // path to your server private key
    'allow_self_signed' => TRUE, // Allow self signed certs (should be false in production)
    'verify_peer' => FALSE
]);

$webServer = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
        new Ratchet\WebSocket\WsServer(
            new Chat()
        )
    ),
    $webSock
);

$webServer->run();

@darylwilliams update me if it worked for you.

Thanks @SFlame, I modified the paths to the cert and key files and have your sample code running and I can connect to the port using netcat, but the browser refuses the connection with the error "failed: Error in connection establishment: net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH", so I'm guessing I'm not pointing at the right cert. I'm using certs from letsencrtypt. I'm going to play with it and I'll let you know what progress I make.

Thanks again for your help, it's much appreciated.

D.

I could start a TLS connection to a ratchet websocket server. I didn't used Ratchet/React at all, but I used stunnel.

So I guess it's always possible with apache/nginx/stunnel to provide a TLS layer without impacting Ratchet application. That could be an alternative solution.

Hi,

I cannot make this work when I deploy to server. I have used exact same settings above, except the server's crt and key files and I've set 'allow_self_signed' to false. But it is not working. Any help please?

@cometofsky Can you please share your settings?

Below is the code I have used

 require dirname(__DIR__) . '/vendor/autoload.php';

$loop   = React\EventLoop\Factory::create();
$pusher = new UGR\SocketIOBundle\Pusher;


  $context = new React\ZMQ\Context($loop);
  $pull    = $context->getSocket(ZMQ::SOCKET_PULL);


$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message', [$pusher,
                                      'onUpdate']);

$webSock = new React\Socket\Server("0.0.0.0:8443", $loop);

  $webSock = new React\Socket\SecureServer($webSock, $loop, [
      'local_cert'        => 'crt-file-path',
       'local_pk'          => 'key-file-path',
      'allow_self_signed' => FALSE,
        'verify_peer'       => FALSE,
      ]);


$webServer = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
        new Ratchet\WebSocket\WsServer(
            new Ratchet\Wamp\WampServer(
                $pusher
            )
        )
    ),
    $webSock
);


$loop->run();

I could solve my issues. In safari now I am getting this error - WebSocket network error: OSStatus Error -9807: Invalid certificate chain. Although I have e valid SSL certificate for my site, I guess they are telling me otherwise.

@cometofsky How did you get this cert? from where?

It was purchased from COMODO. It was not generated by me.

Check my current settings, working well for me:

$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555'); 
$pull->on('message', [$pusher, 'onUpdate']);

$webSock = new React\Socket\Server('{MY SERVER IP}:8443', $loop);
$webSock = new React\Socket\SecureServer($webSock, $loop, [
    'local_cert'        => '{MY CERT PATH}',
    'local_pk'          => '{MY KEY PATH}',
    'verify_peer' => FALSE
]);
$wsServer = new Ratchet\WebSocket\WsServer(
    new Ratchet\Wamp\WampServer(
        $pusher
    )
);
$webServer = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
        new \Ratchet\Session\SessionProvider($wsServer
            ,new \Illuminate\Session\CookieSessionHandler(new \Illuminate\Cookie\CookieJar(),20))
    ),
    $webSock
);
$wsServer->enableKeepAlive($loop,30);

$loop->run();

Thanks for sharing! I have changed from 0.0.0.0 to my server ip while creating React Socket Server. And below is my autobahn settings for client connection.

   var conn = new ab.Session("wss://{my-server-ip}:8443",
        function () {

            console.info('webSocket - subscribed to topic...');

            conn.subscribe('oneTopic', function (topic, data) {

                console.warn('webSocket - connected!');
                console.warn('topic: ', topic);
            });
        },
        function () {
            console.warn('webSocket - connection closed!');
        },
        {'skipSubprotocolCheck': true}
    );

And here is how I am sending data to subscribers-

   $context = new ZMQContext();
    $socket  = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher');
    $socket->connect('tcp://127.0.0.1:5555');

    $socket->send(json_encode([
        'topic'    => 'oneTopic',
        'when'     => time(),
        'someKey' => $someValue,
    ]));

I'm using my domain names for the connection string:

new ab.Session('wss://'+window.location.hostname+':8443/ws',
            function () {
                //subscribe here
            },
            function () {
                //connection closed
            },
            {'skipSubprotocolCheck': true,'multiplex':false,'force new connection':true}
        );

Are you still getting the same error?

@SFlame - THANKS A LOT!!! I have just changed to domain name and now its working perfect!!! A pain relieved! (Y)

I've the same problem...
i've tried with

```
require '/opt/bitnami/apache2/htdocs/gestionaleCore/vendor/autoload.php';
$loop = ReactLoop::create();

$pusher = new Pusher;
// In ascolto del web sever per fare un push ZeroMQ dopo una richiesta AJAX.
$context = new ReactContex($loop);
$pull = $context->getSocket(\ZMQ::SOCKET_PULL);
$pull->bind('tcp://0.0.0.0:5552');
$pull->on('message', [$pusher, 'onSignRequest'] );

$webSock = new SecureServer(
    new ReactServer('0.0.0.0:32000', $loop),
    $loop,
  array(
    'local_cert'        => '/opt/bitnami/apache2/conf/server.crt', // path to your cert
    'local_pk'          => '/opt/bitnami/apache2/conf/server.key', // path to your server private key
    'allow_self_signed' => FALSE, // Allow self signed certs (should be false in production)
    'verify_peer' => FALSE
  )
);

$wsServer = new WsServer(
      new WampServer(
        $pusher
      )
    );

$webServer = new IoServer(
  new HttpServer(
    new SessionProvider(
      $wsServer,
      new CookieSessionHandler(
        new CookieJar(), 20
        )
      )
  ),
  $webSock
);
$wsServer->enableKeepAlive($loop,30);

$this->info('Run handle');
$loop->run();
and with 

require '/opt/bitnami/apache2/htdocs/gestionaleCore/vendor/autoload.php';

$loop = ReactLoop::create();

$pusher = new Pusher;
// In ascolto del web sever per fare un push ZeroMQ dopo una richiesta AJAX.
$context = new ReactContex($loop);
$pull = $context->getSocket(\ZMQ::SOCKET_PULL);
$pull->bind('tcp://0.0.0.0:5552');
$pull->on('message', [$pusher, 'onSignRequest'] );

$webSock = new SecureServer(
    new ReactServer('0.0.0.0:32000', $loop),
    $loop,
  array(
    'local_cert'        => '/opt/bitnami/apache2/conf/server.crt', // path to your cert
    'local_pk'          => '/opt/bitnami/apache2/conf/server.key', // path to your server private key
    'allow_self_signed' => FALSE, // Allow self signed certs (should be false in production)
    'verify_peer' => FALSE
  )
);

$webServer = new IoServer(
  new HttpServer(
    new WsServer(
      new WampServer(
        $pusher
      )
    )
  ),
  $webSock
);

$this->info('Run handle');
$loop->run();

```
without any success...
please help me,
I use the certificate i found on amazon lightsail vps

Best regards

@andreadompe - please follow what I used and what SFlame suggested. I hope you will get to the solution.

@cometofsky you mean this solution
'wss://'+window.location.hostname+':8443/ws' ?
i actually don't have a domain i only have a static ip, i use this vps for test

@andreadompe - instead of binding to 0.0.0.0, consider using 127.0.0.1. Also make sure your ports are not occupied by other services. It may be not but worth checking - your ssl certificate folder have proper permissions.

@cometofsky if i change the address to 127.0.0.1
i get this error
autobahn.min.js:62 WebSocket connection to 'wss://....:30000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

The port is not used.

These are the things you should consider mainly -

  1. $pull->bind('tcp://127.0.0.1:5555');
  2. new React\Socket\SecureServer($new React\Socket\Server("{ip}:{port}", $loop);, $loop, [
    'local_cert' => {cert-path},
    'local_pk' =>{key-path},
    'verify_peer' => FALSE,
    ])
  3. new ab.Session(protocol + "://" + host + ":" + port, successCallback(), errorCallback(), {})
  4. $socket->connect('tcp://127.0.0.1:5555');

@SFlame code works well. I just wanted to add another thing that may cause the problem.

I was also having issues with a ERR_SSL_VERSION_OR_CIPHER_MISMATCH error on my dev machine.

Turns out I wasn't using sudo, ie:

sudo php secure-websocket-server.php

I'm not sure of the security risk around running the websocket as root - but if you're doing this on a dev machine, this may sort the problem for you.

Hello there, I guess i am late posting this one, but I have here a working example with routing also. the code is working well as per the latest ratchet version.

<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
require __DIR__ . '/vendor/autoload.php';

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use Psr\Http\Message\RequestInterface;
use Ratchet\Http\Router;
use Ratchet\Http\HttpServerInterface;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\HttpFoundation\Request;

$routes = new RouteCollection;
$loop = React\EventLoop\Factory::create();

$decorated = new WsServer(new ChatRoom);
$decorated->enableKeepAlive($loop);
$routes->add('chat-room', new Route('/chat-room', array('_controller' => $decorated), array('Origin' => $GLOBALS['Address']), 
    array(), $GLOBALS['Address'], array(), array('GET')));

$decorated = new WsServer(new SingleChat);
$decorated->enableKeepAlive($loop);
$routes->add('single-chat', new Route('/single-chat', array('_controller' => $decorated), array('Origin' => $GLOBALS['Address']), 
    array(), $GLOBALS['Address'], array(), array('GET')));

$decorated = new WsServer(new Ratchet\Server\EchoServer);
$decorated->enableKeepAlive($loop);
$routes->add('echo', new Route('/echo', array('_controller' => $decorated), array('Origin' => $GLOBALS['Address']), 
    array(), $GLOBALS['Address'], array(), array('GET')));


$app = new HttpServer(new Router(new UrlMatcher($routes, new RequestContext)));

$secure_websockets = new \React\Socket\Server('0.0.0.0:8091', $loop);
$secure_websockets = new \React\Socket\SecureServer($secure_websockets, $loop, [
    'local_cert' => 'path/to/certificate.crt',
    'local_pk' => '/path/to/your-key.key',
    'verify_peer' => false
]);

$secure_websockets_server = new \Ratchet\Server\IoServer($app, $secure_websockets, $loop);
$secure_websockets_server->run();

Regards

Dear All
I have an issue which I have posted at https://github.com/ratchetphp/Ratchet/issues/783#issue-566596974. I strongly feel that experts involved in the present discussion may give me some positive feedback on my issue. Any help is appreciated.
Thank you very much

Definitely a late post but I completed the following code with the help of @michaelphipps and its working for me. I did not replace 0.0.0.0 with the server IP.
` use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;

require dirname(DIR) . '/vendor/autoload.php';
require_once dirname(DIR) . '/src/chat.php';

$loop = React\EventLoop\Factory::create();
$webSock = new React\Socket\Server('0.0.0.0:8088', $loop);
$webSock = new React\Socket\SecureServer($webSock, $loop, [
'local_cert' => '/etc/letsencrypt/live/mydomain.com/fullchain.pem', // path to your cert
'local_pk' => '/etc/letsencrypt/live/mydomain.com/privkey.pem', // path to your server private key
'allow_self_signed' => FALSE, // Allow self signed certs (should be false in production)
'verify_peer' => FALSE
]);
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new Chat()
)
),
$webSock, $loop
);

$webServer->run();
?>`

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Opice picture Opice  路  4Comments

nazar-pc picture nazar-pc  路  8Comments

elovin picture elovin  路  3Comments

maliknaik16 picture maliknaik16  路  8Comments

Rinum picture Rinum  路  6Comments