Cannot get TLS to work on proxied websocket with nginx

Open
#142 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
25/100
Issue type
Bug
Clarity
Needs clarification
Activity status
Stale
Tech stack
nginx, php
Domain
networking

Research direction

Start by reproducing the failed handshake with the Pawl client using the provided PHP client and nginx configuration, comparing behavior with verify_peer enabled and disabled. Inspect the TLS settings in the client, Ratchet server, and nginx proxy; done means identifying the handshake failure and establishing a verified wss connection through /wss.

Written by the indexing model from the issue text.

Description

I can get it to work with a javascript websocket but it will not work with a Pawl websocket. It seems like the handshake always fails and I'm not too sure how to debug it.

Here's my client code:

$secureContext = array(
  'verify_peer' => false,
  'verify_peer_name' => true,
  'local_cert' => '/redacted/fullchain.pem',
  'local_pk' => '/redacted/privkey.pem'
);
$reactConnector = new \React\Socket\Connector([
    'dns' => '8.8.8.8',
    'timeout' => 10,
    'tls' => $secureContext
]);
$loop = \React\EventLoop\Loop::get();
$connector = new \Ratchet\Client\Connector($loop, $reactConnector, $secureContext);

$connector('wss://vps1.mazion.tv/wss')->then(function($conn) {
    $conn->send('eyyy!');
    $conn->close();
}, function ($e) {
    echo "Could not connect: {$e->getMessage()}\n";
});

Here is my server code:

class saveTheBeardWS implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        echo $msg;
        foreach ($this->clients as $client) {
            if ($from != $client) {
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}

$loop = Factory::create();

$server = new Server('127.0.0.1:8843', $loop);

$secureServer = new SecureServer($server, $loop, [
    'local_cert'  => '/redacted/fullchain.pem',
    'local_pk' => '/redacted/privkey.pem',
    'verify_peer' => false,
]);

$httpServer = new HttpServer(
    new WsServer(
        new saveTheBeardWS()
    )
);

$ioServer = new IoServer($httpServer, $secureServer, $loop);

$loop->run();

Here's my nginx configuration with the proxy for the websocket:

map $http_upgrade $connection_upgrade {                                                                                                                                                                            
    default upgrade;                                                                                                                                                                                               
    '' close;                                                                                            
}                                 
                                                    
upstream localhost{                                                                                                                                                                                                
    server 127.0.0.1:8843;                                                                               
}                                                                                                        
                                                                                                         
server{                                  
    listen [::]:443 ssl ipv6only=on; # managed by Certbot                                                
    listen 443 ssl; # managed by Certbot                                                                 
    ssl_certificate /redacted/fullchain.pem; # managed by Certbot             
    ssl_certificate_key /redacted/privkey.pem; # managed by Certbot           
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot                                
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot                                  
    server_name redacted;                                                                          
    error_log /redacted/error_log warn;       
    root /redacted;                                                                    
    index index.php;                                                                                                                                                                                               
                                                                                                                                                                                                                   
    #error_page 404 /404.html;                                                                                                                                                                                     
    #error_page 500 502 503 504 /50x.html;                                                                                                                                                                         
                                                                                                                                                                                                                   
    location /wss {                                
        proxy_pass https://localhost;                                                                    
        proxy_ssl_certificate /redacted/fullchain.pem;                                                                                                                                  
        proxy_ssl_certificate_key /redacted/privkey.pem;                      
        proxy_ssl_session_reuse on;                                                                      
        proxy_set_header X-Real-IP $remote_addr;                                                                                                                                                                   
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;                                     
        proxy_http_version 1.1;                                                                          
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;             
        proxy_set_header Host $host;                                                                                                                                                                               
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect off;                                                                              
        proxy_read_timeout 86400s;             
        proxy_send_timeout 86400s;
        keepalive_timeout 86400s;
        reset_timedout_connection on;
    }

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include /etc/nginx/conf.d/fastcgi.conf;
        try_files $uri =404;
        fastcgi_index  index.php;
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        include fastcgi_params;
    }

    location ~ (\.ht|\.json|\.db) {
        deny all;
        return 404;
    }

    location ^~ /vendor {
        deny all;
        return 404;
    }
}

server{
    listen 80;
    listen [::]:80;
    server_name redacted;
    return 301 https://$host$request_uri;
}

If I enable verify_peer on the client-side. It fails to handshake.

If I understand correctly, with verify_peer disabled I am leaving the door open for MITM attacks. What are the risks associated with keeping verify_peer disabled?

What am I missing to get the TLS handshake to work?

Dominant language
PHP
Stars
616
Forks
98
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ratchetphp/Pawl

All issues in ratchetphp/Pawl

Similar issues

More PHP issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.