Mi entorno de desarrollo es este:
Creé una nueva aplicación de Laravel para implementar un servidor Secure Websocket y conectarme a él usando javascript simple en el lado del cliente (archivo blade de Laravel). El servidor websocket funciona bien, por lo que puedo ver, pero el navegador web no puede conectarse, como se ve en esta imagen:
He intentado usar diferentes URL, con y sin número de puerto, pero fue en vano. Creé un certificado SSL y archivos de clave privada, usando la herramienta openssl.exe, y los puse en la carpeta de comandos para fines de prueba.
Este es mi código de identificación para el servidor Secure Websocket:
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(); } Mi servidor virtual en el archivo httpd-ssl.conf :
<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>Se cargan los módulos de Apache proxy_module, proxy_http_module y proxy_wstunnel_module. La aplicación web se ejecuta en HTTPS. Antes, se ejecutaba a través de HTTP y WS y todo funcionaba perfectamente, pero necesito asegurar esta aplicación y tengo problemas para conectarme al servidor websocket seguro.
¿Me estoy perdiendo de algo?
¿Hay algún problema con mi servidor Websocket o con la configuración de Apache?
Seguramente estás tratando de conectarte al destino equivocado. Dice wss:///ssa/wss/, pero probablemente debería ser wss://your.site.domain/ssa/wss/ .
Así que echemos un vistazo al código front-end y descubramos qué tiene de malo.
Ok, como señaló @apokryfos, traté de enviar un proxy al servidor websocket a través de HTTPS, pero lo estaba haciendo de manera incorrecta.
Cambié mi servidor websocket a uno no seguro e hice el siguiente cambio en mi host virtual:
<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>En el lado del cliente, el navegador ahora puede comunicarse con el servidor WS backend a través del puerto HTTPS:
// The connection to the WebSocket Server. var socket = new WebSocket("wss://ssa:443/wss/");Obtuve esta solución de Apache Config: Websockets Proxy WSS request to WS backend
Ahora tengo mi servidor Websocket no seguro enviando/recibiendo a través de HTTPS. Sin duda, esta no es la solución que esperaba aplicar a mis necesidades, pero ciertamente funciona. Todavía espero encontrar una solución formal para conectar un cliente de JavaScript simple a un servidor Secure Websocket (wss://) sin usar un mecanismo de proxy.
Para no complicar mi primera respuesta con más información, aquí proporciono la respuesta que realmente funcionó para mí después de todo.
Creé el servidor Secure Websocket de la siguiente manera:
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(); }Tenga en cuenta que cambié el número de puerto a 8443 (no creo que esto tenga algo que ver) y también cambié el certificado y los archivos clave por los nuevos, generados de la siguiente manera:
openssl req -config config.conf -new -x509 -out server.crt -days 3650
Y el archivo config.conf es:
[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 Toda la diferencia radica en la última línea CA=false para indicar que no firmé ni actué como Autoridad de certificación (CA). Esto elimina el mensaje MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY .
Luego, me deshice de las líneas que definían el proxy en mi archivo httpd-ssl.conf:
<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>Tenga en cuenta que para este host virtual usé el mismo certificado y los archivos de clave que usé para el servidor Secure Websocket.
Ok, eso fue todo por mi problema de certificado.
Ahora todo funciona como se esperaba.