diff --git a/doc/admin-guide/configuration/index.en.rst b/doc/admin-guide/configuration/index.en.rst
index cc2e3f603e6..1aecdbe29fe 100644
--- a/doc/admin-guide/configuration/index.en.rst
+++ b/doc/admin-guide/configuration/index.en.rst
@@ -32,3 +32,4 @@ Proxy Cache Configuration
transparent-proxy.en
transparent-forward-proxying.en
hierachical-caching.en
+ proxy-protocol.en
diff --git a/doc/admin-guide/configuration/proxy-protocol.en.rst b/doc/admin-guide/configuration/proxy-protocol.en.rst
new file mode 100644
index 00000000000..2d7673b2914
--- /dev/null
+++ b/doc/admin-guide/configuration/proxy-protocol.en.rst
@@ -0,0 +1,100 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. include:: ../../common.defs
+
+.. _proxy-protocol:
+
+Proxy Protocol
+****************
+
+The `PROXY protocol `_
+provides a means of passing connection information between layers of the proxy
+infrastructure. Without the PROXY protocol, |TS| would only have connection
+information from the previous hop connecting to |TS| and not the actual
+originating client connection information. This can be done over either HTTP or
+TLS connections.
+
+.. note::
+
+ The current version only supports transforming client IP from PROXY Version 1
+ header to the Forwarded: header.
+
+In the current implementation, the client IP address in the PROXY protocol header
+is passed to the origin server via an HTTP `Forwarded:
+`_ header.
+
+The Proxy Protocol must be enabled on each port. See
+:ts:cv:`proxy.config.http.server_ports` for information on how to enable the
+Proxy Protocol on a port. Once enabled, all incoming requests must be prefaced
+with the PROXY v1 header. Any request not preface by this header will be
+dropped.
+
+As a security measure, an optional whitelist of trusted IP addresses may be
+configured with :ts:cv:`proxy.config.http.proxy_protocol_whitelist`.
+
+ .. important::
+
+ If the whitelist is configured, requests will only be accepted from these
+ IP addressses and must be prefaced with the PROXY v1 header.
+
+See :ts:cv:`proxy.config.http.insert_forwarded` for configuration information.
+Detection of the PROXY protocol header is automatic. If the PROXY header
+precludes the request, it will automatically be parse and made available to the
+Forwarded: request header sent to the origin server.
+
+Example
+-------
+
+As an example, consider the following topology:
+
+.. figure:: ../../static/images/admin/proxy-protocol.png
+ :align: center
+ :alt: PROXY protocol transformed into a Forwarded: header
+
+ PROXY protocol header flow
+
+Without the PROXY protocol header, the client IP would only be reported
+accurately to the Load Balancer. |TS| would only see the connection from the
+Load Balancer. Similarly, the Web Server would only see the connection from
+|TS|. In the example above, if the client initiated a TLS connection, the Web
+Server would see the connection originating from |TS| at ``10.0.0.2``:
+
+.. code-block:: lua
+
+ Forwarded: for=10.0.0.2;by=10.0.0.1;proto=https;host=test000001.com
+
+If the Load Balancer has the Proxy Protocol enabled, requests sent through the
+Load Balancer will be preceded with the PROXY header. |TS| will detect the
+PROXY header and transform that into the Forwarded: HTTP header if configured to
+insert the Forwarded: header with the ``for`` paramter. In the example above,
+if the client initiated a TLS connection, the Web Server can use the Forwarded:
+header to determine the TLS connection originated from the client at ``192.168.1.100``:
+
+.. code-block:: lua
+
+ Forwarded: for=192.168.2.100;by=10.0.0.2;proto=https;host=test000001.com
+
+
+References
+==========
+
+- `The PROXY protocol Versions 1 & 2
+ `_
+
+- `Forwarded HTTP Extension
+ `_
diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst
index 93684d6966e..f4212923b2b 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -608,6 +608,7 @@ HTTP Engine
ip-out Value Local outbound IP address.
ip-resolve Value IP address resolution style.
proto Value List of supported session protocols.
+ pp Enable Proxy Protocol.
ssl SSL terminated.
tr-full Fully transparent (inbound and outbound)
tr-in Inbound transparent.
@@ -643,6 +644,12 @@ proto
all available protocols. For non-TLS proxy ports the default is HTTP
only.
+pp
+ Enables Proxy Protocol on the port. If Proxy Protocol is enabled on the
+ port, all incoming requests must be prefaced with the PROXY header. See
+ :ref:`Proxy Protocol ` for more details on how to configure
+ this option properly.
+
tr-full
Fully transparent. This is a convenience option and is identical to specifying both ``tr-in`` and ``tr-out``.
@@ -1600,6 +1607,30 @@ Proxy User Variables
is prohibited by RFC 7239. Currently, for the ``host`` parameter to provide the original host from the
incoming client request, `proxy.config.url_remap.pristine_host_hdr`_ must be enabled.
+.. ts:cv:: CONFIG proxy.config.http.proxy_protocol_whitelist STRING ``````
+
+ This defines a whitelist of server IPs that are trusted to provide
+ connections with Proxy Protocol information. This is a comma delimited list
+ of IP addresses. Addressed may be listed individually, in a range separated
+ by a dash or by using CIDR notation.
+
+ ======================= ===========================================================
+ Example Effect
+ ======================= ===========================================================
+ ``10.0.2.123`` A single IP Address.
+ ``10.0.3.1-10.0.3.254`` A range of IP address.
+ ``10.0.4.0/24`` A range of IP address specified by CIDR notation.
+ ======================= ============================================================
+
+ .. important::
+
+ If Proxy Protocol is enabled on the port, but this directive is not
+ defined any server may initiate a connection with Proxy Protocol
+ information.
+ See :ts:cv:`proxy.config.http.server_ports` for information on how to enable Proxy Protocol on a port.
+
+ See :ref:`proxy-protocol` for more discussion on how |TS| tranforms the `Forwarded: header.
+
.. ts:cv:: CONFIG proxy.config.http.normalize_ae INT 1
:reloadable:
:overridable:
@@ -2501,7 +2532,7 @@ HostDB
Set the file path for an external host file.
If this is set (non-empty) then the file is presumed to be a hosts file in
- the standard `host file format `_.
+ the standard .
It is read and the entries there added to the HostDB. The file is
periodically checked for a more recent modification date in which case it is
reloaded. The interval is set with :ts:cv:`proxy.config.hostdb.host_file.interval`.
diff --git a/doc/static/images/admin/proxy-protocol.png b/doc/static/images/admin/proxy-protocol.png
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/iocore/net/Connection.cc b/iocore/net/Connection.cc
index e594763be10..9d8e136c14b 100644
--- a/iocore/net/Connection.cc
+++ b/iocore/net/Connection.cc
@@ -241,6 +241,10 @@ Server::setup_fd_for_listen(bool non_blocking, const NetProcessor::AcceptOptions
#endif
}
+ if (opt.f_proxy_protocol) {
+ Debug("proxyprotocol", "Proxy Protocol enabled.");
+ }
+
#if defined(TCP_MAXSEG)
if (NetProcessor::accept_mss > 0) {
if ((res = safe_setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, (char *)&NetProcessor::accept_mss, sizeof(int))) < 0) {
diff --git a/iocore/net/I_NetProcessor.h b/iocore/net/I_NetProcessor.h
index 325a32a191f..031b96aa8d3 100644
--- a/iocore/net/I_NetProcessor.h
+++ b/iocore/net/I_NetProcessor.h
@@ -24,6 +24,7 @@
#pragma once
+#include "ts/IpMap.h"
#include "I_EventSystem.h"
#include "I_Socks.h"
struct socks_conf_struct;
@@ -96,6 +97,9 @@ class NetProcessor : public Processor
*/
bool f_inbound_transparent;
+ /// Proxy Protocol enabled
+ bool f_proxy_protocol;
+
/// Default constructor.
/// Instance is constructed with default values.
AcceptOptions() { this->reset(); }
diff --git a/iocore/net/I_NetVConnection.h b/iocore/net/I_NetVConnection.h
index 5bf51c2b427..e00a3457f34 100644
--- a/iocore/net/I_NetVConnection.h
+++ b/iocore/net/I_NetVConnection.h
@@ -33,6 +33,8 @@
#include "I_Socks.h"
#include
#include
+#include "ts/TextView.h"
+#include "ts/IpMap.h"
#define CONNECT_SUCCESS 1
#define CONNECT_FAILURE 0
@@ -129,10 +131,12 @@ struct NetVCOptions {
@see ip_family
*/
IpAddr local_ip;
+
/** Local port for connection.
Set to 0 for "don't care" (default).
- */
+ */
uint16_t local_port;
+
/// How to bind the local address.
/// @note Default is @c ANY_ADDR.
addr_bind_style addr_binding;
@@ -601,6 +605,9 @@ class NetVConnection : public AnnotatedVConnection
/** Set remote sock addr struct. */
virtual void set_remote_addr() = 0;
+ /** Set remote sock addr struct. */
+ virtual void set_remote_addr(const sockaddr *) = 0;
+
// for InkAPI
bool
get_is_internal_request() const
@@ -627,6 +634,19 @@ class NetVConnection : public AnnotatedVConnection
is_transparent = state;
}
+ /// Get the proxy protocol enabled flag
+ bool
+ get_is_proxy_protocol() const
+ {
+ return is_proxy_protocol;
+ }
+ /// Set the proxy protocol enabled flag on the port
+ void
+ set_is_proxy_protocol(bool state = true)
+ {
+ is_proxy_protocol = state;
+ }
+
virtual int
populate_protocol(std::string_view *results, int n) const
{
@@ -643,6 +663,113 @@ class NetVConnection : public AnnotatedVConnection
NetVConnection(const NetVConnection &) = delete;
NetVConnection &operator=(const NetVConnection &) = delete;
+ enum class ProxyProtocolVersion {
+ UNDEFINED,
+ V1,
+ V2,
+ };
+
+ enum class ProxyProtocolData {
+ UNDEFINED,
+ SRC,
+ DST,
+ };
+
+ int
+ set_proxy_protocol_addr(const ProxyProtocolData src_or_dst, ts::TextView &ip_addr_str)
+ {
+ int ret = -1;
+
+ if (src_or_dst == ProxyProtocolData::SRC) {
+ ret = ats_ip_pton(ip_addr_str, &pp_info.src_addr);
+ } else {
+ ret = ats_ip_pton(ip_addr_str, &pp_info.dst_addr);
+ }
+ return ret;
+ }
+
+ int
+ set_proxy_protocol_src_addr(ts::TextView src)
+ {
+ return set_proxy_protocol_addr(ProxyProtocolData::SRC, src);
+ }
+
+ int
+ set_proxy_protocol_dst_addr(ts::TextView src)
+ {
+ return set_proxy_protocol_addr(ProxyProtocolData::DST, src);
+ }
+
+ int
+ set_proxy_protocol_port(const ProxyProtocolData src_or_dst, in_port_t port)
+ {
+ if (src_or_dst == ProxyProtocolData::SRC) {
+ pp_info.src_addr.port() = htons(port);
+ } else {
+ pp_info.dst_addr.port() = htons(port);
+ }
+ return port;
+ }
+
+ int
+ set_proxy_protocol_src_port(in_port_t port)
+ {
+ return set_proxy_protocol_port(ProxyProtocolData::SRC, port);
+ }
+
+ int
+ set_proxy_protocol_dst_port(in_port_t port)
+ {
+ return set_proxy_protocol_port(ProxyProtocolData::DST, port);
+ }
+
+ void
+ set_proxy_protocol_version(const ProxyProtocolVersion ver)
+ {
+ pp_info.proxy_protocol_version = ver;
+ }
+
+ ProxyProtocolVersion
+ get_proxy_protocol_version()
+ {
+ return pp_info.proxy_protocol_version;
+ }
+
+ sockaddr const *get_proxy_protocol_addr(const ProxyProtocolData);
+
+ sockaddr const *
+ get_proxy_protocol_src_addr()
+ {
+ return get_proxy_protocol_addr(ProxyProtocolData::SRC);
+ }
+
+ uint16_t
+ get_proxy_protocol_src_port()
+ {
+ return ats_ip_port_host_order(this->get_proxy_protocol_addr(ProxyProtocolData::SRC));
+ }
+
+ sockaddr const *
+ get_proxy_protocol_dst_addr()
+ {
+ return get_proxy_protocol_addr(ProxyProtocolData::DST);
+ }
+
+ uint16_t
+ get_proxy_protocol_dst_port()
+ {
+ return ats_ip_port_host_order(this->get_proxy_protocol_addr(ProxyProtocolData::DST));
+ };
+
+ typedef struct _ProxyProtocol {
+ ProxyProtocolVersion proxy_protocol_version = ProxyProtocolVersion::UNDEFINED;
+ uint16_t ip_family;
+ IpEndpoint src_addr;
+ IpEndpoint dst_addr;
+ } ProxyProtocol;
+
+ ProxyProtocol pp_info;
+
protected:
IpEndpoint local_addr;
IpEndpoint remote_addr;
@@ -653,6 +780,8 @@ class NetVConnection : public AnnotatedVConnection
bool is_internal_request;
/// Set if this connection is transparent.
bool is_transparent;
+ /// Set if proxy protocol is enabled
+ bool is_proxy_protocol;
/// Set if the next write IO that empties the write buffer should generate an event.
int write_buffer_empty_event;
/// NetVConnection Context.
@@ -667,6 +796,7 @@ inline NetVConnection::NetVConnection()
got_remote_addr(false),
is_internal_request(false),
is_transparent(false),
+ is_proxy_protocol(false),
write_buffer_empty_event(0),
netvc_context(NET_VCONNECTION_UNSET)
{
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index dfb2bc9d0ec..f8e6e0bb1ad 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -131,6 +131,8 @@ libinknet_a_SOURCES = \
P_UnixNetVConnection.h \
P_UnixPollDescriptor.h \
P_UnixUDPConnection.h \
+ ProxyProtocol.h \
+ ProxyProtocol.cc \
Socks.cc \
SNIActionPerformer.cc \
SSLCertLookup.cc \
diff --git a/iocore/net/P_NetVConnection.h b/iocore/net/P_NetVConnection.h
index a81563304ca..755f35023e0 100644
--- a/iocore/net/P_NetVConnection.h
+++ b/iocore/net/P_NetVConnection.h
@@ -27,7 +27,11 @@ TS_INLINE sockaddr const *
NetVConnection::get_remote_addr()
{
if (!got_remote_addr) {
- set_remote_addr();
+ if (pp_info.proxy_protocol_version != ProxyProtocolVersion::UNDEFINED) {
+ set_remote_addr(get_proxy_protocol_src_addr());
+ } else {
+ set_remote_addr();
+ }
got_remote_addr = true;
}
return &remote_addr.sa;
@@ -81,3 +85,22 @@ NetVConnection::get_local_port()
{
return ats_ip_port_host_order(this->get_local_addr());
}
+
+TS_INLINE sockaddr const *
+NetVConnection::get_proxy_protocol_addr(const ProxyProtocolData src_or_dst)
+{
+ if (src_or_dst == ProxyProtocolData::SRC) {
+ if ((pp_info.src_addr.isValid() && pp_info.src_addr.port() != 0) ||
+ (ats_is_ip4(&pp_info.src_addr) && INADDR_ANY != ats_ip4_addr_cast(&pp_info.src_addr)) // IPv4
+ || (ats_is_ip6(&pp_info.src_addr) && !IN6_IS_ADDR_UNSPECIFIED(&pp_info.src_addr.sin6.sin6_addr))) {
+ return &pp_info.src_addr.sa;
+ }
+ } else {
+ if ((pp_info.dst_addr.isValid() && pp_info.dst_addr.port() != 0) ||
+ (ats_is_ip4(&pp_info.dst_addr) && INADDR_ANY != ats_ip4_addr_cast(&pp_info.dst_addr)) // IPv4
+ || (ats_is_ip6(&pp_info.dst_addr) && !IN6_IS_ADDR_UNSPECIFIED(&pp_info.dst_addr.sin6.sin6_addr))) {
+ return &pp_info.dst_addr.sa;
+ }
+ }
+ return nullptr;
+}
diff --git a/iocore/net/P_SSLConfig.h b/iocore/net/P_SSLConfig.h
index 8e9346d7b26..af4b78bbdd4 100644
--- a/iocore/net/P_SSLConfig.h
+++ b/iocore/net/P_SSLConfig.h
@@ -35,6 +35,7 @@
#include "ts/ink_inet.h"
#include
#include "P_SSLCertLookup.h"
+#include
struct SSLCertLookup;
struct ssl_ticket_key_block;
@@ -108,6 +109,8 @@ struct SSLConfigParams : public ConfigInfo {
static int ssl_wire_trace_percentage;
static char *ssl_wire_trace_server_name;
+ static IpMap *proxy_protocol_ipmap;
+
static init_ssl_ctx_func init_ssl_ctx_cb;
static load_ssl_file_func load_ssl_file_cb;
@@ -130,6 +133,7 @@ struct SSLConfigParams : public ConfigInfo {
void initialize();
void cleanup();
void reset();
+ void SSLConfigInit(IpMap *global);
};
/////////////////////////////////////////////////////////////
diff --git a/iocore/net/P_UnixNetVConnection.h b/iocore/net/P_UnixNetVConnection.h
index 054ed6cb5e5..b3d82f33de4 100644
--- a/iocore/net/P_UnixNetVConnection.h
+++ b/iocore/net/P_UnixNetVConnection.h
@@ -283,6 +283,7 @@ class UnixNetVConnection : public NetVConnection
void set_local_addr() override;
void set_remote_addr() override;
+ void set_remote_addr(const sockaddr *) override;
int set_tcp_init_cwnd(int init_cwnd) override;
int set_tcp_congestion_control(int side) override;
void apply_options() override;
@@ -319,6 +320,13 @@ UnixNetVConnection::set_remote_addr()
this->control_flags.set_flag(ContFlags::DEBUG_OVERRIDE, diags->test_override_ip(remote_addr));
}
+TS_INLINE void
+UnixNetVConnection::set_remote_addr(const sockaddr *new_sa)
+{
+ ats_ip_copy(&remote_addr, new_sa);
+ this->control_flags.set_flag(ContFlags::DEBUG_OVERRIDE, diags->test_override_ip(remote_addr));
+}
+
TS_INLINE void
UnixNetVConnection::set_local_addr()
{
diff --git a/iocore/net/ProxyProtocol.cc b/iocore/net/ProxyProtocol.cc
new file mode 100644
index 00000000000..c63ccf9becd
--- /dev/null
+++ b/iocore/net/ProxyProtocol.cc
@@ -0,0 +1,179 @@
+/** @file
+ *
+ * PROXY protocol definitions and parsers.
+ *
+ * @section license License
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ts/ink_assert.h"
+#include "ts/TextView.h"
+#include "ProxyProtocol.h"
+#include "I_NetVConnection.h"
+
+bool
+ssl_has_proxy_v1(NetVConnection *sslvc, char *buffer, int64_t *bytes_r)
+{
+ ts::TextView tv;
+
+ tv.assign(buffer, *bytes_r);
+
+ // Client must send at least 15 bytes to get a reasonable match.
+ if (tv.size() < PROXY_V1_CONNECTION_HEADER_LEN_MIN) {
+ Debug("proxyprotocol_v1", "ssl_has_proxy_v1: not enough recv'd");
+ return false;
+ }
+
+ // if we don't have the PROXY preface, we don't have a ProxyV1 header
+ if (0 != memcmp(PROXY_V1_CONNECTION_PREFACE, buffer, PROXY_V1_CONNECTION_PREFACE_LEN)) {
+ Debug("proxyprotocol_v1", "ssl_has_proxy_v1: failed the memcmp(%s, %s, %lu)", PROXY_V1_CONNECTION_PREFACE, buffer,
+ PROXY_V1_CONNECTION_PREFACE_LEN);
+ return false;
+ }
+
+ // Find the terminating newline
+ ts::TextView::size_type pos = tv.find('\n');
+ if (pos == tv.npos) {
+ Debug("proxyprotocol_v1", "ssl_has_proxy_v1: newline not found");
+ return false;
+ }
+
+ // Parse the TextView before moving the bytes in the buffer
+ if (!proxy_protov1_parse(sslvc, tv)) {
+ *bytes_r = -EAGAIN;
+ return false;
+ }
+ *bytes_r -= pos + 1;
+ if (*bytes_r <= 0) {
+ *bytes_r = -EAGAIN;
+ } else {
+ Debug("ssl", "Moving %" PRId64 " characters remaining in the buffer from %p to %p", *bytes_r, buffer + pos + 1, buffer);
+ memmove(buffer, buffer + pos + 1, *bytes_r);
+ }
+ return true;
+}
+
+bool
+http_has_proxy_v1(IOBufferReader *reader, NetVConnection *netvc)
+{
+ char buf[PROXY_V1_CONNECTION_HEADER_LEN_MAX + 1];
+ ts::TextView tv;
+
+ tv.assign(buf, reader->memcpy(buf, sizeof(buf), 0));
+
+ // Client must send at least 15 bytes to get a reasonable match.
+ if (tv.size() < PROXY_V1_CONNECTION_HEADER_LEN_MIN) {
+ return false;
+ }
+
+ if (0 != memcmp(PROXY_V1_CONNECTION_PREFACE, buf, PROXY_V1_CONNECTION_PREFACE_LEN)) {
+ return false;
+ }
+
+ // Find the terminating LF, which should already be in the buffer.
+ ts::TextView::size_type pos = tv.find('\n');
+ if (pos == tv.npos) { // not found, it's not a proxy protocol header.
+ return false;
+ }
+ reader->consume(pos + 1); // clear out the header.
+
+ // Now that we know we have a valid PROXY V1 preface, let's parse the
+ // remainder of the header
+
+ return proxy_protov1_parse(netvc, tv);
+}
+
+bool
+proxy_protov1_parse(NetVConnection *netvc, ts::TextView hdr)
+{
+ static const std::string_view PREFACE{PROXY_V1_CONNECTION_PREFACE, PROXY_V1_CONNECTION_PREFACE_LEN};
+ ts::TextView token;
+ in_port_t port;
+
+ // All the cases are special and sequence, might as well unroll them.
+
+ // The header should begin with the PROXY preface
+ token = hdr.split_prefix_at(' ');
+ if (0 == token.size() || token != PREFACE) {
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: header [%.*s] does not start with preface [%.*s]", static_cast(hdr.size()),
+ hdr.data(), static_cast(PREFACE.size()), PREFACE.data());
+ return false;
+ }
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = PREFACE", static_cast(token.size()), token.data());
+
+ // The INET protocol family - TCP4, TCP6 or UNKNOWN
+ token = hdr.split_prefix_at(' ');
+ if (0 == token.size()) {
+ return false;
+ }
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = INET Family", static_cast(token.size()), token.data());
+
+ // Next up is the layer 3 source address
+ // - 255.255.255.255 or ffff:f...f:ffff ffff:f...f:fff
+ token = hdr.split_prefix_at(' ');
+ if (0 == token.size()) {
+ return false;
+ }
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Source Address", static_cast(token.size()), token.data());
+ if (0 != netvc->set_proxy_protocol_src_addr(token)) {
+ return false;
+ }
+
+ // Next is the layer3 destination address
+ // - 255.255.255.255 or ffff:f...f:ffff ffff:f...f:fff
+ token = hdr.split_prefix_at(' ');
+ if (0 == token.size()) {
+ return false;
+ }
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Destination Address", static_cast(token.size()), token.data());
+ if (0 != netvc->set_proxy_protocol_dst_addr(token)) {
+ return false;
+ }
+
+ // Next is the TCP source port represented as a decimal number in the range of [0..65535] inclusive.
+ token = hdr.split_prefix_at(' ');
+ if (0 == token.size()) {
+ return false;
+ }
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Source Port", static_cast(token.size()), token.data());
+
+ if (0 == (port = ts::svtoi(token))) {
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: src port [%d] token [%.*s] failed to parse", port,
+ static_cast(token.size()), token.data());
+ return false;
+ }
+ netvc->set_proxy_protocol_src_port(port);
+
+ // Next is the TCP destination port represented as a decimal number in the range of [0..65535] inclusive.
+ // Final trailer is CR LF so split at CR.
+ token = hdr.split_prefix_at('\r');
+ if (0 == token.size()) {
+ return false;
+ }
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = Destination Port", static_cast(token.size()), token.data());
+ if (0 == (port = ts::svtoi(token))) {
+ Debug("proxyprotocol_v1", "proxy_protov1_parse: dst port [%d] token [%.*s] failed to parse", port,
+ static_cast(token.size()), token.data());
+ return false;
+ }
+ netvc->set_proxy_protocol_dst_port(port);
+
+ netvc->set_proxy_protocol_version(NetVConnection::ProxyProtocolVersion::V1);
+
+ return true;
+}
diff --git a/iocore/net/ProxyProtocol.h b/iocore/net/ProxyProtocol.h
new file mode 100644
index 00000000000..60395cee7b7
--- /dev/null
+++ b/iocore/net/ProxyProtocol.h
@@ -0,0 +1,55 @@
+/** @file
+
+ PROXY Protocol
+
+ See: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#ifndef ProxyProtocol_H_
+#define ProxyProtocol_H_
+
+#include "ts/ink_defs.h"
+#include "ts/ink_memory.h"
+#include
+#include
+#include "I_VConnection.h"
+#include "I_NetVConnection.h"
+#include "I_IOBuffer.h"
+
+// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
+
+extern bool proxy_protov1_parse(NetVConnection *, ts::TextView hdr);
+extern bool ssl_has_proxy_v1(NetVConnection *, char *, int64_t *);
+extern bool http_has_proxy_v1(IOBufferReader *, NetVConnection *);
+
+const char *const PROXY_V1_CONNECTION_PREFACE = "\x50\x52\x4F\x58\x59";
+const char *const PROXY_V2_CONNECTION_PREFACE = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02";
+
+const size_t PROXY_V1_CONNECTION_PREFACE_LEN = strlen(PROXY_V1_CONNECTION_PREFACE); // 5
+const size_t PROXY_V2_CONNECTION_PREFACE_LEN = 13;
+
+const size_t PROXY_V1_CONNECTION_HEADER_LEN_MIN = 15;
+const size_t PROXY_V2_CONNECTION_HEADER_LEN_MIN = 16;
+
+const size_t PROXY_V1_CONNECTION_HEADER_LEN_MAX = 108;
+const size_t PROXY_V2_CONNECTION_HEADER_LEN_MAX = 16;
+
+#endif /* ProxyProtocol_H_ */
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index c034cbce0d2..0da9e8bdc49 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -40,6 +40,7 @@
#include "P_SSLCertLookup.h"
#include "SSLSessionCache.h"
#include
+#include
int SSLConfig::configid = 0;
int SSLCertificateConfig::configid = 0;
@@ -57,6 +58,7 @@ size_t SSLConfigParams::session_cache_max_bucket_size = 100;
init_ssl_ctx_func SSLConfigParams::init_ssl_ctx_cb = nullptr;
load_ssl_file_func SSLConfigParams::load_ssl_file_cb = nullptr;
bool SSLConfigParams::sni_map_enable = false;
+IpMap *SSLConfigParams::proxy_protocol_ipmap = nullptr;
// TS-3534 Wiretracing for SSL Connections
int SSLConfigParams::ssl_wire_trace_enabled = 0;
@@ -88,6 +90,12 @@ SSLConfigParams::~SSLConfigParams()
ink_mutex_destroy(&ctxMapLock);
}
+void
+SSLConfigInit(IpMap *global)
+{
+ SSLConfigParams::proxy_protocol_ipmap = global;
+}
+
void
SSLConfigParams::reset()
{
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index d4317b92346..106b9e26a4e 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -33,6 +33,8 @@
#include "P_SSLClientUtils.h"
#include "P_SSLSNI.h"
#include "HttpTunnel.h"
+#include "ProxyProtocol.h"
+#include
#include
#include
@@ -380,6 +382,49 @@ SSLNetVConnection::read_raw_data()
}
NET_SUM_DYN_STAT(net_read_bytes_stat, r);
+ IpMap *pp_ipmap;
+ pp_ipmap = SSLConfigParams::proxy_protocol_ipmap;
+
+ if (this->get_is_proxy_protocol()) {
+ Debug("proxyprotocol", "[SSLNetVConnection::read_raw_data] proxy protocol is enabled on this port");
+ if (pp_ipmap->getCount() > 0) {
+ Debug("proxyprotocol",
+ "[SSLNetVConnection::read_raw_data] proxy protocol has a configured whitelist of trusted IPs - checking");
+
+ // At this point, using get_remote_addr() will return the ip of the
+ // proxy source IP, not the Proxy Protocol client ip. Since we are
+ // checking the ip of the actual source of this connection, this is
+ // what we want now.
+ void *payload = nullptr;
+ if (!pp_ipmap->contains(get_remote_addr(), &payload)) {
+ Debug("proxyprotocol",
+ "[SSLNetVConnection::read_raw_data] proxy protocol src IP is NOT in the configured whitelist of trusted IPs - "
+ "closing connection");
+ r = -ENOTCONN; // Need a quick close/exit here to refuse the connection!!!!!!!!!
+ goto proxy_protocol_bypass;
+ } else {
+ char new_host[INET6_ADDRSTRLEN];
+ Debug("proxyprotocol", "[SSLNetVConnection::read_raw_data] Source IP [%s] is in the trusted whitelist for proxy protocol",
+ ats_ip_ntop(this->get_remote_addr(), new_host, sizeof(new_host)));
+ }
+ } else {
+ Debug("proxyprotocol",
+ "[SSLNetVConnection::read_raw_data] proxy protocol DOES NOT have a configured whitelist of trusted IPs but "
+ "proxy protocol is ernabled on this port - processing all connections");
+ }
+
+ if (ssl_has_proxy_v1(this, buffer, &r)) {
+ Debug("proxyprotocol", "[SSLNetVConnection::read_raw_data] ssl has proxy_v1 header");
+ set_remote_addr(get_proxy_protocol_src_addr());
+ } else {
+ Debug("proxyprotocol",
+ "[SSLNetVConnection::read_raw_data] proxy protocol was enabled, but required header was not present in the "
+ "transaction - closing connection");
+ }
+ } // end of Proxy Protocol processing
+
+proxy_protocol_bypass:
+
if (r > 0) {
this->handShakeBuffer->fill(r);
diff --git a/iocore/net/UnixNetAccept.cc b/iocore/net/UnixNetAccept.cc
index 898896e757b..e494f5ae0bb 100644
--- a/iocore/net/UnixNetAccept.cc
+++ b/iocore/net/UnixNetAccept.cc
@@ -116,6 +116,7 @@ net_accept(NetAccept *na, void *ep, bool blockable)
vc->mutex = new_ProxyMutex();
vc->action_ = *na->action_;
vc->set_is_transparent(na->opt.f_inbound_transparent);
+ vc->set_is_proxy_protocol(na->opt.f_proxy_protocol);
vc->set_context(NET_VCONNECTION_IN);
#ifdef USE_EDGE_TRIGGER
// Set the vc as triggered and place it in the read ready queue later in case there is already data on the socket.
@@ -327,6 +328,7 @@ NetAccept::do_blocking_accept(EThread *t)
vc->mutex = new_ProxyMutex();
vc->action_ = *action_;
vc->set_is_transparent(opt.f_inbound_transparent);
+ vc->set_is_proxy_protocol(opt.f_proxy_protocol);
vc->options.packet_mark = opt.packet_mark;
vc->options.packet_tos = opt.packet_tos;
vc->options.ip_family = opt.ip_family;
@@ -481,6 +483,7 @@ NetAccept::acceptFastEvent(int event, void *ep)
vc->mutex = new_ProxyMutex();
// no need to set vc->action_
vc->set_is_transparent(opt.f_inbound_transparent);
+ vc->set_is_proxy_protocol(opt.f_proxy_protocol);
vc->options.packet_mark = opt.packet_mark;
vc->options.packet_tos = opt.packet_tos;
vc->options.ip_family = opt.ip_family;
diff --git a/iocore/net/UnixNetProcessor.cc b/iocore/net/UnixNetProcessor.cc
index 3b11c20d368..07d58e16d40 100644
--- a/iocore/net/UnixNetProcessor.cc
+++ b/iocore/net/UnixNetProcessor.cc
@@ -51,6 +51,7 @@ NetProcessor::AcceptOptions::reset()
packet_tos = 0;
tfo_queue_length = 0;
f_inbound_transparent = false;
+ f_proxy_protocol = false;
return *this;
}
@@ -123,6 +124,10 @@ UnixNetProcessor::accept_internal(Continuation *cont, int fd, AcceptOptions cons
Debug("http_tproxy", "Marked accept server %p on port %d as inbound transparent", na, opt.local_port);
}
+ if (opt.f_proxy_protocol) {
+ Debug("http_tproxy", "Marked accept server %p on port %d for proxy protocol", na, opt.local_port);
+ }
+
int should_filter_int = 0;
na->server.http_accept_filter = false;
REC_ReadConfigInteger(should_filter_int, "proxy.config.net.defer_accept");
diff --git a/lib/records/I_RecHttp.h b/lib/records/I_RecHttp.h
index 52d61fb27d5..a216f724a05 100644
--- a/lib/records/I_RecHttp.h
+++ b/lib/records/I_RecHttp.h
@@ -28,6 +28,7 @@
#include
#include
#include
+#include "ts/IpMap.h"
#include
#include
@@ -37,6 +38,11 @@ void RecHttpLoadIp(const char *name, ///< Name of value in configuration file.
IpAddr &ip6 ///< [out] Ipv6 address.
);
+/// Load up an IpMap with IP addresses from the configuration file.
+void RecHttpLoadIpMap(const char *name, ///< Name of value in configuration file.
+ IpMap &ipmap ///< [out] IpMap.
+);
+
/** A set of session protocols.
This depends on using @c SessionProtocolNameRegistry to get the indices.
*/
@@ -239,6 +245,8 @@ struct HttpProxyPort {
TransportType m_type; ///< Type of connection.
in_port_t m_port; ///< Port on which to listen.
uint8_t m_family; ///< IP address family.
+ /// True if proxy protocol is required on incoming requests.
+ bool m_proxy_protocol;
/// True if inbound connects (from client) are transparent.
bool m_inbound_transparent_p;
/// True if outbound connections (to origin servers) are transparent.
@@ -390,6 +398,7 @@ struct HttpProxyPort {
static const char *const OPT_TRANSPARENT_FULL; ///< Full transparency.
static const char *const OPT_TRANSPARENT_PASSTHROUGH; ///< Pass-through non-HTTP.
static const char *const OPT_SSL; ///< SSL (experimental)
+ static const char *const OPT_PROXY_PROTO; ///< Proxy Protocol
static const char *const OPT_PLUGIN; ///< Protocol Plugin handle (experimental)
static const char *const OPT_BLIND_TUNNEL; ///< Blind tunnel.
static const char *const OPT_COMPRESSED; ///< Compressed.
diff --git a/lib/records/RecHttp.cc b/lib/records/RecHttp.cc
index 07f1ee07ae1..abfbe7b2637 100644
--- a/lib/records/RecHttp.cc
+++ b/lib/records/RecHttp.cc
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
SessionProtocolNameRegistry globalSessionProtocolNameRegistry;
@@ -106,6 +107,30 @@ RecHttpLoadIp(const char *value_name, IpAddr &ip4, IpAddr &ip6)
}
}
+void
+RecHttpLoadIpMap(const char *value_name, IpMap &ipmap)
+{
+ char value[1024];
+ IpAddr laddr;
+ IpAddr raddr;
+ void *payload = nullptr;
+
+ if (REC_ERR_OKAY == RecGetRecordString(value_name, value, sizeof(value))) {
+ Debug("config", "RecHttpLoadIpMap: parsing the name [%s] and value [%s] to an IpMap", value_name, value);
+ Tokenizer tokens(", ");
+ int n_addrs = tokens.Initialize(value);
+ for (int i = 0; i < n_addrs; ++i) {
+ const char *val = tokens[i];
+
+ Debug("config", "RecHttpLoadIpMap: marking the value [%s] to an IpMap entry", val);
+ if (0 == ats_ip_range_parse(val, laddr, raddr)) {
+ ipmap.fill(laddr, raddr, payload);
+ }
+ }
+ }
+ Debug("config", "RecHttpLoadIpMap: parsed %zu IpMap entries", ipmap.getCount());
+}
+
const char *const HttpProxyPort::DEFAULT_VALUE = "8080";
const char *const HttpProxyPort::PORTS_CONFIG_NAME = "proxy.config.http.server_ports";
@@ -127,6 +152,7 @@ const char *const HttpProxyPort::OPT_TRANSPARENT_OUTBOUND = "tr-out";
const char *const HttpProxyPort::OPT_TRANSPARENT_FULL = "tr-full";
const char *const HttpProxyPort::OPT_TRANSPARENT_PASSTHROUGH = "tr-pass";
const char *const HttpProxyPort::OPT_SSL = "ssl";
+const char *const HttpProxyPort::OPT_PROXY_PROTO = "pp";
const char *const HttpProxyPort::OPT_PLUGIN = "plugin";
const char *const HttpProxyPort::OPT_BLIND_TUNNEL = "blind";
const char *const HttpProxyPort::OPT_COMPRESSED = "compressed";
@@ -158,6 +184,7 @@ HttpProxyPort::HttpProxyPort()
m_type(TRANSPORT_DEFAULT),
m_port(0),
m_family(AF_INET),
+ m_proxy_protocol(false),
m_inbound_transparent_p(false),
m_outbound_transparent_p(false),
m_transparent_passthrough(false)
@@ -339,6 +366,8 @@ HttpProxyPort::processOptions(const char *opts)
m_type = TRANSPORT_SSL;
} else if (0 == strcasecmp(OPT_PLUGIN, item)) {
m_type = TRANSPORT_PLUGIN;
+ } else if (0 == strcasecmp(OPT_PROXY_PROTO, item)) {
+ m_proxy_protocol = true;
} else if (0 == strcasecmp(OPT_TRANSPARENT_INBOUND, item)) {
#if TS_USE_TPROXY
m_inbound_transparent_p = true;
@@ -533,6 +562,10 @@ HttpProxyPort::print(char *out, size_t n)
return n;
}
+ if (m_proxy_protocol) {
+ zret += snprintf(out + zret, n - zret, ":%s", OPT_PROXY_PROTO);
+ }
+
if (m_outbound_transparent_p && m_inbound_transparent_p) {
zret += snprintf(out + zret, n - zret, ":%s", OPT_TRANSPARENT_FULL);
} else if (m_inbound_transparent_p) {
diff --git a/lib/ts/IpMapConf.h b/lib/ts/IpMapConf.h
index 3e96bba6ba7..7d229adf15d 100644
--- a/lib/ts/IpMapConf.h
+++ b/lib/ts/IpMapConf.h
@@ -27,6 +27,8 @@
class IpMap; // declare in name only.
+// Returns 0 if successful, error string otherwise
+int read_addr(char *line, int n, int *i, sockaddr *addr, char *err);
// Returns 0 if successful, error string otherwise
char *Load_IpMap_From_File(IpMap *map, int fd, char const *key_str);
// Returns 0 if successful, error string otherwise
diff --git a/mgmt/LocalManager.cc b/mgmt/LocalManager.cc
index 907650cf402..21b9f90b56b 100644
--- a/mgmt/LocalManager.cc
+++ b/mgmt/LocalManager.cc
@@ -1023,6 +1023,10 @@ LocalManager::bindProxyPort(HttpProxyPort &port)
mgmt_fatal(0, "[bindProxyPort] Unable to set socket options: %d : %s\n", port.m_port, strerror(errno));
}
+ if (port.m_proxy_protocol) {
+ Debug("lm", "[bindProxyPort] Proxy Protocol enabled");
+ }
+
if (port.m_inbound_transparent_p) {
#if TS_USE_TPROXY
Debug("http_tproxy", "Listen port %d inbound transparency enabled.", port.m_port);
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index b7c03f8033a..1bbb1756ab7 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -520,6 +520,8 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.http.insert_forwarded", RECD_STRING, "none", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.http.proxy_protocol_whitelist", RECD_STRING, "none", RECU_NULL, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+ ,
{RECT_CONFIG, "proxy.config.http.insert_age_in_response", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
,
{RECT_CONFIG, "proxy.config.http.enable_http_stats", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
diff --git a/proxy/PluginVC.cc b/proxy/PluginVC.cc
index 55ea62f2b28..d8df92f3d95 100644
--- a/proxy/PluginVC.cc
+++ b/proxy/PluginVC.cc
@@ -927,6 +927,12 @@ PluginVC::set_remote_addr()
}
}
+void
+PluginVC::set_remote_addr(const sockaddr * /* new_sa ATS_UNUSED */)
+{
+ return;
+}
+
int
PluginVC::set_tcp_init_cwnd(int /* init_cwnd ATS_UNUSED */)
{
diff --git a/proxy/PluginVC.h b/proxy/PluginVC.h
index 96aba0fdd74..e71b096b982 100644
--- a/proxy/PluginVC.h
+++ b/proxy/PluginVC.h
@@ -101,6 +101,7 @@ class PluginVC : public NetVConnection, public PluginIdentity
SOCKET get_socket() override;
void set_local_addr() override;
void set_remote_addr() override;
+ void set_remote_addr(const sockaddr *) override;
int set_tcp_init_cwnd(int init_cwnd) override;
int set_tcp_congestion_control(int) override;
diff --git a/proxy/ProtocolProbeSessionAccept.cc b/proxy/ProtocolProbeSessionAccept.cc
index 8d332941d6d..e9a21dc7cd0 100644
--- a/proxy/ProtocolProbeSessionAccept.cc
+++ b/proxy/ProtocolProbeSessionAccept.cc
@@ -25,6 +25,8 @@
#include "I_Machine.h"
#include "ProtocolProbeSessionAccept.h"
#include "http2/HTTP2.h"
+#include "ProxyProtocol.h"
+#include "I_NetVConnection.h"
static bool
proto_is_http2(IOBufferReader *reader)
@@ -90,6 +92,44 @@ struct ProtocolProbeTrampoline : public Continuation, public ProtocolProbeSessio
goto done;
}
+ // if proxy_protocol is enabled via port descriptor AND the src IP is in
+ // the trusted whitelist for proxy protocol, then check to see if it is
+ // present
+
+ IpMap *pp_ipmap;
+ pp_ipmap = probeParent->proxy_protocol_ipmap;
+
+ if (netvc->get_is_proxy_protocol()) {
+ Debug("proxyprotocol", "ioCompletionEvent: proxy protocol is enabled on this port");
+ if (pp_ipmap->getCount() > 0) {
+ Debug("proxyprotocol", "ioCompletionEvent: proxy protocol has a configured whitelist of trusted IPs - checking");
+ void *payload = nullptr;
+ if (!pp_ipmap->contains(netvc->get_remote_addr(), &payload)) {
+ Debug("proxyprotocol",
+ "ioCompletionEvent: proxy protocol src IP is NOT in the configured whitelist of trusted IPs - closing connection");
+ goto done;
+ } else {
+ char new_host[INET6_ADDRSTRLEN];
+ Debug("proxyprotocol", "ioCompletionEvent: Source IP [%s] is trusted in the whitelist for proxy protocol",
+ ats_ip_ntop(netvc->get_remote_addr(), new_host, sizeof(new_host)));
+ }
+ } else {
+ Debug("proxyprotocol",
+ "ioCompletionEvent: proxy protocol DOES NOT have a configured whitelist of trusted IPs but proxy protocol is "
+ "ernabled on this port - processing all connections");
+ }
+
+ if (http_has_proxy_v1(reader, netvc)) {
+ Debug("proxyprotocol", "ioCompletionEvent: http has proxy_v1 header");
+ netvc->set_remote_addr(netvc->get_proxy_protocol_src_addr());
+ } else {
+ Debug("proxyprotocol",
+ "ioCompletionEvent: proxy protocol was enabled, but required header was not present in the transaction - "
+ "closing connection");
+ goto done;
+ }
+ } // end of Proxy Protocol processing
+
if (proto_is_http2(reader)) {
key = PROTO_HTTP2;
} else {
diff --git a/proxy/ProtocolProbeSessionAccept.h b/proxy/ProtocolProbeSessionAccept.h
index 8ba84201760..c58dbe31bd2 100644
--- a/proxy/ProtocolProbeSessionAccept.h
+++ b/proxy/ProtocolProbeSessionAccept.h
@@ -53,6 +53,8 @@ class ProtocolProbeSessionAccept : public SessionAccept, public ProtocolProbeSes
ProtocolProbeSessionAccept(const ProtocolProbeSessionAccept &) = delete; // disabled
ProtocolProbeSessionAccept &operator=(const ProtocolProbeSessionAccept &) = delete; // disabled
+ IpMap *proxy_protocol_ipmap = nullptr;
+
private:
int mainEvent(int event, void *netvc) override;
diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc
index 97ea45e4cb0..eacfc25850f 100644
--- a/proxy/http/HttpConfig.cc
+++ b/proxy/http/HttpConfig.cc
@@ -908,6 +908,7 @@ register_stat_callbacks()
void
HttpConfig::startup()
{
+ extern void SSLConfigInit(IpMap * map);
http_rsb = RecAllocateRawStatBlock((int)http_stat_count);
register_stat_callbacks();
@@ -925,6 +926,8 @@ HttpConfig::startup()
RecHttpLoadIp("proxy.local.incoming_ip_to_bind", c.inbound_ip4, c.inbound_ip6);
RecHttpLoadIp("proxy.local.outgoing_ip_to_bind", c.outbound_ip4, c.outbound_ip6);
+ RecHttpLoadIpMap("proxy.config.http.proxy_protocol_whitelist", c.config_proxy_protocol_ipmap);
+ SSLConfigInit(&c.config_proxy_protocol_ipmap);
HttpEstablishStaticConfigLongLong(c.server_max_connections, "proxy.config.http.server_max_connections");
HttpEstablishStaticConfigLongLong(c.max_websocket_connections, "proxy.config.http.websocket.max_number_of_connections");
diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h
index 3518dcea90b..83106979c78 100644
--- a/proxy/http/HttpConfig.h
+++ b/proxy/http/HttpConfig.h
@@ -43,6 +43,7 @@
#include "ts/ink_platform.h"
#include "ts/ink_inet.h"
+#include "ts/IpMap.h"
#include "ts/Regex.h"
#include "string_view"
#include "ts/BufferWriter.h"
@@ -790,6 +791,8 @@ struct HttpConfigParams : public ConfigInfo {
public:
IpAddr inbound_ip4, inbound_ip6;
IpAddr outbound_ip4, outbound_ip6;
+ IpAddr proxy_protocol_ip4, proxy_protocol_ip6;
+ IpMap config_proxy_protocol_ipmap;
MgmtInt server_max_connections = 0;
MgmtInt origin_min_keep_alive_connections = 0; // TODO: This one really ought to be overridable, but difficult right now.
diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc
index ab1779161d2..2362949c328 100644
--- a/proxy/http/HttpProxyServerMain.cc
+++ b/proxy/http/HttpProxyServerMain.cc
@@ -161,6 +161,7 @@ make_net_accept_options(const HttpProxyPort *port, unsigned nthreads)
net.f_inbound_transparent = port->m_inbound_transparent_p;
net.ip_family = port->m_family;
net.local_port = port->m_port;
+ net.f_proxy_protocol = port->m_proxy_protocol;
if (port->m_inbound_ip.isValid()) {
net.local_ip = port->m_inbound_ip;
@@ -170,7 +171,6 @@ make_net_accept_options(const HttpProxyPort *port, unsigned nthreads)
net.local_ip = HttpConfig::m_master.inbound_ip4;
}
}
-
return net;
}
@@ -210,6 +210,7 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned
ProtocolProbeSessionAccept *probe = new ProtocolProbeSessionAccept();
HttpSessionAccept *http = nullptr; // don't allocate this unless it will be used.
probe->proxyPort = &port;
+ probe->proxy_protocol_ipmap = &HttpConfig::m_master.config_proxy_protocol_ipmap;
if (port.m_session_protocol_preference.intersects(HTTP_PROTOCOL_SET)) {
http = new HttpSessionAccept(accept_opt);
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index f9ee1f760ad..3eb3cf8f04e 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -5528,7 +5528,12 @@ HttpTransact::initialize_state_variables_from_request(State *s, HTTPHdr *obsolet
ats_ip_copy(&s->request_data.src_ip, &s->client_info.src_addr);
memset(&s->request_data.dest_ip, 0, sizeof(s->request_data.dest_ip));
if (vc) {
- s->request_data.incoming_port = vc->get_local_port();
+ s->request_data.incoming_port = vc->get_local_port();
+ s->pp_info.proxy_protocol_version = vc->get_proxy_protocol_version();
+ if (s->pp_info.proxy_protocol_version != NetVConnection::ProxyProtocolVersion::UNDEFINED) {
+ ats_ip_copy(s->pp_info.src_addr, vc->pp_info.src_addr);
+ ats_ip_copy(s->pp_info.dst_addr, vc->pp_info.dst_addr);
+ }
}
s->request_data.xact_start = s->client_request_time;
s->request_data.api_info = &s->api_info;
diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
index 3ad321133ce..1b0083c43b1 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -935,6 +935,9 @@ class HttpTransact
}
internal_msg_buffer_size = 0;
}
+
+ NetVConnection::ProxyProtocol pp_info;
+
}; // End of State struct.
static void HandleBlindTunnel(State *s);