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 filter: ALPN http filter to override ALPN for upstream connection #8498

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extensions/filters/common/original_src @snowp @klarose
# adaptive concurrency limit extension.
/*/extensions/filters/http/adaptive_concurrency @tonya11en @mattklein123
# http inspector
/*/extensions/filters/listener/http_inspector @crazyxy @PiotrSikora @lizan
/*/extensions/filters/listener/http_inspector @yxue @PiotrSikora @lizan
# attribute context
/*/extensions/filters/common/expr @kyessenov @yangminzhu
# webassembly common extension
Expand All @@ -68,6 +68,7 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/filters/http/ext_authz @gsagula @dio
/*/extensions/filters/http/grpc_web @fengli79 @lizan
/*/extensions/filters/http/squash @yuval-k @alyssawilk
/*/extensions/filters/http/alpn @yxue @lizan
/*/extensions/filters/common/ext_authz @gsagula @dio
/*/extensions/filters/common/original_src @klarose @snowp
/*/extensions/filters/listener/tls_inspector @piotrsikora @htuch
Expand Down
1 change: 1 addition & 0 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ proto_library(
"//envoy/config/filter/accesslog/v2:accesslog",
"//envoy/config/filter/dubbo/router/v2alpha1:router",
"//envoy/config/filter/fault/v2:fault",
"//envoy/config/filter/http/alpn/v2alpha:alpn",
"//envoy/config/filter/http/buffer/v2:buffer",
"//envoy/config/filter/http/csrf/v2:csrf",
"//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:dynamic_forward_proxy",
Expand Down
10 changes: 10 additions & 0 deletions api/envoy/config/filter/http/alpn/v2alpha/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package()

api_proto_library_internal(
name = "alpn",
srcs = ["alpn.proto"],
)
31 changes: 31 additions & 0 deletions api/envoy/config/filter/http/alpn/v2alpha/alpn.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";

package envoy.config.filter.http.alpn.v2alpha;

option java_outer_classname = "AlpnProto";
option java_multiple_files = true;
option java_package = "io.envoyproxy.envoy.config.filter.http.alpn.v2alpha";

import "validate/validate.proto";

// [#protodoc-title: Alpn Filter]

// The filter overrides the ALPN in TLS context for upstream connections.
message FilterConfig {
// Downstream protocols
enum Protocol {
HTTP10 = 0;
HTTP11 = 1;
HTTP2 = 2;
}

message Entry {
// Downstream protocol
Protocol downstream_protocol = 1;

// Application protocols override.
repeated string alpn = 2 [(validate.rules).repeated = {min_items: 1}];
}

repeated Entry alpn_override = 1 [(validate.rules).repeated = {min_items: 1}];
}
10 changes: 10 additions & 0 deletions api/envoy/config/filter/http/alpn/v3alpha/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package()

api_proto_library_internal(
name = "alpn",
srcs = ["alpn.proto"],
)
31 changes: 31 additions & 0 deletions api/envoy/config/filter/http/alpn/v3alpha/alpn.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";

package envoy.config.filter.http.alpn.v3alpha;

option java_outer_classname = "AlpnProto";
option java_multiple_files = true;
option java_package = "io.envoyproxy.envoy.config.filter.http.alpn.v3alpha";

import "validate/validate.proto";

// [#protodoc-title: Alpn Filter]

// The filter overrides the ALPN in TLS context for upstream connections.
message FilterConfig {
// Downstream protocols
enum Protocol {
HTTP10 = 0;
HTTP11 = 1;
HTTP2 = 2;
}

message Entry {
// Downstream protocol
Protocol downstream_protocol = 1;

// Application protocols override.
repeated string alpn = 2 [(validate.rules).repeated = {min_items: 1}];
}

repeated Entry alpn_override = 1 [(validate.rules).repeated = {min_items: 1}];
}
33 changes: 33 additions & 0 deletions docs/root/configuration/http/http_filters/alpn_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.. _config_http_filters_alpn:

ALPN
====

ALPN filter is an HTTP filter which enables Envoy to set ALPN in the TLS context used by upstream
connections. :ref:`alpn_override <envoy_api_field_config.filter.http.alpn.v2alpha.FilterConfig.alpn_override>` provides
the map from downstream protocol to the ALPN used by upstream connections.


Configuration
-------------

A sample filter configuration could be:

.. code-block:: yaml

http_filters:
- name: envoy.filters.http.alpn
config:
alpn_override:
- downstream_protocol: HTTP10
alpn: ["foo", "bar"]
- downstream_protocol: HTTP11
alpn: ["baz"]
- downstream_protocol: HTTP2
alpn: ["qux"]
- name: envoy.router
config: {}

.. attention::

Make sure upstream protocol is the same as downstream protocol. Considering setting :ref:`USE_DOWNSTREAM_PROTOCOL <envoy_api_enum_value_Cluster.ClusterProtocolSelection.USE_DOWNSTREAM_PROTOCOL>`
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 @@ -6,6 +6,7 @@ HTTP filters
.. toctree::
:maxdepth: 2

alpn_filter
buffer_filter
cors_filter
csrf_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 @@ -38,6 +38,7 @@ Version history
* http: :ref:`AUTO <envoy_api_enum_value_config.filter.network.http_connection_manager.v2.HttpConnectionManager.CodecType.AUTO>` codec protocol inference now requires the H2 magic bytes to be the first bytes transmitted by a downstream client.
* http: remove h2c upgrade headers for HTTP/1 as h2c upgrades are currently not supported.
* http: absolute URL support is now on by default. The prior behavior can be reinstated by setting :ref:`allow_absolute_url <envoy_api_field_core.Http1ProtocolOptions.allow_absolute_url>` to false.
* http: added :ref:`ALPN HTTP filter <config_http_filters_alpn>`.
* listeners: added :ref:`continue_on_listener_filters_timeout <envoy_api_field_Listener.continue_on_listener_filters_timeout>` to configure whether a listener will still create a connection when listener filters time out.
* listeners: added :ref:`HTTP inspector listener filter <config_listener_filters_http_inspector>`.
* lua: extended `httpCall()` and `respond()` APIs to accept headers with entry values that can be a string or table of strings.
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 @@ -32,6 +32,7 @@ EXTENSIONS = {
#

"envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config",
"envoy.filters.http.alpn": "//source/extensions/filters/http/alpn:config",
"envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config",
"envoy.filters.http.cors": "//source/extensions/filters/http/cors:config",
"envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config",
Expand Down
36 changes: 36 additions & 0 deletions source/extensions/filters/http/alpn/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
licenses(["notice"]) # Apache 2

# A filter for overriding application protocols.

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

envoy_package()

envoy_cc_library(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
":alpn_filter_lib",
"//include/envoy/registry",
"//include/envoy/server:filter_config_interface",
"//source/extensions/filters/http:well_known_names",
"//source/extensions/filters/http/common:factory_base_lib",
],
)

envoy_cc_library(
name = "alpn_filter_lib",
srcs = ["alpn_filter.cc"],
hdrs = ["alpn_filter.h"],
deps = [
"//include/envoy/http:filter_interface",
"//source/common/network:application_protocol_lib",
"//source/extensions/filters/http/common:pass_through_filter_lib",
"@envoy_api//envoy/config/filter/http/alpn/v2alpha:alpn_cc",
],
)
63 changes: 63 additions & 0 deletions source/extensions/filters/http/alpn/alpn_filter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "extensions/filters/http/alpn/alpn_filter.h"

#include "common/network/application_protocol.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Alpn {

AlpnFilterConfig::AlpnFilterConfig(
const envoy::config::filter::http::alpn::v2alpha::FilterConfig& proto_config) {
for (const auto& pair : proto_config.alpn_override()) {
std::vector<std::string> application_protocols;
for (const auto& protocol : pair.alpn()) {
application_protocols.push_back(protocol);
}

alpn_override_.insert(
{getHttpProtocol(pair.downstream_protocol()), std::move(application_protocols)});
}
}

Http::Protocol AlpnFilterConfig::getHttpProtocol(
const envoy::config::filter::http::alpn::v2alpha::FilterConfig::Protocol& protocol) {
switch (protocol) {
case envoy::config::filter::http::alpn::v2alpha::FilterConfig::Protocol::
FilterConfig_Protocol_HTTP10:
return Http::Protocol::Http10;
case envoy::config::filter::http::alpn::v2alpha::FilterConfig::Protocol::
FilterConfig_Protocol_HTTP11:
return Http::Protocol::Http11;
case envoy::config::filter::http::alpn::v2alpha::FilterConfig::Protocol::
FilterConfig_Protocol_HTTP2:
return Http::Protocol::Http2;
default:
// will not reach here.
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
}
}

Http::FilterHeadersStatus AlpnFilter::decodeHeaders(Http::HeaderMap&, bool) {
auto protocol = decoder_callbacks_->streamInfo().protocol();
if (protocol.has_value()) {
auto alpn_override = config_->getAlpnOverride()[protocol.value()];
if (!alpn_override.empty()) {
decoder_callbacks_->streamInfo().filterState().setData(
Network::ApplicationProtocols::key(),
std::make_unique<Network::ApplicationProtocols>(alpn_override),
Envoy::StreamInfo::FilterState::StateType::ReadOnly);
} else {
ENVOY_LOG(debug, "alpn override is empty");
}
} else {
ENVOY_LOG(debug, "downstream protocol is not set");
}

return Http::FilterHeadersStatus::Continue;
}

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

#include "envoy/config/filter/http/alpn/v2alpha/alpn.pb.h"

#include "extensions/filters/http/common/pass_through_filter.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Alpn {

using AlpnOverride = absl::flat_hash_map<Http::Protocol, std::vector<std::string>>;

class AlpnFilterConfig {
public:
AlpnFilterConfig() = default;

explicit AlpnFilterConfig(
const envoy::config::filter::http::alpn::v2alpha::FilterConfig& proto_config);

AlpnOverride& getAlpnOverride() { return alpn_override_; }

private:
Http::Protocol getHttpProtocol(
const envoy::config::filter::http::alpn::v2alpha::FilterConfig::Protocol& protocol);

AlpnOverride alpn_override_;
};

using AlpnFilterConfigSharedPtr = std::shared_ptr<AlpnFilterConfig>;

class AlpnFilter : public Http::PassThroughDecoderFilter, Logger::Loggable<Logger::Id::filter> {
public:
explicit AlpnFilter(const AlpnFilterConfigSharedPtr& config) : config_(config) {}

// Http::PassThroughDecoderFilter
Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap& headers, bool end_stream) override;

private:
const AlpnFilterConfigSharedPtr config_;
};

} // namespace Alpn
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
27 changes: 27 additions & 0 deletions source/extensions/filters/http/alpn/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "extensions/filters/http/alpn/config.h"

