My development environment is this:
I created a new Laravel app to implement a Secure Websocket Server and get connected to it using plain javascript on the client side (Laravel blade file). The websocket server works fine, as far as I can see it running, but the web browser is not able to connect, as seen on this image:
I have tried using different URLs, with and without port number, but to no avail. I created a SSL certificate and private key files, using openssl.exe tool, and put them in the command folder for testing purposes.
This is my handle code for the Secure Websocket Server:
public function handle()
{
$loop = Factory::create();
$webSock = new SecureServer(
new Server('0.0.0.0:8090', $loop),
$loop,
array(
'local_cert' => 'certificate.crt',
'local_pk' => 'private.key',
'allow_self_signed' => TRUE,
'verify_peer' => FALSE
)
);
// Ratchet magic
$webServer = new IoServer(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
$webSock
);
$loop->run();
}
My virtual host in httpd-ssl.conf file:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
ProxyRequests Off
ProxyPass /wss/ ws://ssa:8090
</VirtualHost>
The Apache modules proxy_module, proxy_http_module and proxy_wstunnel_module are loaded. The web app is running in HTTPS. Before, it was running over HTTP and WS and everything worked perfectly, but I need to secure this app and I am having issues to connect to the secure websocket server.
Am I missing something?
Is there something wrong with my Websocket server or Apache configuration?
You are surely trying to connect to the wrong destination. It says wss:///ssa/wss/, but probably it should be wss://your.site.domain/ssa/wss/ .
So let's look at front end code and find out what's wrong with it.
Ok, as @apokryfos pointed out, I tried to proxy the websocket server through HTTPS but I was doing it in the wrong way.
I changed my websocket server to a non-secure one and did the following change to my virtual host:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
Redirect /wss /wss/
ProxyPass /wss/ ws://127.0.0.1:8090/
ProxyPassReverse /ws/ wss://127.0.0.1:8090/
</VirtualHost>
On the client side, the browser can now contact the backend WS server through the HTTPS port:
// The connection to the WebSocket Server.
var socket = new WebSocket("wss://ssa:443/wss/");
I got this solution from Apache Config: Websockets Proxy WSS request to WS backend
Now I got my non-secure Websocket server sending/receiving through HTTPS. This is, for sure, not the solution I expected to apply to my needs but it certainly works. I still hope to find a formal solution to connecting plain JavaScript client to a Secure Websocket Server (wss://) without using a proxy mechanism.
For not to complicate my first answer with more information, here I provide the answer that really worked for me after all.
I created the Secure Websocket Server as follows:
public function handle() {
$loop = Factory::create();
$webSock = new SecureServer(
new Server('0.0.0.0:8443', $loop),
$loop,
array(
'local_cert' => 'C:/wamp64/bin/apache/apache2.4.41/conf/server.crt',
'local_pk' => 'C:/wamp64/bin/apache/apache2.4.41/conf/server.key',
'allow_self_signed' => TRUE,
'verify_peer' => FALSE
)
);
$webServer = new IoServer(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
$webSock
);
$loop->run();
}
Note I changed the port number to 8443 (I don't think this has something to do) and also changed the certificate and key files for the new ones, generated as follows:
openssl req -config config.conf -new -x509 -out server.crt -days 3650
And the config.conf file is:
[req]
default_bits = 2048
encrypt_key = no
default_md = sha256
default_keyfile = server.key
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = KH
ST = Siem Reap
L = SR
O = AHC
OU = IT
CN = localhost
[bs_section]
CA=false
All the difference lies in the last line CA=false to indicate I did not signed or acted as a Certificate Authority (CA).
This gets rid of the MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY message.
Then, I got rid of the lines that defined the proxy in my httpd-ssl.conf file:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/server.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/server.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
#Redirect /wss /wss/
#ProxyPass /wss/ ws://127.0.0.1:8090/
#ProxyPassReverse /ws/ wss://127.0.0.1:8090/
</VirtualHost>
Please notice that for this virtual host I used the same certificate and key files I used for the Secure Websocket Server.
Ok, that was it for my certificate issue.
Now everything works as expected.