web-dev-qa-db-ja.com

Apache 2.4.7 mod_proxy_wstunnelトンネリングが多すぎる(HTTPとWS)

Ubuntu 14.04 LTSでリバースプロキシとしてApache 2.4.7を実行しています。このApacheサーバーは、<Location>ブロック内のさまざまなmod_proxy構成を介してアクセスされるさまざまなバックエンドアプリケーションへのエントリポイントとして機能します

WebSocketを使用するアプリケーションへのリバースプロキシアクセスを提供する必要があります。アプリケーションはJava HTTPを介してHTMLおよびその他の静的ファイルを提供するSpringアプリケーションであり、ページが読み込まれた後、動的データにWebSocketを使用します。

次の設定を使用して、Nginxの背後でアプリケーションを実行しています。

location /newapp/ {
    proxy_pass http://newapp.example.com:8080/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

残念ながら、Nginxでは利用できないApache認証モジュールが必要なため、本番環境ではこれを使用できません。

私がしたいのは、pseudo-Apache-configで:

<Location /newapp/>
    if not WebSockets:
        ProxyPass http://newapp.example.com:8080/
        ProxyPassReverse /
    else
        ProxyPass ws://newapp.example.com:8080/
        ProxyPassReverse /
</Location>

Apache mod_proxy_wstunnelモジュールは、これが可能であるはずだと思います。 WebSocketはURL /api/socket/...でアクセスされるため、別々の<Location>ブロックを使用して2種類のProxyPassを分離してみました。

<Location /newapp/>
    ProxyPass http://newapp.example.com:8080/ disablereuse=on
    ProxyPassReverse /

    ProxyPassReverseCookieDomain newapp.example.com apps.example.com
    ProxyPassReverseCookiePath http://newapp.example.com:8080/ /newapp/
</Location>

<Location /newapp/api/socket/>
    ProxyPass ws://newapp.example.com:8080/api/socket/
    ProxyPassReverse /
</Location>

これは最初に機能します-ブラウザは http://apps.example.com/newapp/ を要求し、ページはHTTP経由でロードされ、静的アセットがロードされ、JavaScriptコードはWebSocketに接続し、すべてが素晴らしいです。

ただし、新しいリクエストがHTTPを介して行われる場合、たとえばGET /newapp/static/someimage.pngの場合、何か問題が発生します。このリクエストはWebSocketの場所と一致しないため、GET /static/someimage.pngからhttp://newapp.example.com:8080/へのプロキシが必要です。

代わりに、アプリケーションサーバーはGET /newapp/static/someimage.pngのリクエストを受け取り、404を返します。これは、これが認識しているURLではないためです。動作するはずのHTTPリクエストが代わりに失敗するため、これはアプリケーションを破壊します。

ノート:

  • これは画像だけで発生するのではありません-GET /newapp/api/ajax/someapicallも正しくプロキシされません。
  • これは常に起こるとは限りません。この質問を完了するために物事をテストしながら、アプリを完全に動作させることができました。これは時間ベースであった可能性があります。新しいHTTPリクエストを行う前に、数分間操作せずにアプリを実行したままにしました。新しいHTTPリクエストを行ったとき、それらは正しく通過しました。
  • <Location /newapp/api/socket/>セクションを無効にすると、WebSocketが接続に失敗し、HTTPリクエストが引き続き機能するという2つのことが起こります。
  • ブラウザの[更新]ボタンを使用してページを更新した後、この問題を発見しました。ページが再度読み込まれる代わりに、アプリの404画面が表示されました。

私は何が起こっていると思います:

mod_proxy_wstunnelは、/newapp/api/socket/に一致する最初の要求によってアクティブ化されると、Locationに一致するかどうかにかかわらず、クライアントからのすべてのインバウンド要求を引き継ぐと思います。各LocationRequestHeader set Test "some_identifying_value"ディレクティブを追加してこれをテストしました-静的ファイルと/api/socket/infoのHTTPリクエストにはTestヘッダーがありましたが、プロキシされたHTTPリクエストにはTestヘッダーがありませんでした。これは、Apacheディレクティブで処理されずにそのまま渡されることを示しています。

結局のところ、私の質問は次のとおりです。Apacheの任意のバージョン(アップグレードしてよろしいですか?)を構成して、WebSocketの後にHTTPリクエストも正しくリバースプロキシされるように、WebSocketベースのアプリケーションをリバースプロキシすることができますか。接続されていますか?もしそうなら、これはどのように構成されていますか?

2
Brian Beckett

アンデルスの答えは私がそこに行く道の95%を得ました。

基本的なシナリオ:

  • newapp.example.comにサーバーがあります
  • ポート8080はHTTPとWebSocketの両方を実行しています
  • WebSocketsリクエストに応答するURLは/api/socket/です
  • このアプリケーションをhttp://apps.example.com/newapp/としてリバースプロキシしています

これは、上記のシナリオの<Location>ブロックでWebSocketとHTTPリバースプロキシを構成する方法です。

<Location /newapp/>
    ProxyPass http://newapp.example.com:8080/
    ProxyPassReverse /

    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
    RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
    RewriteRule /api/(.*) ws://newapp.example.com:8080/api/$1 [P]
</Location>

最終的な書き換えルールは非常に重要です。これがない場合、リクエストを/newapp/api/socketからWebSocketサーバーに渡します-WebSocketサーバーは拒否します。

正規表現はapi以降のすべてを解析しています-そのブロックをキャプチャするより良い方法があるかもしれませんが、これはうまくいきました。次に、/api/を最終リダイレクトURLに再度追加することを忘れないでください。

最も重要なのは、WebSocket接続が確立された後もHTTPリクエストが引き続き機能することです。

6
Brian Beckett

私は自分のスプリングブートアプリの前でApache 2.4をプロキシとして使用しています。このアプリは、残りのAPI呼び出し、WebSocket(sockjs)、およびいくつかの静的ページを提供します。私はwebsocketを機能させるのにいくつかの問題を抱えていました、秘密を追加する必要がありました rewriteルール 以下を参照してください。

<VirtualHost *:443>
  SSLEngine on
  SSLCertificateFile /etc/httpd/ssl/my.crt
  SSLCertificateKeyFile /etc/httpd/ssl/my.key
  SSLCertificateChainFile /etc/httpd/ssl/intermediate.crt
  ProxyPreserveHost On
  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
  ProxyRequests Off
  RewriteEngine on
  RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
  RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
  RewriteRule .* ws://localhost:6868%{REQUEST_URI} [P]
  ServerName my.dnsname.com
</VirtualHost>
2
anders