Não é possível obter websockets para trabalhar através do proxy HTTPS do apache (erro 302)

4

Não consigo fazer com que os websockets funcionem em um backend de nó usando um proxy do apache através de HTTPS para se conectar à instância do nó. Os Websockets estão funcionando corretamente se nenhum proxy HTTP (apache) for usado.

Minha configuração: Eu tenho um servidor apache com vários hosts virtuais. Eu tenho uma página da Web HTTPS para myserver.com e a API HTTPS com node / express / ws no subdomínio api.myserver.com através do proxy, que redireciona as solicitações para a instância node.js (várias instâncias no PM2) em execução na porta 3333 .

Este é o meu host virtual do apache para o subdomínio:

<VirtualHost *:443>
    ServerName api.myserver.com
    ServerAdmin [email protected]
    DocumentRoot /var/www/html/myserver/api
        Options -Indexes

    SSLEngine on                                                                
    SSLProtocol all -SSLv2                                                      
    SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM                

    SSLCertificateFile /etc/apache2/certs/STAR_myserver_co.crt
    SSLCertificateKeyFile /etc/apache2/certs/myserver_private_key.pem
    SSLCertificateChainFile /etc/apache2/certs/STAR_myserver_co.ca-bundle

    SSLProxyEngine On
    ProxyPreserveHost On
    ProxyRequests Off

    # This is for websocket requests
    ProxyPass /wss/ wss://localhost:3333/
    ProxyPassReverse /wss/ wss://localhost:3333/

    # This is for normal requests    
    ProxyPass / https://localhost:3333/
    ProxyPassReverse / https://localhost:3333/
</VirtualHost>

Isso funciona bem para redirecionar as conexões para o backend expresso do nó. Eu instalei o mod_proxy, mod_proxy_http e mod_proxy_wstunnel.

Este é o backend da API node.js: primeiro, inicializo express, sessões, etc.

// express, session and mongodb session storage
var express = require('express')
var session = require('express-session')
var MongoStore = require('connect-mongo')(session)
var app = express()
// configure sessionStore and sessions, mongodb, etc...

// Certificates and credentials for HTTPS server
var fs = require('fs')
var privateKey  = fs.readFileSync(__dirname + '/certs/myserver_private_key.pem', 'utf8')
var certificate = fs.readFileSync(__dirname + '/certs/myserver_cert.pem', 'utf8')
var ca = fs.readFileSync(__dirname + '/certs/myserver_ca.pem', 'utf8')
var credentials = {key: privateKey, cert: certificate, ca: ca}

app.enable('trust proxy')
app.set("trust proxy", 1)

E então eu configuro o servidor HTTPS com segurança, usando os mesmos certificados do APACHE:

// Setup HTTPS server
var https = require('https')
var server = https.createServer(credentials, app)
server.listen(appPort, 'localhost', function () {
    // Server up and running!
    var host = server.address().address
    var port = server.address().port
    console.log('myserver listening at https://%s:%s', host, port)
})

Por último, configuro as conexões do websocket:

// setup Websockets
wss = new WebSocketServer({ server: server })
wss.on('connection', function connection(ws) {
    var cookies = cookie.parse(ws.upgradeReq.headers.cookie)
    var sid = cookieParser.signedCookie(cookies["connect.sid"], myserver_secret)
    // extract user credentials and data from cookie/sid,

    // get the session object
    sessionStore.get(sid, function (err, ss) {
        ...
    })
})

Então meus clientes tentam se conectar a websockets com segurança (porque, sendo um aplicativo HTTPS, não posso usar a conexão ws: // insecure websockets):

window.WebSocket = window.WebSocket || window.MozWebSocket
webSocket = new WebSocket('wss://' + location.host + '/wss')

E então recebo sempre o mesmo erro 302:

[Error] WebSocket connection to 'wss://api.myserver.com/wss' failed: Unexpected response code: 302

Se eu testar em um servidor local diretamente para a instância do nó link , ele está funcionando perfeitamente e os websockets funcionam como deveriam.

Alguma ideia de como resolver isso? Existe algum problema com os redirecionamentos do ws feitos pelos módulos proxy do Apache?

    
por Digital Leaves 15.11.2016 / 11:49

1 resposta

1

Você já configurou o HTTPS no Apache para ter uma conexão segura do cliente para o servidor físico. Você está intermediando a conexão do Apache para o seu aplicativo node.js no host local, portanto, os únicos bisbilhoteiros possíveis já estão fisicamente na sua caixa. Você disse ao seu app para confiar no proxy com app.enable('trust proxy'); app.set("trust proxy", 1);

Então, por que você também configuraria HTTPS para node.js? Um dos principais motivos pelos quais fazemos o front-end de nossos aplicativos da Web com o apache / nginx / haproxy é o descarregamento de TLS.

Além de obter o cabeçalho X-Forwarded-For com o IP de clientes remotos, a ativação da confiança de proxy também gera X-Forwarded-Proto para que seu aplicativo Express saiba se a conexão era http ou https. Portanto, a camada https entre o Apache e o nó está fazendo pouco além de introduzir a sobrecarga do seu aplicativo.

Eu não posso ajudá-lo com proxy WebSockets com Apache. Comecei a usar o Apache quando ainda era uma série de correções para o httpd. Eu parei antes que os WebSockets rolassem e não posso arriscar cair do vagão. Muitas lembranças dolorosas. Se você está curioso, aqui está como eu uso o haproxy para passar WebSockets para um dos meus aplicativos expressos:

mode      http
option    forwardfor
frontend http-in
bind :::80 v4v6
bind :::443 v4v6 ssl crt /etc/ssl/private
http-request  set-header X-Forwarded-Proto https if { ssl_fc }
http-request  set-header X-Forwarded-Port %[dst_port]
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_websocket hdr_beg(Host) -i ws

redirect scheme https code 301 if !is_websocket !{ ssl_fc }
use_backend websocket_server if  is_websocket

backend websocket_server
timeout queue 5s
timeout server 86400s
timeout connect 86400s
server node_app 127.0.0.1:8080

backend ...others...
    
por 30.08.2017 / 06:50