Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c4de56e
refactor original_src_socket_option into common
klarose Apr 29, 2019
03100a4
refactor creation of socket options to helper
klarose Apr 29, 2019
d13f905
finish up skeleton of original source filter
klarose May 1, 2019
ae0a630
add upstreamSocketOptions
klarose May 1, 2019
78cb8e8
finish up http original src filter
klarose May 1, 2019
3ce2987
fixup rebase previous original_src_filter
klarose May 2, 2019
682536d
hook upstreamSocketOptions into httpConnPool
klarose May 2, 2019
818e307
Move upstream options to ActiveStream
klarose May 2, 2019
1be9292
document original_src http filter
klarose May 2, 2019
9822df4
Merge branch 'master' into http_original_src_filter
klarose May 2, 2019
918a5c6
Various fixes
klarose May 2, 2019
04480a0
add release note
klarose May 2, 2019
b289bb6
fix compile issue not found by gcc
klarose May 2, 2019
81a54e4
Merge branch 'master' into http_original_src_filter
klarose May 8, 2019
0c294dd
review fixes
klarose May 8, 2019
2df6143
fix spelling
klarose May 8, 2019
9766731
Merge branch 'master' into http_original_src_filter
klarose May 9, 2019
b48b79a
fix failing coverage test
klarose May 9, 2019
cecf5e3
code review fixes
klarose May 9, 2019
db489b3
add snowp as the original_src sponsor.
klarose May 13, 2019
af1c9bc
Code review fixes
klarose May 15, 2019
9f93270
Merge branch 'master' into http_original_src_filter
klarose May 15, 2019
594edef
Merge branch 'master' into http_original_src_filter
klarose May 21, 2019
befc157
Rework UT a bit
klarose May 21, 2019
b69c87f
Merge branch 'master' into http_original_src_filter
klarose May 22, 2019
6a7ed5e
Merge branch 'master' into http_original_src_filter
klarose May 29, 2019
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
1 change: 1 addition & 0 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ proto_library(
"//envoy/config/filter/http/ip_tagging/v2:ip_tagging",
"//envoy/config/filter/http/jwt_authn/v2alpha:jwt_authn",
"//envoy/config/filter/http/lua/v2:lua",
"//envoy/config/filter/http/original_src/v2alpha1:original_src",
"//envoy/config/filter/http/rate_limit/v2:rate_limit",
"//envoy/config/filter/http/rbac/v2:rbac",
"//envoy/config/filter/http/router/v2:router",
Expand Down
8 changes: 8 additions & 0 deletions api/envoy/config/filter/http/original_src/v2alpha1/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal")

licenses(["notice"]) # Apache 2

api_proto_library_internal(
name = "original_src",
srcs = ["original_src.proto"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
syntax = "proto3";

package envoy.config.filter.http.original_src.v2alpha1;

option java_outer_classname = "OriginalSrcProto";
option java_multiple_files = true;
option java_package = "io.envoyproxy.envoy.config.filter.http.original_src.v2alpha1";

option go_package = "v2alpha1";

import "validate/validate.proto";

// [#protodoc-title: Original Src Filter]
// Use the Original source address on upstream connections.

// The Original Src filter binds upstream connections to the original source address determined
// for the request. This address could come from something like the Proxy Protocol filter, or it
// could come from trusted http headers.
message OriginalSrc {

// Sets the SO_MARK option on the upstream connection's socket to the provided value. Used to
// ensure that non-local addresses may be routed back through envoy when binding to the original
// source address. The option will not be applied if the mark is 0.
// [#proto-status: experimental]
uint32 mark = 1;
}
1 change: 1 addition & 0 deletions docs/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ 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/jwt_authn/v2alpha/jwt_authn/envoy/config/filter/http/jwt_authn/v2alpha/config.proto.rst
/envoy/config/filter/http/lua/v2/lua/envoy/config/filter/http/lua/v2/lua.proto.rst
/envoy/config/filter/http/original_src/v2alpha1/original_src/envoy/config/filter/http/original_src/v2alpha1/original_src.proto.rst
/envoy/config/filter/http/rate_limit/v2/rate_limit/envoy/config/filter/http/rate_limit/v2/rate_limit.proto.rst
/envoy/config/filter/http/rbac/v2/rbac/envoy/config/filter/http/rbac/v2/rbac.proto.rst
/envoy/config/filter/http/router/v2/router/envoy/config/filter/http/router/v2/router.proto.rst
Expand Down
1 change: 1 addition & 0 deletions docs/root/api-v2/config/filter/http/http.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ HTTP filters

*/v2/*
*/v2alpha/*
*/v2alpha1/*
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 @@ -22,6 +22,7 @@ HTTP filters
ip_tagging_filter
jwt_authn_filter
lua_filter
original_src_filter
rate_limit_filter
rbac_filter
router_filter
Expand Down
65 changes: 65 additions & 0 deletions docs/root/configuration/http_filters/original_src_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.. _config_http_filters_original_src:

Original Source
===============

* :ref:`HTTP filter v2 API reference <envoy_api_msg_config.filter.http.original_src.v2alpha1.OriginalSrc>`
* This filter should be configured with the name *envoy.filters.http.original_src*.

The original source http filter replicates the downstream remote address of the connection on
the upstream side of Envoy. For example, if a downstream connection connects to Envoy with IP
address ``10.1.2.3``, then Envoy will connect to the upstream with source IP ``10.1.2.3``. The
downstream remote address is determined based on the logic for the "trusted client address"
outlined in :ref:`XFF <config_http_conn_man_headers_x-forwarded-for>`.


IP Version Support
------------------
The filter supports both IPv4 and IPv6 as addresses. Note that the upstream connection must support
the version used.

Extra Setup
-----------

The downstream remote address used will likely be globally routable. By default, packets returning
from the upstream host to that address will not route through Envoy. The network must be configured
to forcefully route any traffic whose IP was replicated by Envoy back through the Envoy host.

If Envoy and the upstream are on the same host -- e.g. in an sidecar deployment --, then iptables
and routing rules can be used to ensure correct behaviour. The filter has an unsigned integer
configuration,
:ref:`mark <envoy_api_field_config.filter.http.original_src.v2alpha1.OriginalSrc.mark>`. Setting
this to *X* causes Envoy to *mark* all upstream packets originating from this http with value
*X*. Note that if
:ref:`mark <envoy_api_field_config.filter.http.original_src.v2alpha1.OriginalSrc.mark>` is set
to 0, Envoy will not mark upstream packets.

We can use the following set of commands to ensure that all ipv4 and ipv6 traffic marked with *X*
(assumed to be 123 in the example) routes correctly. Note that this example assumes that *eth0* is
the default outbound interface.

.. code-block:: text

iptables -t mangle -I PREROUTING -m mark --mark 123 -j CONNMARK --save-mark
iptables -t mangle -I OUTPUT -m connmark --mark 123 -j CONNMARK --restore-mark
ip6tables -t mangle -I PREROUTING -m mark --mark 123 -j CONNMARK --save-mark
ip6tables -t mangle -I OUTPUT -m connmark --mark 123 -j CONNMARK --restore-mark
ip rule add fwmark 123 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
ip -6 rule add fwmark 123 lookup 100
ip -6 route add local ::/0 dev lo table 100
echo 1 > /proc/sys/net/ipv4/conf/eth0/route_localnet


Example HTTP configuration
------------------------------

The following example configures Envoy to use the original source for all connections made on port
8888. All upstream packets are marked with 123.

.. code-block:: yaml

http_filters:
Comment thread
klarose marked this conversation as resolved.
- name: envoy.filters.http.original_src
config:
mark: 123
26 changes: 25 additions & 1 deletion docs/root/intro/arch_overview/ip_transparency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ HTTP Headers

HTTP headers may carry the original IP address of the request in the
:ref:`x-forwarded-for <config_http_conn_man_headers_x-forwarded-for>` header. The upstream server
can use this header to determine the downstream remote address.
can use this header to determine the downstream remote address. Envoy may also use this header to
choose the IP address used by the
:ref:`Original Src HTTP Filter <arch_overview_ip_transparency_original_src_http>`.

The HTTP header approach has a few downsides:

Expand Down Expand Up @@ -69,3 +71,25 @@ Some drawbacks to the Original Source filter:
* It requires that Envoy have access to the downstream remote address.
* Its configuration is relatively complex.
* It may introduce a slight performance hit due to restrictions on connection pooling.

.. _arch_overview_ip_transparency_original_src_http:

Original Source HTTP Filter
---------------------------

In controlled deployments, it may be possible to replicate the downstream remote address on the
upstream connection by using a
:ref:`Original Source HTTP filter <config_http_filters_original_src>`. This filter operates much like
the :ref:`Original Src Listener Filter <arch_overview_ip_transparency_original_src_listener>`. The
main difference is that it can infer the original source address from HTTP headers, which is important
for cases where a single downstream connection carries multiple HTTP requests from different original
Comment thread
klarose marked this conversation as resolved.
source addresses.
This filter will work with any upstream HTTP host. However, it requires fairly complex configuration,
and it may not be supported in all deployments due to routing constraints.

Some drawbacks to the Original Source filter:

* It requires that Envoy be properly configured to extract the downstream remote address from the
:ref:`x-forwarded-for <config_http_conn_man_headers_x-forwarded-for>` header.
* Its configuration is relatively complex.
* It may introduce a slight performance hit due to restrictions on connection pooling.
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Version history
* hot restart: stats are no longer shared between hot restart parent/child via shared memory, but rather by RPC. Hot restart version incremented to 11.
* http: mitigated a race condition with the :ref:`delayed_close_timeout<envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.delayed_close_timeout>` where it could trigger while actively flushing a pending write buffer for a downstream connection.
* jwt_authn: make filter's parsing of JWT more flexible, allowing syntax like ``jwt=eyJhbGciOiJS...ZFnFIw,extra=7,realm=123``
* original_src filter: added the :ref:`filter<config_http_filters_original_src>`.
* redis: added :ref:`prefix routing <envoy_api_field_config.filter.network.redis_proxy.v2.RedisProxy.prefix_routes>` to enable routing commands based on their key's prefix to different upstream.
* redis: add support for zpopmax and zpopmin commands.
* redis: added
Expand Down
14 changes: 14 additions & 0 deletions include/envoy/http/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,20 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks {
// Note that HttpConnectionManager sanitization will *not* be performed on the
// recreated stream, as it is assumed that sanitization has already been done.
virtual bool recreateStream() PURE;

/**
* Adds socket options to be applied to the upstream request. Note that unique values for the
* options will likely lead to many connection pools being created. The added options are appended
* to any previously added.
*
* @param options The options to be added.
*/
virtual void addUpstreamSocketOptions(const Network::Socket::OptionsSharedPtr& options) PURE;

/**
* @return The socket options to be applied to the upstream request.
*/
virtual Network::Socket::OptionsSharedPtr getUpstreamSocketOptions() const PURE;
};

/**
Expand Down
5 changes: 5 additions & 0 deletions include/envoy/upstream/load_balancer.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ class LoadBalancerContext {
* ignored.
*/
virtual uint32_t hostSelectionRetryCount() const PURE;

/**
* Returns the set of socket options which should be applied on upstream connections
*/
virtual Network::Socket::OptionsSharedPtr upstreamSocketOptions() const PURE;
};

/**
Expand Down
2 changes: 2 additions & 0 deletions source/common/http/async_client_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ class AsyncStreamImpl : public AsyncClient::Stream,
void setDecoderBufferLimit(uint32_t) override {}
uint32_t decoderBufferLimit() override { return 0; }
bool recreateStream() override { return false; }
void addUpstreamSocketOptions(const Network::Socket::OptionsSharedPtr&) override {}
Network::Socket::OptionsSharedPtr getUpstreamSocketOptions() const override { return {}; }

AsyncClient::StreamCallbacks& stream_callbacks_;
const uint64_t stream_id_;
Expand Down
3 changes: 2 additions & 1 deletion source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@ ConnectionManagerImpl::ActiveStream::ActiveStream(ConnectionManagerImpl& connect
stream_id_(connection_manager.random_generator_.random()),
request_response_timespan_(new Stats::Timespan(
connection_manager_.stats_.named_.downstream_rq_time_, connection_manager_.timeSource())),
stream_info_(connection_manager_.codec_->protocol(), connection_manager_.timeSource()) {
stream_info_(connection_manager_.codec_->protocol(), connection_manager_.timeSource()),
upstream_options_(std::make_shared<Network::Socket::Options>()) {
connection_manager_.stats_.named_.downstream_rq_total_.inc();
connection_manager_.stats_.named_.downstream_rq_active_.inc();
if (connection_manager_.codec_->protocol() == Protocol::Http2) {
Expand Down
9 changes: 9 additions & 0 deletions source/common/http/conn_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
uint32_t decoderBufferLimit() override { return parent_.buffer_limit_; }
bool recreateStream() override;

void addUpstreamSocketOptions(const Network::Socket::OptionsSharedPtr& options) override {
Network::Socket::appendOptions(parent_.upstream_options_, options);
}

Network::Socket::OptionsSharedPtr getUpstreamSocketOptions() const override {
return parent_.upstream_options_;
}

// Each decoder filter instance checks if the request passed to the filter is gRPC
// so that we can issue gRPC local responses to gRPC requests. Filter's decodeHeaders()
// called here may change the content type, so we must check it before the call.
Expand Down Expand Up @@ -517,6 +525,7 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
// Whether a filter has indicated that the response should be treated as a headers only
// response.
bool encoding_headers_only_{};
Network::Socket::OptionsSharedPtr upstream_options_;
};

typedef std::unique_ptr<ActiveStream> ActiveStreamPtr;
Expand Down
4 changes: 4 additions & 0 deletions source/common/router/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ class Filter : Logger::Loggable<Logger::Id::router>,
return retry_state_->hostSelectionMaxAttempts();
}

Network::Socket::OptionsSharedPtr upstreamSocketOptions() const override {
return callbacks_->getUpstreamSocketOptions();
}

/**
* Set a computed cookie to be sent with the downstream headers.
* @param key supplies the size of the cookie
Expand Down
36 changes: 23 additions & 13 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@

namespace Envoy {
namespace Upstream {
namespace {

void addOptionsIfNotNull(Network::Socket::OptionsSharedPtr& options,
const Network::Socket::OptionsSharedPtr& to_add) {
if (to_add) {
Comment thread
klarose marked this conversation as resolved.
Outdated
Network::Socket::appendOptions(options, to_add);
}
}

} // namespace

void ClusterManagerInitHelper::addCluster(Cluster& cluster) {
// See comments in ClusterManagerImpl::addOrUpdateCluster() for why this is only called during
Expand Down Expand Up @@ -1133,22 +1143,22 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool(
return nullptr;
}

// Inherit socket options from downstream connection, if set.
std::vector<uint8_t> hash_key = {uint8_t(protocol)};

// Use downstream connection socket options for computing connection pool hash key, if any.
Network::Socket::OptionsSharedPtr upstream_options(std::make_shared<Network::Socket::Options>());
if (context) {
// Inherit socket options from downstream connection, if set.
if (context->downstreamConnection()) {
addOptionsIfNotNull(upstream_options, context->downstreamConnection()->socketOptions());
}
addOptionsIfNotNull(upstream_options, context->upstreamSocketOptions());
}

// Use the socket options for computing connection pool hash key, if any.
// This allows socket options to control connection pooling so that connections with
// different options are not pooled together.
bool have_options = false;
if (context && context->downstreamConnection()) {
const Network::ConnectionSocket::OptionsSharedPtr& options =
context->downstreamConnection()->socketOptions();
if (options) {
for (const auto& option : *options) {
have_options = true;
option->hashKey(hash_key);
}
}
for (const auto& option : *upstream_options) {
option->hashKey(hash_key);
}

ConnPoolsContainer& container = *parent_.getHttpConnPoolsContainer(host, true);
Expand All @@ -1159,7 +1169,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool(
container.pools_->getPool(priority, hash_key, [&]() {
return parent_.parent_.factory_.allocateConnPool(
parent_.thread_local_dispatcher_, host, priority, protocol,
have_options ? context->downstreamConnection()->socketOptions() : nullptr);
!upstream_options->empty() ? upstream_options : nullptr);
});

if (pool.has_value()) {
Expand Down
2 changes: 2 additions & 0 deletions source/common/upstream/load_balancer_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ class LoadBalancerContextBase : public LoadBalancerContext {
bool shouldSelectAnotherHost(const Host&) override { return false; }

uint32_t hostSelectionRetryCount() const override { return 1; }

Network::Socket::OptionsSharedPtr upstreamSocketOptions() const override { return {}; }
};

/**
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 @@ -39,6 +39,7 @@ EXTENSIONS = {
"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",
"envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config",
"envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config",
"envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config",
"envoy.filters.http.router": "//source/extensions/filters/http/router:config",
Expand Down
37 changes: 37 additions & 0 deletions source/extensions/filters/common/original_src/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
licenses(["notice"]) # Apache 2

# Helprs for filters for mirroring the downstream remote address on the upstream's source.

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

envoy_package()

envoy_cc_library(
name = "original_src_socket_option_lib",
srcs = ["original_src_socket_option.cc"],
hdrs = ["original_src_socket_option.h"],
deps = [
"//include/envoy/network:listen_socket_interface",
"//source/common/common:assert_lib",
"//source/common/common:minimal_logger_lib",
"//source/common/network:address_lib",
"//source/common/network:utility_lib",
],
)

envoy_cc_library(
name = "socket_option_factory_lib",
srcs = ["socket_option_factory.cc"],
hdrs = ["socket_option_factory.h"],
deps = [
":original_src_socket_option_lib",
"//include/envoy/network:listen_socket_interface",
"//source/common/network:address_lib",
"//source/common/network:socket_option_factory_lib",
"//source/common/network:utility_lib",
],
)
Loading