My Backend and Soketi servers run in same docker environment. I configure it with http and it works fine but now I want to use these services in production with ssl.
My backend placed behind Nginx and all http connections are redirected to 443 https port. So after I change laravel-websockets(pusher), Soketi and frontend code to use https I got error from backend code: cURL error 60: SSL certificate problem: unable to get local issuer certificate. Frontend code is abble to connect to soketi and subscribe to desired private channel.
Is it a way to continue use http to communicate between Backend and Soketi placed in same docker environment? If no what should I change in my configuration to wire my backend properly with https Soketi server?
app.js(Soketi connection and subscribe is ok):
import Echo from 'laravel-echo'
import Pusher from 'pusher-js'
window.Pusher = Pusher
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
forceTLS: false,
encrypted: false,
disableStats: true,
wsHost: window.location.hostname,
wsPort: 6007,
wssPort: 6007,
enabledTransports: ['ws', 'wss'],
})
Backend code(connection error: cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://soketi:6001/apps/myapp/events?auth_key=qwerty3&auth_timestamp=1698669966&auth_version=1.0&body_md5=f0ee1708e294c27b9200b1b942b4ad1d&auth_signature=b2648472a55b21d70b2f14a1e17s7ce39bcaeff7ba6afdsb256262ed05df79d12):
class RestoreHistoryFromBlockchainJobStatus implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(
public Token $token,
public int $userId,
public $result
) {}
public function broadcastAs()
{
return 'RestoreHistoryFromBlockchainJobStatus';
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('retroshooting.'.$this->userId);
}
}
RestoreHistoryFromBlockchainJobStatus::dispatch($this->token, $this->user->id, $action);
.env:
PUSHER_HOST=soketi
PUSHER_APP_ID=myapp
PUSHER_APP_KEY='qwerty'
PUSHER_APP_SECRET='qwerty'
PUSHER_PORT=6001
SOKETI_PORT=6007
PUSHER_APP_CLUSTER=mt1
PUSHER_SCHEME=https
docker-compose.yml:
### NGINX Server #########################################
nginx:
build:
context: ./nginx
args:
- CHANGE_SOURCE=${CHANGE_SOURCE}
- PHP_UPSTREAM_CONTAINER=${NGINX_PHP_UPSTREAM_CONTAINER}
- PHP_UPSTREAM_PORT=${NGINX_PHP_UPSTREAM_PORT}
- http_proxy
- https_proxy
- no_proxy
volumes:
- ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG}
- ${NGINX_HOST_LOG_PATH}:/var/log/nginx
- ${NGINX_SITES_PATH}:/etc/nginx/sites-available
- ${NGINX_SSL_PATH}:/etc/nginx/ssl
- ./certbot/letsencrypt/:/var/www/letsencrypt
- ../data/certbot/certs/:/var/certs
ports:
- "${NGINX_HOST_HTTP_PORT}:80"
- "${NGINX_HOST_HTTPS_PORT}:443"
- "${VARNISH_BACKEND_PORT}:81"
depends_on:
- php-fpm
networks:
- frontend
- backend
### Soketi Server ##############################################
soketi:
build:
context: ./soketi
volumes:
- ./soketi/config.json:/app/bin/config.json:ro
- ../data/certbot/certs/:/var/certs # for websockets
environment:
SOKETI_DEBUG: '0'
SOKETI_METRICS_SERVER_PORT: '${SOKETI_METRICS_SERVER_PORT}'
SOKETI_DEFAULT_APP_ID: '${SOKETI_DEFAULT_APP_ID}'
SOKETI_DEFAULT_APP_KEY: '${SOKETI_DEFAULT_APP_KEY}'
SOKETI_DEFAULT_APP_SECRET: '${SOKETI_DEFAULT_APP_SECRET}'
SOKETI_SSL_CERT: '${SOKETI_SSL_CERT}'
SOKETI_SSL_KEY: '${SOKETI_SSL_KEY}'
SOKETI_SSL_PASS: '${SOKETI_SSL_PASS}'
SOKETI_SSL_CA: '${SOKETI_SSL_CA}'
ports:
- "${SOKETI_PORT}:6001"
- "${SOKETI_METRICS_SERVER_PORT}:${SOKETI_METRICS_SERVER_PORT}"
networks:
- frontend
- backend
broadcasting.php:
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY', 'app-key'),
'secret' => env('PUSHER_APP_SECRET', 'app-secret'),
'app_id' => env('PUSHER_APP_ID', 'app-id'),
'options' => [
'host' => env('PUSHER_HOST', '127.0.0.1'),
'port' => env('PUSHER_PORT', 6001),
'scheme' => env('PUSHER_SCHEME', 'http'),
'encrypted' => true,
'useTLS' => env('PUSHER_SCHEME') === 'https',
],
],
nginx.conf:
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl ipv6only=on;
ssl_certificate /var/certs/www-cert1.pem;
ssl_certificate_key /var/certs/www-privkey1.pem;
server_name my-domain.net;
root /var/www/public;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri /index.php =404;
fastcgi_pass php-upstream;
fastcgi_index index.php;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#fixes timeouts
fastcgi_read_timeout 600;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/;
log_not_found off;
}
error_log /var/log/nginx/laravel_error.log;
access_log /var/log/nginx/laravel_access.log;
}
I tried to continue use http in my backend broadcasting(PUSHER_SCHEME=http in .env) and got error with empty response:
Pusher error: cURL error 52: Empty reply from server (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for http://soketi:6001/apps/myapp/events?auth_key=qwerty&auth_timestamp=1698693715&auth_version=1.0&body_md5=ac1e69bdf7a6538901fa1d46&auth_signature=aec99de9ffb5sajkkldvjsv109504edad0a. {"userId":1,"exception":"[object] (Illuminate\\Broadcasting\\BroadcastException(code: 0): Pusher error: cURL error 52: Empty reply from server
It is because pusher-php-server use curl under the hood that verify host of certificate by default. I use public certificate on my Soketi server so it fails to verify by curl when I try to connect via docker locally.
I solved it by adding client_options to pusher connection in broadcasting.php. It forces BroadcastManager to create Pusher instance with a custom GuzzleClient that takes into account 'verify' option. So full broadcasting.php pusher connection looks like: