简体   繁体   中英

Secure websockets with Cro

Briefly : I created a service on an inte.net server using Cro and websocket. Very simple using Cro examples. No problem when sending and receiving data from an HTML page when the page is served as localhost. When the page is served using https, the websocket cannot be established. How is the wss protocol be used with Cro?

Update : After installing cro and running cro stub:secure , the service.p6 has some more code not explicit in the documentation.

More detail : I have a docker file running on the inte.net server, Cro is set to listen on 35145, so the docker command is docker --rm -t myApp -p 35145:35145

The service file contains

use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Cro::HTTP::Router;
use Cro::HTTP::Router::WebSocket;

my $host = %*ENV<RAKU_WEB_REPL_HOST> // '0.0.0.0';
my $port = %*ENV<RAKU_WEB_REPL_PORT> // 35145;
my Cro::Service $http = Cro::HTTP::Server.new(
    http => <1.1>,
    :$host,
    :$port,
    application => routes(),
    after => [
        Cro::HTTP::Log::File.new(logs => $*OUT, errors => $*ERR)
    ]
    );
$http.start;
react {
    whenever signal(SIGINT) {
        say "Shutting down...";
        $http.stop;
        done;
    }
}
sub routes() {
    route {
        get -> 'raku' {
            web-socket :json, -> $incoming {
                supply whenever $incoming -> $message {
                    my $json = await $message.body;
                    if $json<code> {
                        my $stdout, $stderr;
                        # process code 
                        emit({ :$stdout, :$stderr })
                    }
                }
            }
        }
    }
}

In the HTML I have a textarea container with an id raku-code . The js script has the following (I set websocketHost and websocketPort elsewhere in the script) in a handler that fires after the DOM is ready:

const connect = function() {
    // Return a promise, which will wait for the socket to open
    return new Promise((resolve, reject) => {
        // This calculates the link to the websocket.
        const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
        const socketUrl = `${socketProtocol}//${websocketHost}:${websocketPort}/raku`;
        socket = new WebSocket(socketUrl);

        // This will fire once the socket opens
        socket.onopen = (e) => {
            // Send a little test data, which we can use on the server if we want
            socket.send(JSON.stringify({ "loaded" : true }));
            // Resolve the promise - we are connected
            resolve();
        }

        // This will fire when the server sends the user a message
        socket.onmessage = (data) => {
            let parsedData = JSON.parse(data.data);
            const resOut = document.getElementById('raku-ws-stdout');
            const resErr = document.getElementById('raku-ws-stderr');
            resOut.textContent = parsedData.stdout;
            resErr.textContent = parsedData.stderr;
        }

When an HTML file with this JS script is set up, and served locally I can send data to the Cro app running on an inte.net server, and the Cro App (running in a docker image) processes and returns data which is placed in the right HTML container. Using Firefox and the developer tools, I can see that the ws connection is created.

But when I serve the same file via Apache which forces access via https, Firefox issues an error that the 'wss' connection cannot be created. In addition, if I force a 'ws' connection in the JS script, Firefox prevents the creation of a non-secure connection.

a) How do I change the Cro coding to allow for wss? From the Cro documentation it seems I need to add a Cro::TLS listener, but it isn't clear where to instantiate the listener. b) If this is to be in a docker file, would I need to include the secret encryption keys in the image, which is not something I would like to do? c) Is there a way to put the Cro app behind the Apache server so that the websocket is decrypted/encrypted by Apache?

Though is not a pure cro solution, but you can run your cro app on (none ssl/https) http/web socket port - localhost and then have an Nginx server (configured to serve https/ssl trafic) to handle incoming public https/ssl requests and bypass them as a plain http traffic to your app using nginx reverse proxy mechanism (this is also often referred as an ssl termination), that way you remove a necessity to handle https/ssl on cro side. The only hurdle here might be if a web sockets protocol is handled well by Nginx proxy. I've never tried that but probably you should be fine according to the Nginx docs - https://www.nginx.com/blog/websocket-nginx/

How do I change the Cro coding to allow for wss? From the Cro documentation it seems I need to add a Cro::TLS listener, but it isn't clear where to instantiate the listener.

Just pass the needed arguments to Cro::HTTP::Server , it will set up the listener for you.

If this is to be in a docker file, would I need to include the secret encryption keys in the image, which is not something I would like to do?

No. You can keep them in a volume, or bind-mount them from the host machine.

Is there a way to put the Cro app behind the Apache server so that the websocket is decrypted/encrypted by Apache?

Yes, same as with any other app. Use mod_proxy and a ProxyPass command. Other frontends such as nginx, haproxy, or envoy will also do the job.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM