Skip to content

Commit a016a6c

Browse files
committed
http: Use last address in XFF when all are trusted
When parsing XFF using a list of trusted CIDRs, if all IP in XFF match the trusted CIDRs (there are no untrusted IPs), return the first IP in XFF (the last evaluated) - and a test to confirm the behaviour. Signed-off-by: James O'Gorman <[email protected]>
1 parent 2c95c9f commit a016a6c

File tree

3 files changed

+22
-5
lines changed

3 files changed

+22
-5
lines changed

api/envoy/extensions/http/original_ip_detection/xff/v3/xff.proto

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ message XffConfig {
4141
//
4242
// If ``recurse`` is set the :ref:`config_http_conn_man_headers_x-forwarded-for`
4343
// header is also evaluated against the trusted CIDR list, and the last non-trusted
44-
// address is used as the original client address.
44+
// address is used as the original client address. If all addresses in ``x-forwarded-for``
45+
// are within the trusted list, the first entry is used.
4546
//
4647
// This is typically used when requests are proxied by a
4748
// `CDN <https://en.wikipedia.org/wiki/Content_delivery_network>`_.
@@ -55,8 +56,9 @@ message XffTrustedCidrs {
5556
// connections are considered trusted.
5657
repeated config.core.v3.CidrRange cidrs = 1;
5758

58-
// If ``true``, recurse through the :ref:`config_http_conn_man_headers_x-forwarded-for`
59-
// HTTP header to find the first (from the right) non-trusted IP address; otherwise
60-
// the last address in ``x-forwarded-for`` is used.
59+
// If ``true``, recurse backwards through the :ref:`config_http_conn_man_headers_x-forwarded-for`
60+
// HTTP header to find the first (from the right) non-trusted IP address. If all addresses
61+
// are trusted, the last (from the right) IP address is returned.
62+
// Otherwise the last address in ``x-forwarded-for`` is used.
6163
google.protobuf.BoolValue recurse = 2;
6264
}

source/common/http/utility.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,12 +805,14 @@ Utility::GetLastAddressFromXffInfo Utility::getLastNonTrustedAddressFromXFF(
805805
static constexpr absl::string_view separator(",");
806806

807807
const auto xff_entries = StringUtil::splitToken(xff_string, separator, false, true);
808+
Network::Address::InstanceConstSharedPtr last_valid_addr;
808809

809810
for (auto it = xff_entries.rbegin(); it != xff_entries.rend(); it++) {
810811
auto addr = Network::Utility::parseInternetAddressNoThrow(std::string(*it));
811812
if (addr == nullptr) {
812813
continue;
813814
}
815+
last_valid_addr = addr;
814816

815817
bool trusted = false;
816818
for (const auto& cidr : trusted_cidrs) {
@@ -834,7 +836,9 @@ Utility::GetLastAddressFromXffInfo Utility::getLastNonTrustedAddressFromXFF(
834836
}
835837
return {addr, true};
836838
}
837-
return {nullptr, false};
839+
// If we reach this point all addresses in XFF were considered trusted, so return
840+
// first IP in XFF (the last in the reverse-evaluated chain).
841+
return {last_valid_addr, true};
838842
}
839843

840844
Utility::GetLastAddressFromXffInfo

test/extensions/http/original_ip_detection/xff/xff_test.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ TEST_F(XffTrustedCidrsRecurseTest, Recurse) {
129129
EXPECT_EQ("2.2.2.2", result.detected_remote_address->ip()->addressAsString());
130130
}
131131

132+
TEST_F(XffTrustedCidrsRecurseTest, RecurseAllAddressesTrusted) {
133+
Envoy::Http::TestRequestHeaderMapImpl headers{
134+
{"x-forwarded-for", "192.0.2.4,192.0.2.5, 198.51.100.1"}};
135+
136+
auto remote_address = std::make_shared<Network::Address::Ipv4Instance>("192.0.2.11");
137+
Envoy::Http::OriginalIPDetectionParams params = {headers, remote_address};
138+
auto result = xff_extension_->detect(params);
139+
ASSERT_NE(result.detected_remote_address, nullptr);
140+
EXPECT_EQ("192.0.2.4", result.detected_remote_address->ip()->addressAsString());
141+
}
142+
132143
} // namespace Xff
133144
} // namespace OriginalIPDetection
134145
} // namespace Http

0 commit comments

Comments
 (0)