Skip to content

Commit

Permalink
http: explicitly blocking CONNECT for HTTP/1.1 (#9637)
Browse files Browse the repository at this point in the history
Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
  • Loading branch information
alyssawilk authored and mattklein123 committed Jan 9, 2020
1 parent 8057eed commit 014f207
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 3 deletions.
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",
"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

0 comments on commit 014f207

Please sign in to comment.