-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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: add Kill Request HTTP filter #14170
Changes from 2 commits
bf6d7e9
5799de7
2fedcf8
34a1853
a93c2f1
5865258
7ca49c2
d3e0546
410ec40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. | ||
|
||
load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") | ||
|
||
licenses(["notice"]) # Apache 2 | ||
|
||
api_proto_package( | ||
deps = [ | ||
"//envoy/type/v3:pkg", | ||
"@com_github_cncf_udpa//udpa/annotations:pkg", | ||
], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
syntax = "proto3"; | ||
|
||
package envoy.extensions.filters.http.kill_request.v3; | ||
|
||
import "envoy/type/v3/percent.proto"; | ||
|
||
import "udpa/annotations/status.proto"; | ||
import "udpa/annotations/versioning.proto"; | ||
|
||
option java_package = "io.envoyproxy.envoy.extensions.filters.http.kill_request.v3"; | ||
option java_outer_classname = "KillRequestProto"; | ||
option java_multiple_files = true; | ||
option (udpa.annotations.file_status).package_version_status = ACTIVE; | ||
|
||
// [#protodoc-title: Kill Request] | ||
// Kill Request :ref:`configuration overview <config_http_filters_kill_request>`. | ||
// [#extension: envoy.filters.http.kill_request] | ||
|
||
// Configuration for KillRequest filter. | ||
message KillRequest { | ||
// The probability that a Kill request will be triggered. | ||
type.v3.FractionalPercent probability = 1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
.. _config_http_filters_kill_request: | ||
|
||
Kill Request | ||
=============== | ||
|
||
The KillRequest filter can be used to crash Envoy when receiving a Kill request. | ||
By default, KillRequest filter is not built into Envoy binary since it is removed from *envoy_all_extensions()* in *all_extensions.bzl*. If you want to use this extenstion, please add it in *envoy_all_extensions()*. | ||
|
||
Configuration | ||
------------- | ||
|
||
* This filter should be configured with the name *envoy.filters.http.kill_request*. | ||
|
||
.. _config_http_filters_kill_request_http_header: | ||
|
||
Enable Kill Request via HTTP header | ||
-------------------------------------------- | ||
|
||
The KillRequest filter requires the following header in the request: | ||
|
||
x-envoy-kill-request | ||
whether the request is a Kill request. | ||
The header value must be one of (case-insensitive) ["true", "t", "yes", "y", "1"] | ||
in order for the request to be a Kill request. | ||
|
||
.. note:: | ||
|
||
If the headers appear multiple times only the first value is used. | ||
|
||
The following is an example configuration: | ||
|
||
.. code-block:: yaml | ||
|
||
name: envoy.filters.http.kill_request | ||
typed_config: | ||
"@type": type.googleapis.com/envoy.extensions.filters.http.kill_request.v3.KillRequest | ||
probability: | ||
numerator: 100 | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,15 @@ _required_extensions = { | |
"envoy.transport_sockets.tls": "//source/extensions/transport_sockets/tls:config", | ||
} | ||
|
||
_kill_request_http_filter = "envoy.filters.http.kill_request" | ||
|
||
# Return all extensions to be compiled into Envoy. | ||
def envoy_all_extensions(denylist = []): | ||
all_extensions = dicts.add(_required_extensions, EXTENSIONS) | ||
|
||
# !!! kill_request filter should not be built into Envoy. !!! | ||
denylist = denylist + [_kill_request_http_filter] | ||
|
||
# These extensions can be removed on a site specific basis. | ||
return [v for k, v in all_extensions.items() if not k in denylist] | ||
|
||
|
@@ -37,7 +42,7 @@ _http_filter_prefix = "envoy.filters.http" | |
def envoy_all_http_filters(): | ||
all_extensions = dicts.add(_required_extensions, EXTENSIONS) | ||
|
||
return [v for k, v in all_extensions.items() if k.startswith(_http_filter_prefix)] | ||
return [v for k, v in all_extensions.items() if k.startswith(_http_filter_prefix) and k != _kill_request_http_filter] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you refactor so we only have to add the filter once to the denylist? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be wrong, but I don't see a way to refactor to make it simpler since denylist is a function parameter in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just create a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
# All network-layer filters are extensions with names that have the following prefix. | ||
_network_filter_prefix = "envoy.filters.network" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,7 @@ EXTENSIONS = { | |
"envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", | ||
"envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", | ||
"envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", | ||
"envoy.filters.http.kill_request": "//source/extensions/filters/http/kill_request:kill_request_config", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, you include here but then exclude later.. how does a user actually include this in a build? Can you comment on this to make it clearer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks Harvey. Added comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean Can we do this a bit differently and add a new set of definitions here, DISABLED_BY_DEFAULT_EXTENSIONS, that looks like the current structure but only has the kill filter. We can feed this definition into both all_extensions.bzl and also into the docs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks Harvey! Added the filter to DISABLED_BY_DEFAULT_EXTENSIONS. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is much better. What I was hinting at ^^ is that we should have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah :) thank you Harvey for the explanation! Removed the filter from |
||
"envoy.filters.http.local_ratelimit": "//source/extensions/filters/http/local_ratelimit:config", | ||
"envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", | ||
"envoy.filters.http.oauth2": "//source/extensions/filters/http/oauth2:config", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
load( | ||
"//bazel:envoy_build_system.bzl", | ||
"envoy_cc_extension", | ||
"envoy_cc_library", | ||
"envoy_extension_package", | ||
) | ||
|
||
licenses(["notice"]) # Apache 2 | ||
|
||
envoy_extension_package() | ||
|
||
envoy_cc_library( | ||
name = "kill_request_filter_lib", | ||
srcs = ["kill_request_filter.cc"], | ||
hdrs = ["kill_request_filter.h"], | ||
deps = [ | ||
"//include/envoy/common:random_generator_interface", | ||
"//include/envoy/http:filter_interface", | ||
"//include/envoy/http:header_map_interface", | ||
"//source/common/http:header_map_lib", | ||
"//source/common/http:header_utility_lib", | ||
"//source/common/http:headers_lib", | ||
"//source/common/protobuf:utility_lib", | ||
"@envoy_api//envoy/extensions/filters/http/kill_request/v3:pkg_cc_proto", | ||
], | ||
) | ||
|
||
envoy_cc_extension( | ||
name = "kill_request_config", | ||
srcs = ["kill_request_config.cc"], | ||
hdrs = ["kill_request_config.h"], | ||
security_posture = "robust_to_untrusted_downstream", | ||
deps = [ | ||
"//include/envoy/registry", | ||
"//source/extensions/filters/http:well_known_names", | ||
"//source/extensions/filters/http/common:factory_base_lib", | ||
"//source/extensions/filters/http/kill_request:kill_request_filter_lib", | ||
"@envoy_api//envoy/extensions/filters/http/kill_request/v3:pkg_cc_proto", | ||
], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#include "extensions/filters/http/kill_request/kill_request_config.h" | ||
|
||
#include "envoy/extensions/filters/http/kill_request/v3/kill_request.pb.h" | ||
#include "envoy/extensions/filters/http/kill_request/v3/kill_request.pb.validate.h" | ||
#include "envoy/registry/registry.h" | ||
|
||
#include "extensions/filters/http/kill_request/kill_request_filter.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace HttpFilters { | ||
namespace KillRequest { | ||
|
||
Http::FilterFactoryCb KillRequestFilterFactory::createFilterFactoryFromProtoTyped( | ||
const envoy::extensions::filters::http::kill_request::v3::KillRequest& proto_config, | ||
const std::string&, Server::Configuration::FactoryContext& context) { | ||
return [proto_config, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { | ||
callbacks.addStreamFilter( | ||
std::make_shared<KillRequestFilter>(proto_config, context.api().randomGenerator())); | ||
}; | ||
} | ||
|
||
/** | ||
* Static registration for the KillRequest filter. @see RegisterFactory. | ||
*/ | ||
REGISTER_FACTORY(KillRequestFilterFactory, Server::Configuration::NamedHttpFilterConfigFactory); | ||
|
||
} // namespace KillRequest | ||
} // namespace HttpFilters | ||
} // namespace Extensions | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#pragma once | ||
|
||
#include "envoy/extensions/filters/http/kill_request/v3/kill_request.pb.h" | ||
#include "envoy/extensions/filters/http/kill_request/v3/kill_request.pb.validate.h" | ||
|
||
#include "extensions/filters/http/common/factory_base.h" | ||
#include "extensions/filters/http/well_known_names.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace HttpFilters { | ||
namespace KillRequest { | ||
|
||
/** | ||
* Config registration for KillRequestFilter. @see NamedHttpFilterConfigFactory. | ||
*/ | ||
class KillRequestFilterFactory | ||
: public Common::FactoryBase<envoy::extensions::filters::http::kill_request::v3::KillRequest> { | ||
public: | ||
KillRequestFilterFactory() : FactoryBase(HttpFilterNames::get().KillRequest) {} | ||
|
||
private: | ||
Http::FilterFactoryCb createFilterFactoryFromProtoTyped( | ||
const envoy::extensions::filters::http::kill_request::v3::KillRequest& proto_config, | ||
const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; | ||
}; | ||
|
||
} // namespace KillRequest | ||
} // namespace HttpFilters | ||
} // namespace Extensions | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#include "extensions/filters/http/kill_request/kill_request_filter.h" | ||
|
||
#include <csignal> | ||
|
||
#include "common/protobuf/utility.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace HttpFilters { | ||
namespace KillRequest { | ||
|
||
bool KillRequestFilter::isKillRequestEnabled() { | ||
return ProtobufPercentHelper::evaluateFractionalPercent(kill_request_.probability(), | ||
random_generator_.random()); | ||
} | ||
|
||
Http::FilterHeadersStatus KillRequestFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool) { | ||
const auto kill_request_header = headers.get(KillRequestHeaders::get().KillRequest); | ||
bool is_kill_request = false; | ||
// This is an implicitly untrusted header, so per the API documentation only | ||
// the first value is used. | ||
if (kill_request_header.empty() || | ||
!absl::SimpleAtob(kill_request_header[0]->value().getStringView(), &is_kill_request)) { | ||
return Http::FilterHeadersStatus::Continue; | ||
} | ||
|
||
if (is_kill_request && isKillRequestEnabled()) { | ||
// Crash Envoy. | ||
raise(SIGABRT); | ||
} | ||
|
||
return Http::FilterHeadersStatus::Continue; | ||
} | ||
|
||
} // namespace KillRequest | ||
} // namespace HttpFilters | ||
} // namespace Extensions | ||
} // namespace Envoy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: s/extenstion/extension/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done