Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

# csrf extension
/*/extensions/filters/http/csrf @dschaller @mattklein123
# original_src http filter extension
/*/extensions/filters/http/original_src @snowp @klarose
# original_src listener filter extension
/*/extensions/filters/listener/original_src @snowp @klarose
# dubbo_proxy extension
/*/extensions/filters/network/dubbo_proxy @zyfjeff @lizan
# thrift_proxy extension
Expand Down
1 change: 1 addition & 0 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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 @@ -102,6 +102,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
71 changes: 71 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,71 @@
.. _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>`.


Note that the filter is intended to be used in conjunction with the
:ref:`Router <config_http_filters_router>` filter. In particular, it must run prior to the router
filter so that it may add the desired source IP to the state of the filter chain.

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:
- name: envoy.filters.http.original_src
config:
mark: 123
- name: envoy.router
config: {}
28 changes: 27 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,27 @@ 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
source addresses. Deployments with a front proxy forwarding to sidecar proxies are examples where case
applies.

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 @@ -24,6 +24,7 @@ Version history
* http: fixed a crashing bug where gRPC local replies would cause segfaults when upstream access logging was on.
* 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>`.
* rbac: migrated from v2alpha to v2.
* redis: add support for Redis cluster custom cluster type.
* 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.
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 @@ -44,6 +44,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",
],
)
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#include "extensions/filters/listener/original_src/original_src_socket_option.h"
#include "extensions/filters/common/original_src/original_src_socket_option.h"

#include "common/common/assert.h"

namespace Envoy {
namespace Extensions {
namespace ListenerFilters {
namespace Filters {
namespace Common {
namespace OriginalSrc {

OriginalSrcSocketOption::OriginalSrcSocketOption(
Expand Down Expand Up @@ -57,6 +58,7 @@ OriginalSrcSocketOption::getOptionDetails(const Network::Socket&,
}

} // namespace OriginalSrc
} // namespace ListenerFilters
} // namespace Common
} // namespace Filters
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

namespace Envoy {
namespace Extensions {
namespace ListenerFilters {
namespace Filters {
namespace Common {
namespace OriginalSrc {
/**
* A socket option implementation which allows a connection to spoof its source IP/port using
Expand Down Expand Up @@ -40,6 +41,7 @@ class OriginalSrcSocketOption : public Network::Socket::Option {
};

} // namespace OriginalSrc
} // namespace ListenerFilters
} // namespace Common
} // namespace Filters
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "extensions/filters/common/original_src/socket_option_factory.h"

#include "common/network/socket_option_factory.h"
#include "common/network/utility.h"

#include "extensions/filters/common/original_src/original_src_socket_option.h"

namespace Envoy {
namespace Extensions {
namespace Filters {
namespace Common {
namespace OriginalSrc {

Network::Socket::OptionsSharedPtr
buildOriginalSrcOptions(Network::Address::InstanceConstSharedPtr source, uint32_t mark) {
const auto address_without_port = Network::Utility::getAddressWithPort(*source, 0);

// Note: we don't expect this to change the behaviour of the socket. We expect it to be copied
// into the upstream connection later.
auto options_to_add = std::make_shared<Network::Socket::Options>();
options_to_add->emplace_back(
std::make_shared<Filters::Common::OriginalSrc::OriginalSrcSocketOption>(
std::move(address_without_port)));

if (mark != 0) {
const auto mark_options = Network::SocketOptionFactory::buildSocketMarkOptions(mark);
options_to_add->insert(options_to_add->end(), mark_options->begin(), mark_options->end());
}

const auto transparent_options = Network::SocketOptionFactory::buildIpTransparentOptions();
options_to_add->insert(options_to_add->end(), transparent_options->begin(),
transparent_options->end());
return options_to_add;
}

} // namespace OriginalSrc
} // namespace Common
} // namespace Filters
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include "envoy/network/address.h"
#include "envoy/network/listen_socket.h"

namespace Envoy {
namespace Extensions {
namespace Filters {
namespace Common {
namespace OriginalSrc {

Network::Socket::OptionsSharedPtr
buildOriginalSrcOptions(Network::Address::InstanceConstSharedPtr source, uint32_t mark);

} // namespace OriginalSrc
} // namespace Common
} // namespace Filters
} // namespace Extensions
} // namespace Envoy
47 changes: 47 additions & 0 deletions source/extensions/filters/http/original_src/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
licenses(["notice"]) # Apache 2

# A filter 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 = "config_lib",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
"@envoy_api//envoy/config/filter/http/original_src/v2alpha1:original_src_cc",
],
)

envoy_cc_library(
name = "original_src_lib",
srcs = ["original_src.cc"],
hdrs = ["original_src.h"],
deps = [
":config_lib",
"//include/envoy/http:filter_interface",
"//source/common/common:assert_lib",
"//source/common/common:minimal_logger_lib",
"//source/extensions/filters/common/original_src:socket_option_factory_lib",
],
)

envoy_cc_library(
name = "config", # The extension build system requires a library named config
srcs = ["original_src_config_factory.cc"],
hdrs = ["original_src_config_factory.h"],
deps = [
":config_lib",
":original_src_lib",
"//include/envoy/registry",
"//source/extensions/filters/http:well_known_names",
"//source/extensions/filters/http/common:factory_base_lib",
"@envoy_api//envoy/config/filter/http/original_src/v2alpha1:original_src_cc",
],
)
Loading