記contour偶発404問題調査
4668 ワード
最近、デルの本番環境はcontourから1.4.0にアップグレードされ、ユーザーは偶発的な404の問題を反映しています.
簡単なテストの結果、ブラウザを介してhttpを有効にしたWebサイトにアクセスするだけで404が偶発的に発生します.
私たちのプロジェクトhttpproxyは以下の通りです.
envoyのaccesslogを表示すると、次のように表示されます.
SNI関連と考えられる.
このとき万能のgithubで検索してみます.やはりもう他の人が穴を踏んだ.
I've found a similar behavior after upgrading as well. It appears to be related to http2 connection coalescing. The SNI (envoy
#1493 . So far I've only seen it impact users on Mozilla/Firefox which fits since it appears to have the most aggressive connection coalescing from what I've read. Pretty certain this is due to
#2381 , but given the
Envoy CVE it probably shouldn't be reverted until Envoy comes up with a fix.
I was able to work around it by issuing separate certs for each virtualhost and updating the
本質的にenvoyの1つのバグ(Envoy does not adhere to HTTP/2 RFC 7540)によるものである.
基本的な原理は、ブラウザがHTTP/2接続を非常に多重化することである.ブラウザが
すべての
しかし、
残念ながらenvoyは修復されていません.しかし、コミュニティは2つのソリューションを提供しています.の1つの解決策は、421が誤って指向化された要求応答を、所与のリスナー/フィルタチェーン上で構成されていないホスト名要求に送信するか(ただし、 の別の解決策は、HTTP/2 ORIGINフレームワーク(RFC 8336)を使用して、所与のリスナー/フィルタチェーン上で許可されたホスト名をブロードキャストすることである(ただし、グローバルリストも必要であり、少数のクライアントのみがこの拡張子をサポートする).
第1の案については、大体3つの考え方があります. RBACフィルタDENYにHTTP応答コードを指定できたらどうしますか?その後、HCMが構成された管理サーバは、そのHCM上で許可されたサーバ名にRBACポリシーを追加し、DENY上で421を生成することができる. 管理サーバは、LuaフィルタにおいてSNIサーバ名チェックをプログラムすることができ、一致しなければ421を生成する. は、許容可能なSNIサーバ名を使用して構成できる専用フィルタを追加する.
contourはenvoyをデータ層として選択し、この問題を避けることができず、チームは最終的に第2の考え方を選んで解決した.
luaを用いてTLSエラーリクエストのフィルタを実現した.
具体的なコードは以下の通りです.
TLSルーティングは、一意の仮想ホスト名に専用です.ただし、ワイルドカード証明書を使用すると、元のホスト名が一致しなくても、ブラウザはサーバ接続を積極的に統合して再利用します.これは、各TLS仮想ホストが1つのホストにルーティングされているだけであるため、404のエラー応答が発生する.
仮想ホストのFQDNに一致しない場合、421を生成することによって、このような動作がユーザに漏れることを回避する.この場合、ブラウザを使用してリクエストが処理されていないことを理解し、新しい接続に再送信する必要があります.
もちろんこれは一時的な案にすぎず、本当の解決にはenvoyの徹底的な修復が必要だ.
簡単なテストの結果、ブラウザを介してhttpを有効にしたWebサイトにアクセスするだけで404が偶発的に発生します.
私たちのプロジェクトhttpproxyは以下の通りです.
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: hawkeye-grafana
namespace: sgt
spec:
virtualhost:
fqdn: hawkeye.xx.me
tls:
secretName: https-xx-me-new
routes:
- conditions:
- prefix: /
services:
- name: hawkeye-grafana
port: 80
envoyのaccesslogを表示すると、次のように表示されます.
[2020-04-26T22:28:27.120Z] "GET / HTTP/2" 404 NR 0 0 0 - "10.107.8.251" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36" "45fc064a-3124-47f2-b49e-cffb6de031e9" "hawkeye.xx.me" "-"
SNI関連と考えられる.
このとき万能のgithubで検索してみます.やはりもう他の人が穴を踏んだ.
I've found a similar behavior after upgrading as well. It appears to be related to http2 connection coalescing. The SNI (envoy
authority
) will not match the requested host name and Envoy will 404 the connection similar to how it behaves in #1493 . So far I've only seen it impact users on Mozilla/Firefox which fits since it appears to have the most aggressive connection coalescing from what I've read. Pretty certain this is due to
#2381 , but given the
Envoy CVE it probably shouldn't be reverted until Envoy comes up with a fix.
I was able to work around it by issuing separate certs for each virtualhost and updating the
httpproxy
to use the cert for that virtualhost instead of using a wildcard that covered them all. 本質的にenvoyの1つのバグ(Envoy does not adhere to HTTP/2 RFC 7540)によるものである.
基本的な原理は、ブラウザがHTTP/2接続を非常に多重化することである.ブラウザが
www.example.com
との接続を開き、TLS握手中に*.example.com
の証明書を表示すると、ホスト名が同じIP(一部のブラウザーはそれに関心を持たない)と解析される限り、すべての要求が*.example.com
にルーティングされる.すべての
*.example.com
ホスト名が同じリスナー/フィルタチェーンによってサービスされる限り、ルーティングは各接続ではなく各要求に基づいて行われるので、Envoyでは問題ありません.しかし、
www.example.com
(*.example.com
証明書付き)が1つのリスナー/フィルタチェーンによってサービスを提供し、app.example.com
が別のリスナー/フィルタチェーンによってサービスを提供する場合、接続は接続のライフサイクル全体にわたって1つのリスナー/フィルタチェーンにロックされているため、www.example.com
との接続が最初に確立された場合、app.example.com
に対する要求はwww.example.com
の構成を使用して同じ接続に統合されるため、問題がある.その後、エラーのバックエンドに転送します.残念ながらenvoyは修復されていません.しかし、コミュニティは2つのソリューションを提供しています.
*.example.com
が構成されている場合は機能しない)、または421が誤って指向化された要求応答を、他のリスナー/フィルタチェーン上で構成するために要求されたホスト名に送信するか(ただし、これは、すべての構成されたホスト名のグローバルリストが必要である).第1の案については、大体3つの考え方があります.
contourはenvoyをデータ層として選択し、この問題を避けることができず、チームは最終的に第2の考え方を選んで解決した.
luaを用いてTLSエラーリクエストのフィルタを実現した.
具体的なコードは以下の通りです.
func FilterMisdirectedRequests(fqdn string) *http.HttpFilter {
code := `
function envoy_on_request(request_handle)
local headers = request_handle:headers()
local host = headers:get(":authority")
if host ~= "%s" then
request_handle:respond({
[":status"] = "421",
},
""
)
end
end
`
return &http.HttpFilter{
Name: "envoy.filters.http.lua",
ConfigType: &http.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&lua.Lua{
InlineCode: fmt.Sprintf(code, fqdn),
}),
},
}
}
TLSルーティングは、一意の仮想ホスト名に専用です.ただし、ワイルドカード証明書を使用すると、元のホスト名が一致しなくても、ブラウザはサーバ接続を積極的に統合して再利用します.これは、各TLS仮想ホストが1つのホストにルーティングされているだけであるため、404のエラー応答が発生する.
仮想ホストのFQDNに一致しない場合、421を生成することによって、このような動作がユーザに漏れることを回避する.この場合、ブラウザを使用してリクエストが処理されていないことを理解し、新しい接続に再送信する必要があります.
もちろんこれは一時的な案にすぎず、本当の解決にはenvoyの徹底的な修復が必要だ.