Hi There,
First of all, thank you for a great plugin.
I have looked at the other questions regarding SSL and none seem to cover my scenario.
I am having an issue with Self Signed SSL Certificates.
I have created them with this command :
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
I then set the broadcasting.php as follows :
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
'host' => 'sockets.myDomainHere.com',
'port' => 6001,
'scheme' => 'https',
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
]
],
],
I Then Set the websockets.php as follows ( comment removed for size ):
'ssl' => [
'local_cert' => '/full/path/to/self/signed/cert.cert',
'local_pk' => '/full/path/to/self/signed/key.key',
'passphrase' => 'ThePassphraseIEnteredWhenCreatingTheCertificate',
'verify_peer' => false,
],
On my client I have it as follows :
new Echo({
auth:{ headers: { 'Authorization': 'Bearer ' + user.token } },
broadcaster: 'pusher',
key: '123456',
wsHost: 'sockets.myDomainHere.com',
wsPort: 6001,
wssPort: 6001,
disableStats: true,
enabledTransports: ['ws', 'wss']
});
So I have 2 issues :
1) If I go to the Debug Dashboard I cannot connect to the Server
ERROR :
pusher.min.js:8 WebSocket connection to 'ws://sockets.myDomainHere.com:6001/app/123456?protocol=7&client=js&version=4.3.1&flash=false' failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESET
2) If I go to my app frontend, I get this :
WebSocket connection to 'wss://sockets.myDomainHere.com:6001/app/123456?protocol=7&client=js&version=4.3.1&flash=false' failed: Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID
Any feedback will be great, Thanks.
I _think_ you need to enable the wss transport manually in the view for the dashboard to get it to work on wss (https://github.com/beyondcode/laravel-websockets/blob/master/resources/views/dashboard.blade.php#L127)
You will also need to still trust the self-signed certificate in your OS or in your browser (Google your OS + browser + "trust self-signed certificate" for detailed instruction). Only use self-signed when developing, never instruct your users to trust a self-signed certificate (use Let's Encrypt for a free certificate)
Hope the above helps!
i spent 2 days reading every article and documentation, to figure out the problem, and i turned out to be using wrong port for ssl so just change the port in the broadcasting config file to ssl port and here is my config snippet
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'host' => 'socket.example.com',
'port' => 443,
'scheme' => 'https',
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
]
],
I managed to get it working, and it is really great.
In my case it was the certificates I was using, I switched over to Let's Encrypt and it all worked great
Holy @thesimpledesigners, you saved my life!
I struggled with this for days and was about to give up.
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
]
did the trick.
Thank you so much!
I just want to leave this warning here for anyone who might come along later.
I would really advise anyone to NOT disable VERIFYHOST and VERIFYPEER, that's just throwing away all the security HTTPS provides. This only makes sense to do if you trust the network your application is running on (no risk of man-in-the-middles), and that you have no good option for trusting the certificate. I recommend taking a look at https://github.com/paragonie/certainty which should allow you to use an up-to-date trust store, and also provide your own certificates to that store (useful when using self-signed certificates).
A better option over self-signed certs would be to make your own CA instead, which allows you to trust the CA instead of having to trust the individual self-signed certs. For that, check out https://github.com/FiloSottile/mkcert which can make the process of managing a CA much simpler.
@cbezzy Your configuration is OK, it worked
What I do not understand is that let's encrypt creates the cert and key files that change names monthly....How did you include the paths here if the filenames keep changing?
'ssl' => [
'local_cert' => '/full/path/to/self/signed/cert.cert',
'local_pk' => '/full/path/to/self/signed/key.key',
'passphrase' => 'ThePassphraseIEnteredWhenCreatingTheCertificate',
'verify_peer' => false,
],
Thanks,
@FreddyCrugger I'm not sure they change their names, I have been lets encrypt for all most a year on 4 websites and I have never faced that issue.
@FreddyCrugger I'm not sure they change their names, I have been lets encrypt for all most a year on 4 websites and I have never faced that issue.
Thanks for the reply.
Yes this is the kind of files I get:
/home/account/ssl/account_com_a25e6_65c3b_1577536339_599647d72a3bcf7b.crt
Then 30 days later I get this:
/home/account/ssl/account_com_a25e6_65c3b_1577536339_98797647d873bc87.crt
And so on......I am really stuck on this...
@FreddyCrugger letsencrypt certs are symlinked in /etc/letsencrypt/live/[website_domain]/ so that in "live" folder you have always the current cert with the same static filename.
@ordinov there is no letsencrypt folder in etc folder. let's encrypt do change file name in shared hosting.
In my case Laravel Web sockets couldn't read the SSL file because I didn't set the correct permissions for the user running the websockets i.e. www-data, etc. So either copying the certificate files to a place where www-data, or the user who will be running the websockets, can access, or setting appropriate permissions. [From stack overflow] https://serverfault.com/questions/216477/what-should-be-the-permissions-of-apache-ssl-directory-certificate-and-key.
Hi all. After two days modifying the package's sources to guess why wss requests were in vain, trying different approachs, working in local and on my server, I understood that the problem may or may not be on wss transport by laravel-websockets, or may even be or not be in the lack of console logs by the package's processes taking place on execution time of the websocket server. At that point I worked out a solution by reverse proxing with nginx.
Simple and elegant, requests to the socket will be sent towards the app server, under https using your vhost's installed cert,with no need to configure any cert to the .env variables for that putpose, as all traffic between the websovket server an dthe user gets proxied locally on your server through a reverse proxy configured at nginx directives for the vhost where your app may be.
(This sample expects pusher host port to be 6001)
location /app {
proxy_pass http://localhost:6001;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
# Allow the use of websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
These nginx directives intercept the requests under the /app path, and make your vhost work for them as a proxy for wss requests. Hence traffic is encrypted by means of the website's SSL cert.
Apache rules are not an option as mod_rewrite can intercept upgrade headers but cannot proxy them to localhoat directly AND ProxyPass directives ignore upgrade headers and don't support ws protocols. So rewriting upgraded https requests lead to no proxying option and proxying to ws protocol is not supported by apache. In Spain we call it "_es como la pescadilla que se muerde la cola"_ something like _is like the fish that bites its own tail_.
As you may see at this point, Apache is actually unable to afford a valid rulesed definition to reach a websocket by wss protocol. Hence nginx reverse proying technic is the best approach to an ideal fully secured transport chain I were able to guess, while preserving pusher clients staying under SSL encryption and relying on the integrity of the server for the last jump from nginx layer to local server port for websocket instance relying on supposed beyondcode package's 'encrypted' option.
I tried proxing to another vhosts, also combining nginx and apache. The only solution preserving encryption of end user incoming & outgoing requests ended up being this one, which inside the server works in plain http. Is true it exposes that requests inside the server and could be intercepted whem proxied to localhost by your server's inner dataflows.
Is a weak point altough. Having this part exposed as many would say. As I think, if this is a problem, then is the less important problem as if this part of the data flow is compromised it means the server is in itself compromised.
And please get this package to log in prod at will. Is a pain in the ass when you put pem fullchain and privkey certs truly working with not only website but also for signing mails and this package simply stays muted at log level delivering no other thing that 404's when server logs clearly indicate requests under wss are all pointing to urls coded as routes by this package when on wss protocol. It's nonsense the combo of a non working wss with suck lack of console logs in production.
broadcasting.php
`
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_DRIVER', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
'host' => '127.0.0.1',
'port' => 8443,
'scheme' => 'https',
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
]
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];
`
websockets.php
`
return [
/*
* Set a custom dashboard configuration
*/
'dashboard' => [
'port' => env('LARAVEL_WEBSOCKETS_PORT', 8443),
],
/*
* This package comes with multi tenancy out of the box. Here you can
* configure the different apps that can use the webSockets server.
*
* Optionally you specify capacity so you can limit the maximum
* concurrent connections for a specific app.
*
* Optionally you can disable client events so clients cannot send
* messages to each other via the webSockets.
*/
'apps' => [
[
'id' => env('PUSHER_APP_ID'),
'name' => env('APP_NAME'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'path' => env('PUSHER_APP_PATH'),
'capacity' => null,
'enable_client_messages' => true,
'enable_statistics' => true,
'encrypted' => true,
],
],
/*
* This class is responsible for finding the apps. The default provider
* will use the apps defined in this config file.
*
* You can create a custom provider by implementing the
* `AppProvider` interface.
*/
'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,
/*
* This array contains the hosts of which you want to allow incoming requests.
* Leave this empty if you want to accept requests from all hosts.
*/
'allowed_origins' => [
//
],
/*
* The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
*/
'max_request_size_in_kb' => 250,
/*
* This path will be used to register the necessary routes for the package.
*/
'path' => 'laravel-websockets',
/*
* Dashboard Routes Middleware
*
* These middleware will be assigned to every dashboard route, giving you
* the chance to add your own middleware to this list or change any of
* the existing middleware. Or, you can simply stick with this list.
*/
'middleware' => [
'web',
Authorize::class,
],
'statistics' => [
/*
* This model will be used to store the statistics of the WebSocketsServer.
* The only requirement is that the model should extend
* `WebSocketsStatisticsEntry` provided by this package.
*/
'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,
/**
* The Statistics Logger will, by default, handle the incoming statistics, store them
* and then release them into the database on each interval defined below.
*/
'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class,
/*
* Here you can specify the interval in seconds at which statistics should be logged.
*/
'interval_in_seconds' => 60,
/*
* When the clean-command is executed, all recorded statistics older than
* the number of days specified here will be deleted.
*/
'delete_statistics_older_than_days' => 60,
/*
* Use an DNS resolver to make the requests to the statistics logger
* default is to resolve everything to 127.0.0.1.
*/
'perform_dns_lookup' => false,
],
/*
* Define the optional SSL context for your WebSocket connections.
* You can see all available options at: http://php.net/manual/en/context.ssl.php
*/
'ssl' => [
/*
* Path to local certificate file on filesystem. It must be a PEM encoded file which
* contains your certificate and private key. It can optionally contain the
* certificate chain of issuers. The private key also may be contained
* in a separate file specified by local_pk.
*/
'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),
/*
* Path to local private key file on filesystem in case of separate files for
* certificate (local_cert) and private key.
*/
'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),
/*
* Passphrase for your local_cert file.
*/
'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),
'verify_peer' => false,
'allow_self_signed' => true,
],
/*
* Channel Manager
* This class handles how channel persistence is handled.
* By default, persistence is stored in an array by the running webserver.
* The only requirement is that the class should implement
* `ChannelManager` interface provided by this package.
*/
'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,
];
`
bootstrap.js
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
encrypted: true,
wsHost: window.location.hostname,
wsPort: 8443,
wssPort: 8443,
forceTLS: true,
disableStats: false,
enabledTransports: ['ws', 'wss']
});
.env
BROADCAST_DRIVER=pusher
APP_URL_BASE=DOMAIN_NAME_HERE
APP_URL=https://${APP_URL_BASE}
LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=/etc/letsencrypt/live/DIR_NAME_HERE/fullchain.pem
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=/etc/letsencrypt/live/DIR_NAME_HERE/privkey.pem
/etc/supervisorctl/conf.d/websockets.conf
[program:websockets]
process_name=%(program_name)s
command=sudo php artisan websockets:serve --port=8443
directory=/var/www/html/PROJECT_NAME_HERE
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/www/html/PROJECT_NAME_HERE/storage/logs/websockets.log
Check your port that it is not disabled.
ufw allow 6001 - open this port
Most helpful comment
i spent 2 days reading every article and documentation, to figure out the problem, and i turned out to be using wrong port for ssl so just change the port in the broadcasting config file to ssl port and here is my config snippet