Skip to content
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

http: explicitly blocking CONNECT for HTTP/1.1 #9637

Merged
merged 2 commits into from
Jan 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Version history
* decompressor: remove decompressor hard assert failure and replace with an error flag.
* ext_authz: added :ref:`configurable ability<envoy_api_field_config.filter.http.ext_authz.v2.ExtAuthz.include_peer_certificate>` to send the :ref:`certificate<envoy_api_field_service.auth.v2.AttributeContext.Peer.certificate>` to the `ext_authz` service.
* health check: gRPC health checker sets the gRPC deadline to the configured timeout duration.
* http: added strict validation that CONNECT is refused as it is not yet implemented. This can be reversed temporarily by setting the runtime feature `envoy.reloadable_features.strict_method_validation` to false.
* http: added support for http1 trailers. To enable use :ref:`enable_trailers <envoy_api_field_core.Http1ProtocolOptions.enable_trailers>`.
* http: added the ability to sanitize headers nominated by the Connection header. This new behavior is guarded by envoy.reloadable_features.connection_header_sanitization which defaults to true.
* http: blocks unsupported transfer-encodings. Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.reject_unsupported_transfer_encodings` to false.
Expand Down
6 changes: 6 additions & 0 deletions source/common/http/header_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Http {

struct SharedResponseCodeDetailsValues {
const absl::string_view InvalidAuthority = "http.invalid_authority";
const absl::string_view ConnectUnsupported = "http.connect_not_supported";
};

using SharedResponseCodeDetails = ConstSingleton<SharedResponseCodeDetailsValues>;
Expand Down Expand Up @@ -178,6 +179,11 @@ HeaderUtility::requestHeadersValid(const HeaderMap& headers) {
headers.Host() && !HeaderUtility::authorityIsValid(headers.Host()->value().getStringView())) {
return SharedResponseCodeDetails::get().InvalidAuthority;
}
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.strict_method_validation") &&
headers.Method() &&
Http::Headers::get().MethodValues.Connect == headers.Method()->value().getStringView()) {
return SharedResponseCodeDetails::get().ConnectUnsupported;
}
return absl::nullopt;
}

Expand Down
2 changes: 1 addition & 1 deletion source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ constexpr const char* runtime_features[] = {
"envoy.reloadable_features.buffer_filter_populate_content_length",
"envoy.reloadable_features.outlier_detection_support_for_grpc_status",
"envoy.reloadable_features.connection_header_sanitization",
"envoy.reloadable_features.strict_header_validation",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to delete this? Don't we still need this since it exists in the code? Or should we clean it up in the code also?

Copy link
Contributor Author

@alyssawilk alyssawilk Jan 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Also removing a spurious "envoy.reloadable_features.strict_header_validation" as it was enabled twice." :-P

Github truncated but the file was
"envoy.reloadable_features.test_feature_true",
"envoy.reloadable_features.strict_header_validation",
"envoy.reloadable_features.buffer_filter_populate_content_length",
"envoy.reloadable_features.outlier_detection_support_for_grpc_status",
"envoy.reloadable_features.connection_header_sanitization",
"envoy.reloadable_features.strict_header_validation",

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry I should read more. :)

"envoy.reloadable_features.strict_authority_validation",
"envoy.reloadable_features.reject_unsupported_transfer_encodings",
"envoy.reloadable_features.strict_method_validation",
};

// This is a section for officially sanctioned runtime features which are too
Expand Down
2 changes: 1 addition & 1 deletion test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1568,7 +1568,7 @@ TEST_F(HttpConnectionManagerImplTest, NoPath) {
NiceMock<MockStreamEncoder> encoder;
EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> void {
decoder = &conn_manager_->newStream(encoder);
HeaderMapPtr headers{new TestHeaderMapImpl{{":authority", "host"}, {":method", "CONNECT"}}};
HeaderMapPtr headers{new TestHeaderMapImpl{{":authority", "host"}, {":method", "NOT_CONNECT"}}};
decoder->decodeHeaders(std::move(headers), true);
data.drain(4);
}));
Expand Down
2 changes: 1 addition & 1 deletion test/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ envoy_cc_test(
],
# As this test has many H1/H2/v4/v6 tests it takes a while to run.
# Shard it enough to bring the run time in line with other integration tests.
shard_count = 3,
shard_count = 5,
deps = [
":http_protocol_integration_lib",
"//source/common/http:header_map_lib",
Expand Down
36 changes: 36 additions & 0 deletions test/integration/protocol_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,42 @@ TEST_P(DownstreamProtocolIntegrationTest, InvalidAuthority) {
}
}

TEST_P(DownstreamProtocolIntegrationTest, ConnectIsBlocked) {
initialize();
codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(
Http::TestHeaderMapImpl{{":method", "CONNECT"}, {":path", "/"}, {":authority", "host"}});

if (downstreamProtocol() == Http::CodecClient::Type::HTTP1) {
response->waitForEndStream();
EXPECT_EQ("400", response->headers().Status()->value().getStringView());
EXPECT_TRUE(response->complete());
} else {
response->waitForReset();
codec_client_->waitForDisconnect();
}
}

// Make sure that with stream_error_on_invalid_http_messaging true, CONNECT
// results in stream teardown not connection teardown.
TEST_P(DownstreamProtocolIntegrationTest, ConnectStreamRejection) {
if (downstreamProtocol() == Http::CodecClient::Type::HTTP1) {
return;
}
config_helper_.addConfigModifier([](envoy::extensions::filters::network::http_connection_manager::
v3alpha::HttpConnectionManager& hcm) -> void {
hcm.mutable_http2_protocol_options()->set_stream_error_on_invalid_http_messaging(true);
});

initialize();
codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(
Http::TestHeaderMapImpl{{":method", "CONNECT"}, {":path", "/"}, {":authority", "host"}});

response->waitForReset();
EXPECT_FALSE(codec_client_->disconnected());
}

// For tests which focus on downstream-to-Envoy behavior, and don't need to be
// run with both HTTP/1 and HTTP/2 upstreams.
INSTANTIATE_TEST_SUITE_P(Protocols, DownstreamProtocolIntegrationTest,
Expand Down