#include "extensions/filters/http/alpn/alpn_filter.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Alpn {

Http::FilterFactoryCb AlpnConfigFactory::createFilterFactoryFromProtoTyped(
const envoy::config::filter::http::alpn::v2alpha::FilterConfig& proto_config,
const std::string&, Server::Configuration::FactoryContext&) {
AlpnFilterConfigSharedPtr filter_config{std::make_shared<AlpnFilterConfig>(proto_config)};
return [filter_config](Http::FilterChainFactoryCallbacks& callbacks) -> void {
callbacks.addStreamDecoderFilter(std::make_unique<AlpnFilter>(filter_config));
};
}

/**
* Static registration for the alpn override filter. @see RegisterFactory.
*/
REGISTER_FACTORY(AlpnConfigFactory, Server::Configuration::NamedHttpFilterConfigFactory);

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

#include "envoy/config/filter/http/alpn/v2alpha/alpn.pb.h"
#include "envoy/config/filter/http/alpn/v2alpha/alpn.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 Alpn {

/**
* Config registration for the alpn filter.
*/
class AlpnConfigFactory
: public Common::FactoryBase<envoy::config::filter::http::alpn::v2alpha::FilterConfig> {
public:
AlpnConfigFactory() : FactoryBase(HttpFilterNames::get().Alpn) {}

Http::FilterFactoryCb createFilterFactoryFromProtoTyped(
const envoy::config::filter::http::alpn::v2alpha::FilterConfig& proto_config,
const std::string& stat_prefix, Server::Configuration::FactoryContext& context) override;
};

} // namespace Alpn
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
2 changes: 2 additions & 0 deletions source/extensions/filters/http/well_known_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class HttpFilterNameValues {
const std::string OriginalSrc = "envoy.filters.http.original_src";
// Dynamic forward proxy filter
const std::string DynamicForwardProxy = "envoy.filters.http.dynamic_forward_proxy";
// ALPN filter
const std::string Alpn = "envoy.filters.http.alpn";

// Converts names from v1 to v2
const Config::V1Converter v1_converter_;
Expand Down
Loading