web-dev-qa-db-ja.com

HAProxy CORSOPTIONSヘッダーインターセプトのセットアップ

NGinxのセットアップで、ajaxプリフライトからのOPTIONSリクエストをインターセプトし、正しいCORSヘッダーと200のレスポンスで応答して、リクエストを続行できるようにしました。フロントエンドプロキシをHAProxyに統合しようとしていますが、このパズルのピースを機能させるのに問題があります。

私の特定の問題は、OPTIONSリクエストに適切に応答できるサーバーがある場合、適切なCORSオプションを追加できますが、一部のバックエンドは、プリフライトリクエストが発行されたときに405エラーで処理/応答できないことです。私のhaproxy.cfgには、ヘッダーを追加するための次の行が含まれていました。

capture request header Origin len 128
http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Credentials:\ true if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Headers:\ Origin,\ X-Requested-With,\ Content-Type,\ Origin,\ User-Agent,\ If-Modified-Since,\ Cache-Control,\ Accept if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Methods:\ GET,\ POST,\ PUT,\ DELETE,\ OPTIONS if { capture.req.hdr(0) -m found }
rspadd Access-Control-Max-Age:\ 1728000 if { capture.req.hdr(0) -m found }

与えられた解決策:

Webサーバーに要求を渡さずにHAProxyで応答を送信する方法 クライアントの要求からすべての正しいヘッダーを設定すると機能しますが、動的ではないため、理想的なソリューションではありません。

どんな助けでもいただければ幸いです!

13
Brian

Luaを使用できますが、USE_LUAをチェックして、HAproxyがhaproxy -vvでビルドされていることを確認する必要があります。

これは設定例です。私自身は試していませんが、何ができるかがわかります。

# haproxy.cfg

global
    lua-load cors.lua

frontend foo
    ...
    http-request use-service lua.cors-response if METH_OPTIONS { req.hdr(Origin) -m found } { ... }

# cors.lua
core.register_service("cors-response", "http", function(applet)
    applet:set_status(200)
    applet:add_header("Content-Length", "0")
    applet:add_header("Access-Control-Allow-Origin", applet.headers["Origin"][0])
    applet:add_header("Access-Control-Allow-Credentials", "true")
    applet:add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Origin, User-Agent, If-Modified-Since, Cache-Control, Accept")
    applet:add_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
    applet:add_header("Access-Control-Max-Age", "1728000")
    applet:start_response()
end)
5
anine.io

anine.ioからのすばらしい回答 に基づいて、許可されたオリジンのリストを定義し、すべてのHTTPリクエストに不足しているAcccess-Control-Allow-Originヘッダーを追加できる次のソリューションを思いつきました。 anine.ioからの回答は、CORSプリフライトのみを示しており、通常のリクエストは考慮していませんでした。

haproxy.cfgcors.luaファイルをグローバルセクションにロードします(必要に応じてパスを調整します)

global
    lua-load /usr/local/etc/haproxy/cors.lua

フロントエンド定義にCORS構成を追加します

frontend http-in
    # CORS configuration
    # capture Origin HTTP header
    capture request header Origin len 128
    # add Access-Control-Allow-Origin HTTP header to response if Origin matches the list of allowed URLs
    http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if !METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }
    # if a preflight request is made, use CORS preflight backend
    http-request use-service lua.cors-response if METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }

cors.luaというファイルを作成し、上記で指定したパスの下に保存します。ファイルにはCORSプリフライトが含まれています。正当な理由がない場合は、メソッドまたはヘッダーを制限しないでください。haproxy.confのCORS構成で定義されたACLに、メソッドまたはヘッダーに関する制限を含める必要があります。 注:現在、ブラウザは*ヘッダーのワイルドカードAccess-Control-Allow-Methodsをサポートしていません。cors.luaファイルには次のコンテンツが含まれている必要があります

core.register_service("cors-response", "http", function(applet)
    applet:set_status(200)
    applet:add_header("Content-Length", "0")
    applet:add_header("Access-Control-Allow-Origin", applet.headers["Origin"][0])
    applet:add_header("Access-Control-Allow-Credentials", "true")
    applet:add_header("Access-Control-Allow-Headers", "*")
    applet:add_header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS")
    applet:add_header("Access-Control-Max-Age", "1728000")
    applet:start_response()
end)

cors-origins.lstというファイルを作成し、CORS構成で上記で指定したパスの下に保存します。ファイルには正規表現(または単純な文字列)が含まれている必要があります。クライアントがOriginヘッダーを送信すると、これらの正規表現に対して検証され、一致する場合にのみ、cors.luaからのCORSプリフライトが返されます(HTTP OPTIONSリクエストの場合)またはAccess-Control-Allow-OriginクライアントリクエストのOriginヘッダーの値がレスポンスに追加されます。 cors-origins.lstのコンテンツの例は次のとおりです。

example.com
localhost.*
.*\.mydomain\.com:[8080|8443]

http://test-cors.org/ で構成をテストします。 GETリクエストの場合、CORSプリフライトはありません。 GET以外のリクエストの場合、CORSプリフライトリクエストを最初にクライアントが実行して(HTTP OPTIONS呼び出しなど)、目的のメソッド、ヘッダー、承認が許可されているかどうかを確認する必要があります。

CORSの詳細については、 HTTPアクセス制御(CORS) を参照してください。

5

ここのリングに自分の答えを投げたい。動作するセットアップに到達するために、かなりの苦労を経験しました。ここでのこの質問に対する他の回答は非常に役に立ちましたが、完全に機能する構成は得られませんでした。

すべてのオリジンを許可したかったのです。ホワイトリストに登録されたオリジンが必要な場合は、有用な正規表現のトリックについて @ Florian Feldhausからの回答 を参照してください。ホワイトリストを使用する代わりに、ロケーションヘッダーをエコーバックします。

http-request set-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }

また、Access-Control-Allow-HeadersAccess-Control-Expose-Headersを明示的に設定する必要がありました。 これらのヘッダーでのワイルドカードのブラウザーサポートはまだ完全にはありません。

したがって、構成の内容は次のとおりです。

  1. このcors.luaスクリプト を使用してプリフライトリクエストを処理します
  2. http-response set-headerを使用して通常のリクエストを処理し、Access-Control-Allow- *ヘッダーを追加します
  3. cORSヘッダー(1 KBを超える)に合うようにtune.maxrewriteを調整します

1)と2)の手順は、ここの他の回答で説明されていますが、手順3)は、理解するのに長い時間がかかりました。完全な構成とそこにたどり着いた道のりを このブログ投稿 で文書化しました。投稿には、githubの要点へのリンクが含まれています。

3