web-dev-qa-db-ja.com

Apache HTTPSプロキシを介してWebSocketを機能させることができません(302エラー)

HTTPSを介してApacheプロキシを使用してノードインスタンスに接続することで、ノードバックエンドでWebSocketを機能させることができません。 (Apache)http(s)プロキシが使用されていない場合、WebSocketは正しく機能しています。

私のセットアップ:複数の仮想ホストを備えたApacheサーバーがあります。 myserver.comのHTTPSWebページとプロキシを介してapi.myserver.comサブドメインにnode/express/wsを含むHTTPSAPIがあり、ポート3333で実行されているnode.jsインスタンス(PM2上の複数のインスタンス)にリクエストをリダイレクトします。 。

これはサブドメインの私のApache仮想ホストです:

<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>

これは、接続をノードエクスプレスバックエンドにリダイレクトする場合に問題なく機能します。 mod_proxy、mod_proxy_http、mod_proxy_wstunnelをインストールしました。

これはnode.jsAPIバックエンドです。最初に、express、sessionsなどを初期化します。

// 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)

次に、Apacheと同じ証明書を使用して、HTTPSサーバーを安全にセットアップします。

// 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)
})

最後に、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) {
        ...
    })
})

次に、クライアントはWebSocketに安全に接続しようとします(HTTPSアプリであるため、ws://の安全でないWebSocket接続を使用できないため)。

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

そして、私は常に同じエラー302を受け取ります:

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

ローカルサーバーでノードインスタンスに直接テストすると、 https:// localhost:3333 / 完全に機能し、WebSocketは正常に機能します。

これを解決する方法のアイデアはありますか? Apacheプロキシモジュールによって行われたwsリダイレクトに問題はありますか?

3
Digital Leaves

ApacheでHTTPSを設定しているので、クライアントから物理サーバーへの安全な接続が得られます。 Apacheからローカルホスト上のnode.jsアプリへの接続をプロキシしているため、可能な盗聴者はすでに物理的にボックス上にあります。アプリに プロキシを信頼する with app.enable('trust proxy'); app.set("trust proxy", 1);と指示しました

では、なぜnode.jsにもHTTPSを構成するのでしょうか。 Apache/nginx/haproxyを使用してWebアプリをフロントエンドする主な理由の1つは、TLSオフロードです。

リモートクライアントIPでX-Forwarded-Forヘッダーを取得することに加えて、プロキシ信頼を有効にするとX-Forwarded-Protoも取得されるため、Expressアプリは接続がhttpかhttpsかを認識します。したがって、Apacheとノードの間のhttpsレイヤーは、アプリにオーバーヘッドを導入する以外にほとんど何もしていません。

ApacheでWebSocketをプロキシするのを手伝うことはできません。それがまだhttpdの一連のパッチだったとき、私はApacheを使い始めました。 WebSocketが転がる前にやめたので、荷馬車から落ちる危険はありません。辛い思い出が多すぎます。興味がある場合は、haproxyを使用してWebSocketをExpressアプリの1つに渡す方法を次に示します。

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...
1
Matt Simerson