New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Envoy does not adhere to HTTP/2 RFC 7540 #6767
Comments
|
@alyssawilk @PiotrSikora thoughts on this? I haven't read the relevant RFCs in detail to fully understand what is needed. |
|
Intersection of H2 specs and TLS handshake? I eagerly anticipate Piotr sorting this out :-P |
|
The gist is that browsers coalesce HTTP/2 connections pretty aggressively: when a browser opens connection to As long as all However, if One solution would be to send Another solution would be using HTTP/2 ORIGIN frame (RFC8336) to advertise allowed hostnames on a given listener/filter chain (but this requires a global list as well, and this extension is supported only by a few clients). |
|
Is is possible to reprioritize this issue? We have a use case where we have thousands of services behind hundreds of FQDN's that are served by a set of identical envoys, (all using a wildcard TLS cert). We exhibit this exact issue when HTTP/2 is enabled but not when enforcing usage of HTTP/1.1. |
|
Here's the CVE for this vulnerability https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11767 |
|
CC @envoyproxy/security-team |
I can think of 3 alternatives:
|
|
What is the plan for fixing https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11767 in envoy? |
|
AFAIK there is no plan currently. Someone needs to own this issue and drive a resolution if they are passionate about fixing it. |
|
I hacked up a 421 response when the virtual host lookup fails and this works for a simple case that I tried. If this is a reasonable approach I'd need a bit of help to polish it up and figure out how to write tests for it. https://gist.github.com/jpeach/e01f5f752eed5ffd09ea1f18634d1fc5 |
|
I think I managed to find a workaround: On Envoy instance I added an "envoy.lua" HTTP filter, that checks if the response code is a 404 (the same code, that is being generated for non-existent route) AND checks if the "x-envoy-upstream-service-time" header is NOT present. The Lua code: function envoy_on_response(response_handle)
if response_handle:headers():get(":status") == "404" and response_handle:headers():get("x-envoy-upstream-service-time") == nil then
response_handle:headers():replace(":status", "421")
end
endExample configuration on Envoy (fetched by LDS): "http_filters": [
{
"name": "envoy.lua",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua",
"inline_code": `
function envoy_on_response(response_handle)
if response_handle:headers():get(":status") == "404" and response_handle:headers():get("x-envoy-upstream-service-time") == nil then
response_handle:headers():replace(":status", "421")
end
end`
}
},
{
"name": "envoy.filters.http.router"
}
] |
Nice! That's a bit cleaner than my equivalent |
|
May be a dumb question - if we have a scenario like (from Piotr above) How can we distinguish from a browser coalescing the request from someone legitimately sending a request with Also, RFC 7540 is about HTTP/2. The above example can be done with HTTP 1 - do we expect a 421 still? |
|
I am exploring the relationship among SNI, SAN in cert and Host in Http. |
|
We now use this Lua Snippet: function envoy_on_request(request_handle)
local streamInfo = request_handle:streamInfo()
if streamInfo:requestedServerName() ~= "" then
if (string.sub(streamInfo:requestedServerName(), 0, 2) = "*." and not string.find(request_handle:headers():get(":authority"), string.sub(streamInfo:requestedServerName(), 1))) then
request_handle:respond({[":status"] == "421"}, "Misdirected Request")
end
if (string.sub(streamInfo:requestedServerName(), 0, 2) ~= "*." and streamInfo:requestedServerName() ~= request_handle:headers():get(":authority")) then
request_handle:respond({[":status"] = "421"}, "Misdirected Request")
end
end
endEDIT: Fixed for HTTP requests where requestedServerName is empty |
|
@maennchen Awesome! Any idea on how to implement it with Istio? |
Does it work in Safari for you? (we found out that Safari completely ignores 421 code and does not retry that request) |
|
@Dekim Sure: apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: misdirected-request
namespace: istio-system
spec:
workloadSelector:
labels:
app: istio-ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
subFilter:
name: envoy.filters.http.router
patch:
operation: INSERT_BEFORE
value:
name: envoy.lua
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
inlineCode: |
function envoy_on_request(request_handle)
local streamInfo = request_handle:streamInfo()
if streamInfo:requestedServerName() ~= "" then
if (string.sub(streamInfo:requestedServerName(), 0, 2) == "*." and not string.find(request_handle:headers():get(":authority"), string.sub(streamInfo:requestedServerName(), 1))) then
request_handle:respond({[":status"] = "421"}, "Misdirected Request")
end
if (string.sub(streamInfo:requestedServerName(), 0, 2) ~= "*." and streamInfo:requestedServerName() ~= request_handle:headers():get(":authority")) then
request_handle:respond({[":status"] = "421"}, "Misdirected Request")
end
end
end@feature-id I haven't tested Safari. EDIT: Fixed for HTTP requests where requestedServerName is empty |
|
@maennchen Awesome! Let's see if can somebody test it on Safari. That would be awesome! |
|
Last time I tested Safari (2020) it didn't support retrying on 421 responses. There have been new releases since then, but the bug I filed wasn't resolved so I doubt it is fixed (or ever will be now). IMHO, the best approach is to avoid wildcard certificates and issue a new dedicated certificate to every domain. |
Unfortunately, this isn't an option if you have over 9000 domains. :) |
|
Currently, Envoy has two arguable behaviors
Should we provide an option in HCM to return 421 on the above conflict?
If the client's request is {SNI="*.foo.com", :authority = "bar.foo.com"}, should we provide an another option to reject the http request because "bar.foo.com" has a better SNI match filter chain? |
|
I'm not sure about the second option, but the first one would let us in Contour remove a Lua workaround we have that does the same thing. You can see the whole thing at https://github.com/projectcontour/contour/blob/40d5c259697ca92693071bc6977a1c8da08555b0/internal/envoy/v3/listener.go#L565-L603 |
|
Thank you for sharing.
|
They fixed it in Monterey!? That's great news |
|
@maennchen Your lua code is incorrect. lua's indices start at 1 instead of 0. So the code should be as follows: function envoy_on_request(request_handle)
local streamInfo = request_handle:streamInfo()
if streamInfo:requestedServerName() ~= "" then
if (string.sub(streamInfo:requestedServerName(), 1, 2) = "*." and not string.find(request_handle:headers():get(":authority"), string.sub(streamInfo:requestedServerName(), 2))) then
request_handle:respond({[":status"] == "421"}, "Misdirected Request")
end
if (string.sub(streamInfo:requestedServerName(), 1, 2) ~= "*." and streamInfo:requestedServerName() ~= request_handle:headers():get(":authority")) then
request_handle:respond({[":status"] = "421"}, "Misdirected Request")
end
end
end |
Title: Envoy does not adhere to HTTP/2 RFC 7540
Description:
[optional Relevant Links:]
The text was updated successfully, but these errors were encountered: