Skip to content

Commit

Permalink
http2: configure HTTP/2 flood mitigation through runtime. (#32)
Browse files Browse the repository at this point in the history
Signed-off-by: Yan Avlasov <[email protected]>
  • Loading branch information
yanavlasov authored and PiotrSikora committed Aug 13, 2019
1 parent 79cbdca commit d3d5dcd
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 22 deletions.
6 changes: 6 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@ Version history
========================
* http: added mitigation of client initiated atacks that result in flooding of the downstream HTTP/2 connections.
* http: added :ref:`inbound_empty_frames_flood <config_http_conn_man_stats_per_codec>` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the limit on consecutive inbound frames with an empty payload and no end stream flag. The limit is configured by setting the :ref:`max_consecutive_inbound_frames_with_empty_payload config setting <envoy_api_field_core.Http2ProtocolOptions.max_consecutive_inbound_frames_with_empty_payload>`.
Runtime feature `envoy.reloadable_features.http2_protocol_options.max_consecutive_inbound_frames_with_empty_payload` overrides :ref:`max_consecutive_inbound_frames_with_empty_payload setting <envoy_api_field_core.Http2ProtocolOptions.max_consecutive_inbound_frames_with_empty_payload>`. Large override value (i.e. 2147483647) effectively disables mitigation of inbound frames with empty payload.
* http: added :ref:`inbound_priority_frames_flood <config_http_conn_man_stats_per_codec>` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the limit on inbound PRIORITY frames. The limit is configured by setting the :ref:`max_inbound_priority_frames_per_stream config setting <envoy_api_field_core.Http2ProtocolOptions.max_inbound_priority_frames_per_stream>`.
Runtime feature `envoy.reloadable_features.http2_protocol_options.max_inbound_priority_frames_per_stream` overrides :ref:`max_inbound_priority_frames_per_stream setting <envoy_api_field_core.Http2ProtocolOptions.max_inbound_priority_frames_per_stream>`. Large override value effectively disables flood mitigation of inbound PRIORITY frames.
* http: added :ref:`inbound_window_update_frames_flood <config_http_conn_man_stats_per_codec>` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the limit on inbound WINDOW_UPDATE frames. The limit is configured by setting the :ref:`max_inbound_window_update_frames_per_data_frame_sent config setting <envoy_api_field_core.Http2ProtocolOptions.max_inbound_window_update_frames_per_data_frame_sent>`.
Runtime feature `envoy.reloadable_features.http2_protocol_options.max_inbound_window_update_frames_per_data_frame_sent` overrides :ref:`max_inbound_window_update_frames_per_data_frame_sent setting <envoy_api_field_core.Http2ProtocolOptions.max_inbound_window_update_frames_per_data_frame_sent>`. Large override value effectively disables flood mitigation of inbound WINDOW_UPDATE frames.
* http: added :ref:`outbound_flood <config_http_conn_man_stats_per_codec>` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the outbound queue limit. The limit is configured by setting the :ref:`max_outbound_frames config setting <envoy_api_field_core.Http2ProtocolOptions.max_outbound_frames>`
Runtime feature `envoy.reloadable_features.http2_protocol_options.max_outbound_frames` overrides :ref:`max_outbound_frames config setting <envoy_api_field_core.Http2ProtocolOptions.max_outbound_frames>`. Large override value effectively disables flood mitigation of outbound frames of all types.
* http: added :ref:`outbound_control_flood <config_http_conn_man_stats_per_codec>` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the outbound queue limit for PING, SETTINGS and RST_STREAM frames. The limit is configured by setting the :ref:`max_outbound_control_frames config setting <envoy_api_field_core.Http2ProtocolOptions.max_outbound_control_frames>`.
Runtime feature `envoy.reloadable_features.http2_protocol_options.max_outbound_control_frames` overrides :ref:`max_outbound_control_frames config setting <envoy_api_field_core.Http2ProtocolOptions.max_outbound_control_frames>`. Large override value effectively disables flood mitigation of outbound frames of types PING, SETTINGS and RST_STREAM.
* http: enabled strict validation of HTTP/2 messaging. Previous behavior can be restored using :ref:`stream_error_on_invalid_http_messaging config setting <envoy_api_field_core.Http2ProtocolOptions.stream_error_on_invalid_http_messaging>`.
Runtime feature `envoy.reloadable_features.http2_protocol_options.stream_error_on_invalid_http_messaging` overrides :ref:`stream_error_on_invalid_http_messaging config setting <envoy_api_field_core.Http2ProtocolOptions.stream_error_on_invalid_http_messaging>`.

1.11.0 (July 11, 2019)
======================
Expand Down
1 change: 1 addition & 0 deletions source/common/http/http2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ envoy_cc_library(
"//source/common/http:header_map_lib",
"//source/common/http:headers_lib",
"//source/common/http:utility_lib",
"//source/common/runtime:runtime_lib",
],
)

Expand Down
53 changes: 53 additions & 0 deletions source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,59 @@ void ConnectionImpl::StreamImpl::onMetadataDecoded(MetadataMapPtr&& metadata_map
decoder_->decodeMetadata(std::move(metadata_map_ptr));
}

namespace {

const char InvalidHttpMessagingOverrideKey[] =
"envoy.reloadable_features.http2_protocol_options.stream_error_on_invalid_http_messaging";
const char MaxOutboundFramesOverrideKey[] =
"envoy.reloadable_features.http2_protocol_options.max_outbound_frames";
const char MaxOutboundControlFramesOverrideKey[] =
"envoy.reloadable_features.http2_protocol_options.max_outbound_control_frames";
const char MaxConsecutiveInboundFramesWithEmptyPayloadOverrideKey[] =
"envoy.reloadable_features.http2_protocol_options."
"max_consecutive_inbound_frames_with_empty_payload";
const char MaxInboundPriorityFramesPerStreamOverrideKey[] =
"envoy.reloadable_features.http2_protocol_options.max_inbound_priority_frames_per_stream";
const char MaxInboundWindowUpdateFramesPerDataFrameSentOverrideKey[] =
"envoy.reloadable_features.http2_protocol_options."
"max_inbound_window_update_frames_per_data_frame_sent";

bool checkRuntimeOverride(bool config_value, const char* override_key) {
return Runtime::runtimeFeatureEnabled(override_key) ? true : config_value;
}

} // namespace

ConnectionImpl::ConnectionImpl(Network::Connection& connection, Stats::Scope& stats,
const Http2Settings& http2_settings,
const uint32_t max_request_headers_kb)
: stats_{ALL_HTTP2_CODEC_STATS(POOL_COUNTER_PREFIX(stats, "http2."))}, connection_(connection),
max_request_headers_kb_(max_request_headers_kb),
per_stream_buffer_limit_(http2_settings.initial_stream_window_size_),
stream_error_on_invalid_http_messaging_(checkRuntimeOverride(
http2_settings.stream_error_on_invalid_http_messaging_, InvalidHttpMessagingOverrideKey)),
flood_detected_(false),
max_outbound_frames_(
Runtime::getInteger(MaxOutboundFramesOverrideKey, http2_settings.max_outbound_frames_)),
frame_buffer_releasor_([this](const Buffer::OwnedBufferFragmentImpl* fragment) {
releaseOutboundFrame(fragment);
}),
max_outbound_control_frames_(Runtime::getInteger(
MaxOutboundControlFramesOverrideKey, http2_settings.max_outbound_control_frames_)),
control_frame_buffer_releasor_([this](const Buffer::OwnedBufferFragmentImpl* fragment) {
releaseOutboundControlFrame(fragment);
}),
max_consecutive_inbound_frames_with_empty_payload_(
Runtime::getInteger(MaxConsecutiveInboundFramesWithEmptyPayloadOverrideKey,
http2_settings.max_consecutive_inbound_frames_with_empty_payload_)),
max_inbound_priority_frames_per_stream_(
Runtime::getInteger(MaxInboundPriorityFramesPerStreamOverrideKey,
http2_settings.max_inbound_priority_frames_per_stream_)),
max_inbound_window_update_frames_per_data_frame_sent_(Runtime::getInteger(
MaxInboundWindowUpdateFramesPerDataFrameSentOverrideKey,
http2_settings.max_inbound_window_update_frames_per_data_frame_sent_)),
dispatching_(false), raised_goaway_(false), pending_deferred_reset_(false) {}

