Skip to content

Commit

Permalink
Header to metadata filter (#2436) (#3633)
Browse files Browse the repository at this point in the history
Add support for extracting dynamic metadata from requests. This can then
be used as static metadata would be used (e.g.: for subset load balancer
metadata matches, logging, etc).

Risk Level: Low
Testing: unit-test
Docs Changes: Basic docs.
Release Notes: N/A

Signed-off-by: Raul Gutierrez Segales <[email protected]>
  • Loading branch information
rgs1 authored and zuercher committed Jun 21, 2018
1 parent c75fb59 commit 827c0a5
Show file tree
Hide file tree
Showing 20 changed files with 869 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
/*/extensions/filters/network/thrift_proxy @zuercher @brian-pane
# jwt_authn http filter extension
/*/extensions/filters/http/jwt_authn @qiwzhang @lizan
# header_to_metadata extension
/*/extensions/filters/http/header_to_metadata @rgs1 @zuercher
1 change: 1 addition & 0 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ proto_library(
"//envoy/config/filter/http/ext_authz/v2alpha:ext_authz",
"//envoy/config/filter/http/fault/v2:fault",
"//envoy/config/filter/http/gzip/v2:gzip",
"//envoy/config/filter/http/header_to_metadata/v2:header_to_metadata",
"//envoy/config/filter/http/health_check/v2:health_check",
"//envoy/config/filter/http/ip_tagging/v2:ip_tagging",
"//envoy/config/filter/http/lua/v2:lua",
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/config/filter/http/header_to_metadata/v2/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("//bazel:api_build_system.bzl", "api_proto_library")

licenses(["notice"]) # Apache 2

api_proto_library(
name = "header_to_metadata",
srcs = ["header_to_metadata.proto"],
deps = [],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
syntax = "proto3";

package envoy.config.filter.http.header_to_metadata.v2;
option go_package = "v2";

import "validate/validate.proto";

// [#protodoc-title: Header-To-Metadata Filter]
//
// The configuration for transforming headers into metadata. This is useful
// for matching load balancer subsets, logging, etc.
//
// Header to Metadata :ref:`configuration overview <config_http_filters_header_to_metadata>`.

message Config {
enum ValueType {
STRING = 0;
NUMBER = 1;
}

message KeyValuePair {
// The namespace — if this is empty, the filter's namespace will be used.
string metadata_namespace = 1;

// The key to use within the namespace.
string key = 2 [(validate.rules).string.min_bytes = 1];

// The value to pair with the given key.
//
// When used for a `on_header_present` case, if value is non-empty it'll be used
// instead of the header value. If both are empty, no metadata is added.
//
// When used for a `on_header_missing` case, a non-empty value must be provided
// otherwise no metadata is added.
string value = 3;

// The value's type — defaults to string.
ValueType type = 4;
}

// A Rule defines what metadata to apply when a header is present or missing.
message Rule {
// The header that triggers this rule — required.
string header = 1 [(validate.rules).string.min_bytes = 1];

// If the header is present, apply this metadata KeyValuePair.
//
// If the value in the KeyValuePair is non-empty, it'll be used instead
// of the header value.
KeyValuePair on_header_present = 2;

// If the header is not present, apply this metadata KeyValuePair.
//
// The value in the KeyValuePair must be set, since it'll be used in lieu
// of the missing header value.
KeyValuePair on_header_missing = 3;

// Whether or not to remove the header after a rule is applied.
//
// This prevents headers from leaking.
bool remove = 4;
}

// The list of rules to apply to requests.
repeated Rule request_rules = 1;

// The list of rules to apply to responses.
repeated Rule response_rules = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ message HttpFilter {
// * :ref:`envoy.grpc_json_transcoder <config_http_filters_grpc_json_transcoder>`
// * :ref:`envoy.grpc_web <config_http_filters_grpc_web>`
// * :ref:`envoy.health_check <config_http_filters_health_check>`
// * :ref:`envoy.header_to_metadata <config_http_filters_header_to_metadata>`
// * :ref:`envoy.ip_tagging <config_http_filters_ip_tagging>`
// * :ref:`envoy.lua <config_http_filters_lua>`
// * :ref:`envoy.rate_limit <config_http_filters_rate_limit>`
Expand Down
1 change: 1 addition & 0 deletions api/test/validate/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ api_cc_test(
"//envoy/config/filter/http/buffer/v2:buffer",
"//envoy/config/filter/http/fault/v2:fault",
"//envoy/config/filter/http/gzip/v2:gzip",
"//envoy/config/filter/http/header_to_metadata/v2:header_to_metadata",
"//envoy/config/filter/http/health_check/v2:health_check",
"//envoy/config/filter/http/ip_tagging/v2:ip_tagging",
"//envoy/config/filter/http/lua/v2:lua",
Expand Down
1 change: 1 addition & 0 deletions api/test/validate/pgv_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "envoy/config/filter/http/fault/v2/fault.pb.validate.h"
#include "envoy/config/filter/http/gzip/v2/gzip.pb.validate.h"
#include "envoy/config/filter/http/health_check/v2/health_check.pb.validate.h"
#include "envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.pb.validate.h"
#include "envoy/config/filter/http/ip_tagging/v2/ip_tagging.pb.validate.h"
#include "envoy/config/filter/http/lua/v2/lua.pb.validate.h"
#include "envoy/config/filter/http/router/v2/router.pb.validate.h"
Expand Down
1 change: 1 addition & 0 deletions docs/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ PROTO_RST="
/envoy/config/filter/http/fault/v2/fault/envoy/config/filter/http/fault/v2/fault.proto.rst
/envoy/config/filter/http/gzip/v2/gzip/envoy/config/filter/http/gzip/v2/gzip.proto.rst
/envoy/config/filter/http/health_check/v2/health_check/envoy/config/filter/http/health_check/v2/health_check.proto.rst
/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.proto.rst
/envoy/config/filter/http/ip_tagging/v2/ip_tagging/envoy/config/filter/http/ip_tagging/v2/ip_tagging.proto.rst
/envoy/config/filter/http/lua/v2/lua/envoy/config/filter/http/lua/v2/lua.proto.rst
/envoy/config/filter/http/rate_limit/v2/rate_limit/envoy/config/filter/http/rate_limit/v2/rate_limit.proto.rst
Expand Down
64 changes: 64 additions & 0 deletions docs/root/configuration/http_filters/header_to_metadata_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.. _config_http_filters_header_to_metadata:

Envoy Header-To-Metadata Filter
===============================
* :ref:`v2 API reference <envoy_api_msg_config.filter.http.header_to_metadata.v2.Config>`

This filter is configured with rules that will be matched against requests and responses.
Each rule has a header and can be triggered either when the header is present or missing. When
a rule is triggered, dynamic metadata will be added based on the configuration of the rule.
The metadata can then be used for load balancing decisions, consumed from logs, etc.

A typical use case for this filter is to dynamically match requests with load balancer
subsets. For this, a given header's value would be extracted and attached to the request
as dynamic metadata which would then be used to match a subset of endpoints.

Example
-------

A sample filter configuration to route traffic to endpoints based on the presence or
absence of a version header could be:

.. code-block:: yaml
http_filters:
- name: envoy.filters.http.header_to_metadata
config:
request_rules:
- header: x-version
on_header_present:
metadata_namespace: envoy.lb
key: version
type: STRING
on_header_missing:
metadata_namespace: envoy.lb
key: default
value: 'true'
type: STRING
remove: false
A corresponding upstream cluster configuration could be:

.. code-block:: yaml
clusters:
- name: versioned-cluster
type: EDS
lb_policy: ROUND_ROBIN
lb_subset_config:
fallback_policy: ANY_ENDPOINT
subset_selectors:
- keys:
- default
- keys:
- version
This would then allow requests with the `x-version` header set to be matched against
endpoints with the corresponding version. Whereas requests with that header missing
would be matched with the default endpoints.

Statistics
----------

Currently, this filter generates no statistics.
1 change: 1 addition & 0 deletions docs/root/configuration/http_filters/http_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ HTTP filters
grpc_web_filter
gzip_filter
health_check_filter
header_to_metadata_filter
ip_tagging_filter
lua_filter
rate_limit_filter
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Version history
* websocket: support configuring
:ref:`idle_timeout and max_connect_attempts <envoy_api_field_route.RouteAction.websocket_config>`.
* upstream: added support for host override for a request in :ref:`Original destination host request header <arch_overview_load_balancing_types_original_destination_request_header>`.
* header to metadata: added :ref:`HTTP Header to Metadata filter<config_http_filters_header_to_metadata>`.

1.6.0 (March 20, 2018)
======================
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 @@ -33,6 +33,7 @@ EXTENSIONS = {
"envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config",
"envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config",
"envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config",
"envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata: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.lua": "//source/extensions/filters/http/lua:config",
Expand Down
35 changes: 35 additions & 0 deletions source/extensions/filters/http/header_to_metadata/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
licenses(["notice"]) # Apache 2
# HTTP L7 filter that transforms request data into dynamic metadata
# Public docs: docs/root/configuration/http_filters/header_to_metadata_filter.rst

load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "header_to_metadata_filter_lib",
srcs = ["header_to_metadata_filter.cc"],
hdrs = ["header_to_metadata_filter.h"],
deps = [
"//include/envoy/server:filter_config_interface",
"//source/extensions/filters/http:well_known_names",
"@envoy_api//envoy/config/filter/http/header_to_metadata/v2:header_to_metadata_cc",
],
)

envoy_cc_library(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
"//include/envoy/registry",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/http:well_known_names",
"//source/extensions/filters/http/common:factory_base_lib",
"//source/extensions/filters/http/header_to_metadata:header_to_metadata_filter_lib",
],
)
38 changes: 38 additions & 0 deletions source/extensions/filters/http/header_to_metadata/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "extensions/filters/http/header_to_metadata/config.h"

#include <string>

#include "envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.pb.validate.h"
#include "envoy/registry/registry.h"

#include "common/protobuf/utility.h"

#include "extensions/filters/http/header_to_metadata/header_to_metadata_filter.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace HeaderToMetadataFilter {

Http::FilterFactoryCb HeaderToMetadataConfig::createFilterFactoryFromProtoTyped(
const envoy::config::filter::http::header_to_metadata::v2::Config& proto_config,
const std::string&, Server::Configuration::FactoryContext&) {
ConfigSharedPtr filter_config(std::make_shared<Config>(proto_config));

return [filter_config](Http::FilterChainFactoryCallbacks& callbacks) -> void {
callbacks.addStreamFilter(
Http::StreamFilterSharedPtr{new HeaderToMetadataFilter(filter_config)});
};
}

/**
* Static registration for the header-to-metadata filter. @see RegisterFactory.
*/
static Registry::RegisterFactory<HeaderToMetadataConfig,
Server::Configuration::NamedHttpFilterConfigFactory>
register_;

} // namespace HeaderToMetadataFilter
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
30 changes: 30 additions & 0 deletions source/extensions/filters/http/header_to_metadata/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include "envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.pb.h"

#include "extensions/filters/http/common/factory_base.h"
#include "extensions/filters/http/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace HeaderToMetadataFilter {

/**
* Config registration for the header-to-metadata filter. @see NamedHttpFilterConfigFactory.
*/
class HeaderToMetadataConfig
: public Common::FactoryBase<envoy::config::filter::http::header_to_metadata::v2::Config> {
public:
HeaderToMetadataConfig() : FactoryBase(HttpFilterNames::get().HEADER_TO_METADATA) {}

private:
Http::FilterFactoryCb createFilterFactoryFromProtoTyped(
const envoy::config::filter::http::header_to_metadata::v2::Config& proto_config,
const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override;
};

} // namespace HeaderToMetadataFilter
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
Loading

0 comments on commit 827c0a5

Please sign in to comment.