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: add Kill Request HTTP filter #14170

Merged
merged 9 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,5 @@ extensions/filters/http/oauth2 @rgs1 @derekargueta @snowp
# HTTP Local Rate Limit
/*/extensions/filters/http/local_ratelimit @rgs1 @mattklein123
/*/extensions/filters/common/local_ratelimit @mattklein123 @rgs1
# HTTP Kill Request
/*/extensions/filters/http/kill_request @qqustc @htuch
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ proto_library(
"//envoy/extensions/filters/http/health_check/v3:pkg",
"//envoy/extensions/filters/http/ip_tagging/v3:pkg",
"//envoy/extensions/filters/http/jwt_authn/v3:pkg",
"//envoy/extensions/filters/http/kill_request/v3:pkg",
"//envoy/extensions/filters/http/local_ratelimit/v3:pkg",
"//envoy/extensions/filters/http/lua/v3:pkg",
"//envoy/extensions/filters/http/oauth2/v3alpha:pkg",
Expand Down
12 changes: 12 additions & 0 deletions api/envoy/extensions/filters/http/kill_request/v3/BUILD
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;
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ proto_library(
"//envoy/extensions/filters/http/health_check/v3:pkg",
"//envoy/extensions/filters/http/ip_tagging/v3:pkg",
"//envoy/extensions/filters/http/jwt_authn/v3:pkg",
"//envoy/extensions/filters/http/kill_request/v3:pkg",
"//envoy/extensions/filters/http/local_ratelimit/v3:pkg",
"//envoy/extensions/filters/http/lua/v3:pkg",
"//envoy/extensions/filters/http/oauth2/v3alpha:pkg",
Expand Down
1 change: 1 addition & 0 deletions docs/root/configuration/http/http_filters/http_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ HTTP filters
header_to_metadata_filter
ip_tagging_filter
jwt_authn_filter
kill_request_filter
local_rate_limit_filter
lua_filter
oauth2_filter
Expand Down
39 changes: 39 additions & 0 deletions docs/root/configuration/http/http_filters/kill_request_filter.rst
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()*.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: s/extenstion/extension/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


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

1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ New Features
* http: added HCM :ref:`timeout config field <envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.request_headers_timeout>` to control how long a downstream has to finish sending headers before the stream is cancelled.
* http: added frame flood and abuse checks to the upstream HTTP/2 codec. This check is off by default and can be enabled by setting the `envoy.reloadable_features.upstream_http2_flood_checks` runtime key to true.
* jwt_authn: added support for :ref:`per-route config <envoy_v3_api_msg_extensions.filters.http.jwt_authn.v3.PerRouteConfig>`.
* kill_request: added new :ref:`HTTP kill request filter <config_http_filters_kill_request>`.
* listener: added an optional :ref:`default filter chain <envoy_v3_api_field_config.listener.v3.Listener.default_filter_chain>`. If this field is supplied, and none of the :ref:`filter_chains <envoy_v3_api_field_config.listener.v3.Listener.filter_chains>` matches, this default filter chain is used to serve the connection.
* log: added a new custom flag ``%_`` to the log pattern to print the actual message to log, but with escaped newlines.
* lua: added `downstreamDirectRemoteAddress()` and `downstreamLocalAddress()` APIs to :ref:`streamInfo() <config_http_filters_lua_stream_info_wrapper>`.
Expand Down

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.

7 changes: 6 additions & 1 deletion source/extensions/all_extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand All @@ -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]
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 envoy_all_extensions(). We always need some logic in envoy_all_http_filters to filter out kill_request_http_filter

Copy link
Member

Choose a reason for hiding this comment

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

Just create a GLOBAL_DENYLIST definition at the top and use it in both places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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"
Expand Down
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Harvey. Added comment.

Copy link
Member

Choose a reason for hiding this comment

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

Do you mean all_extensions.bzl in the comment? I still thinking adding it and then blocking by default is a bit weird.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Harvey! Added the filter to DISABLED_BY_DEFAULT_EXTENSIONS.

Copy link
Member

Choose a reason for hiding this comment

The 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 DISABLED_BY_DEFAULT_EXTENSIONS as you've done, but also not have to include the kill request filter in EXTENSIONS. This should be easy to do; you might need to modify some consumers, e.g. doc build, to consume from DISABLED_BY_DEFAULT_EXTENSIONS. That way, you can tell folks to move their extension from DISABLED_BY_DEFAULT_EXTENSIONS to EXTENSIONS to enable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah :) thank you Harvey for the explanation! Removed the filter from EXTENSIONS and updated the consumers to make the CI doc test pass. PTAL

"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",
Expand Down
40 changes: 40 additions & 0 deletions source/extensions/filters/http/kill_request/BUILD
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",
],
)
31 changes: 31 additions & 0 deletions source/extensions/filters/http/kill_request/kill_request_config.cc
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
31 changes: 31 additions & 0 deletions source/extensions/filters/http/kill_request/kill_request_config.h
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
38 changes: 38 additions & 0 deletions source/extensions/filters/http/kill_request/kill_request_filter.cc
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
Loading