ConnectionImpl::~ConnectionImpl() { nghttp2_session_del(session_); }

void ConnectionImpl::dispatch(Buffer::Instance& data) {
Expand Down
23 changes: 2 additions & 21 deletions source/common/http/http2/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "common/http/http2/metadata_decoder.h"
#include "common/http/http2/metadata_encoder.h"
#include "common/http/utility.h"
#include "common/runtime/runtime_impl.h"

#include "absl/types/optional.h"
#include "nghttp2/nghttp2.h"
Expand Down Expand Up @@ -78,27 +79,7 @@ class Utility {
class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Logger::Id::http2> {
public:
ConnectionImpl(Network::Connection& connection, Stats::Scope& stats,
const Http2Settings& http2_settings, const uint32_t max_request_headers_kb)
: stats_{ALL_HTTP2_CODEC_STATS(POOL_COUNTER_PREFIX(stats, "http2."))},
connection_(connection), max_request_headers_kb_(max_request_headers_kb),
per_stream_buffer_limit_(http2_settings.initial_stream_window_size_),
stream_error_on_invalid_http_messaging_(
http2_settings.stream_error_on_invalid_http_messaging_),
flood_detected_(false), max_outbound_frames_(http2_settings.max_outbound_frames_),
frame_buffer_releasor_([this](const Buffer::OwnedBufferFragmentImpl* fragment) {
releaseOutboundFrame(fragment);
}),
max_outbound_control_frames_(http2_settings.max_outbound_control_frames_),
control_frame_buffer_releasor_([this](const Buffer::OwnedBufferFragmentImpl* fragment) {
releaseOutboundControlFrame(fragment);
}),
max_consecutive_inbound_frames_with_empty_payload_(
http2_settings.max_consecutive_inbound_frames_with_empty_payload_),
max_inbound_priority_frames_per_stream_(
http2_settings.max_inbound_priority_frames_per_stream_),
max_inbound_window_update_frames_per_data_frame_sent_(
http2_settings.max_inbound_window_update_frames_per_data_frame_sent_),
dispatching_(false), raised_goaway_(false), pending_deferred_reset_(false) {}
const Http2Settings& http2_settings, const uint32_t max_request_headers_kb);

~ConnectionImpl() override;

Expand Down
11 changes: 11 additions & 0 deletions source/common/runtime/runtime_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ bool runtimeFeatureEnabled(absl::string_view feature) {
return RuntimeFeaturesDefaults::get().enabledByDefault(feature);
}

uint64_t getInteger(absl::string_view feature, uint64_t default_value) {
ASSERT(absl::StartsWith(feature, "envoy.reloadable_features"));
if (Runtime::LoaderSingleton::getExisting()) {
return Runtime::LoaderSingleton::getExisting()->threadsafeSnapshot()->getInteger(
std::string(feature), default_value);
}
ENVOY_LOG_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::runtime), warn,
"Unable to use runtime singleton for feature {}", feature);
return default_value;
}

const size_t RandomGeneratorImpl::UUID_LENGTH = 36;

uint64_t RandomGeneratorImpl::random() {
Expand Down
1 change: 1 addition & 0 deletions source/common/runtime/runtime_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace Envoy {
namespace Runtime {

bool runtimeFeatureEnabled(absl::string_view feature);
uint64_t getInteger(absl::string_view feature, uint64_t default_value);

using RuntimeSingleton = ThreadSafeSingleton<Loader>;

Expand Down
5 changes: 5 additions & 0 deletions test/common/http/http2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ envoy_cc_test(
"//source/common/http/http2:codec_lib",
"//source/common/stats:stats_lib",
"//test/common/http:common_lib",
"//test/common/http/http2:http2_frame",
"//test/mocks/http:http_mocks",
"//test/mocks/init:init_mocks",
"//test/mocks/local_info:local_info_mocks",
"//test/mocks/network:network_mocks",
"//test/mocks/protobuf:protobuf_mocks",
"//test/mocks/thread_local:thread_local_mocks",
"//test/mocks/upstream:upstream_mocks",
"//test/test_common:utility_lib",
],
Expand Down
Loading

0 comments on commit d3d5dcd

Please sign in to comment.