trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bc...@apache.org
Subject [trafficserver] branch 8.0.x updated: PROXY Protocol transformed to Forwarded HTTP heder.
Date Fri, 31 Aug 2018 22:07:11 GMT
This is an automated email from the ASF dual-hosted git repository.

bcall pushed a commit to branch 8.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/8.0.x by this push:
     new a692053  PROXY Protocol transformed to Forwarded HTTP heder.
a692053 is described below

commit a692053ee3caf3f58e2360de3791669caee1af34
Author: Steven Feltner <sfeltner@godaddy.com>
AuthorDate: Wed Jul 11 18:29:15 2018 -0700

    PROXY Protocol transformed to Forwarded HTTP heder.
---
 doc/admin-guide/configuration/index.en.rst         |   1 +
 .../configuration/proxy-protocol.en.rst            | 100 ++++++++++++
 doc/admin-guide/files/records.config.en.rst        |  33 +++-
 doc/static/images/admin/proxy-protocol.png         |   0
 iocore/net/Connection.cc                           |   4 +
 iocore/net/I_NetProcessor.h                        |   4 +
 iocore/net/I_NetVConnection.h                      | 132 ++++++++++++++-
 iocore/net/Makefile.am                             |   2 +
 iocore/net/P_NetVConnection.h                      |  25 ++-
 iocore/net/P_SSLConfig.h                           |   4 +
 iocore/net/P_UnixNetVConnection.h                  |   8 +
 iocore/net/ProxyProtocol.cc                        | 179 +++++++++++++++++++++
 iocore/net/ProxyProtocol.h                         |  55 +++++++
 iocore/net/SSLConfig.cc                            |   8 +
 iocore/net/SSLNetVConnection.cc                    |  45 ++++++
 iocore/net/UnixNetAccept.cc                        |   3 +
 iocore/net/UnixNetProcessor.cc                     |   5 +
 lib/records/I_RecHttp.h                            |   9 ++
 lib/records/RecHttp.cc                             |  33 ++++
 lib/ts/IpMapConf.h                                 |   2 +
 mgmt/LocalManager.cc                               |   4 +
 mgmt/RecordsConfig.cc                              |   2 +
 proxy/PluginVC.cc                                  |   6 +
 proxy/PluginVC.h                                   |   1 +
 proxy/ProtocolProbeSessionAccept.cc                |  40 +++++
 proxy/ProtocolProbeSessionAccept.h                 |   2 +
 proxy/http/HttpConfig.cc                           |   3 +
 proxy/http/HttpConfig.h                            |   3 +
 proxy/http/HttpProxyServerMain.cc                  |   3 +-
 proxy/http/HttpTransact.cc                         |   7 +-
 proxy/http/HttpTransact.h                          |   3 +
 31 files changed, 721 insertions(+), 5 deletions(-)

diff --git a/doc/admin-guide/configuration/index.en.rst b/doc/admin-guide/configuration/index.en.rst
index cc2e3f6..1aecdbe 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 0000000..2d7673b
--- /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 <https://www.haproxy.com/blog/haproxy/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:
+<https://tools.ietf.org/html/rfc7239>`_ 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
+   <https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt>`_
+
+-  `Forwarded HTTP Extension
+   <https://tools.ietf.org/html/rfc7239#page-6>`_
diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst
index ef7dd5f..57d6e5b 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 <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``.
 
@@ -1607,6 +1614,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 ```<ip list>```
+
+   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:
@@ -2508,7 +2539,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 <http://tools.ietf.org/html/rfc1123#page-13>`_.
+   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 0000000..e69de29
diff --git a/iocore/net/Connection.cc b/iocore/net/Connection.cc
index e594763..9d8e136 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 325a32a..031b96a 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 @@ public:
     */
     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 5bf51c2..e00a345 100644
--- a/iocore/net/I_NetVConnection.h
+++ b/iocore/net/I_NetVConnection.h
@@ -33,6 +33,8 @@
 #include "I_Socks.h"
 #include <ts/apidefs.h>
 #include <string_view>
+#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 @@ public:
   /** 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 @@ public:
     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 @@ public:
   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 @@ protected:
   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 dfb2bc9..f8e6e0b 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 a815633..755f350 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 f4cde7a..9fbb07e 100644
--- a/iocore/net/P_SSLConfig.h
+++ b/iocore/net/P_SSLConfig.h
@@ -35,6 +35,7 @@
 #include "ts/ink_inet.h"
 #include <openssl/rand.h>
 #include "P_SSLCertLookup.h"
+#include <ts/IpMap.h>
 
 struct SSLCertLookup;
 struct ssl_ticket_key_block;
@@ -111,6 +112,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;
 
@@ -133,6 +136,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 054ed6c..b3d82f3 100644
--- a/iocore/net/P_UnixNetVConnection.h
+++ b/iocore/net/P_UnixNetVConnection.h
@@ -283,6 +283,7 @@ public:
 
   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;
@@ -320,6 +321,13 @@ UnixNetVConnection::set_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()
 {
   int local_sa_size = sizeof(local_addr);
diff --git a/iocore/net/ProxyProtocol.cc b/iocore/net/ProxyProtocol.cc
new file mode 100644
index 0000000..c63ccf9
--- /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<int>(hdr.size()),
+          hdr.data(), static_cast<int>(PREFACE.size()), PREFACE.data());
+    return false;
+  }
+  Debug("proxyprotocol_v1", "proxy_protov1_parse: [%.*s] = PREFACE", static_cast<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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 0000000..60395ce
--- /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 <ts/ink_resolver.h>
+#include <ts/ink_platform.h>
+#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 4b87f05..9cb6bb2 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -40,6 +40,7 @@
 #include "P_SSLCertLookup.h"
 #include "SSLSessionCache.h"
 #include <records/I_RecHttp.h>
+#include <HttpConfig.h>
 
 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;
@@ -89,6 +91,12 @@ SSLConfigParams::~SSLConfigParams()
 }
 
 void
+SSLConfigInit(IpMap *global)
+{
+  SSLConfigParams::proxy_protocol_ipmap = global;
+}
+
+void
 SSLConfigParams::reset()
 {
   serverCertPathOnly = serverCertChainFilename = configFilePath = serverCACertFilename = serverCACertPath = clientCertPath =
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index eb78db2..f6944a4 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 <HttpConfig.h>
 
 #include <climits>
 #include <string>
@@ -381,6 +383,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 898896e..e494f5a 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 3b11c20..07d58e1 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 52d61fb..a216f72 100644
--- a/lib/records/I_RecHttp.h
+++ b/lib/records/I_RecHttp.h
@@ -28,6 +28,7 @@
 #include <ts/apidefs.h>
 #include <ts/apidefs.h>
 #include <ts/ink_assert.h>
+#include "ts/IpMap.h"
 #include <algorithm>
 #include <vector>
 
@@ -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 @@ public:
   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 @@ public:
   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 07f1ee0..abfbe7b 100644
--- a/lib/records/RecHttp.cc
+++ b/lib/records/RecHttp.cc
@@ -29,6 +29,7 @@
 #include <strings.h>
 #include <ts/ink_inet.h>
 #include <string_view>
+#include <ts/IpMapConf.h>
 
 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 3e96bba..7d229ad 100644
--- a/lib/ts/IpMapConf.h
+++ b/lib/ts/IpMapConf.h
@@ -28,6 +28,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
 char *Load_IpMap_From_File(IpMap *map, FILE *f, char const *key_str);
diff --git a/mgmt/LocalManager.cc b/mgmt/LocalManager.cc
index 8212a73..441a303 100644
--- a/mgmt/LocalManager.cc
+++ b/mgmt/LocalManager.cc
@@ -1033,6 +1033,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 ac4cd3b..88d1482 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -523,6 +523,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 3c99653..7f5db48 100644
--- a/proxy/PluginVC.cc
+++ b/proxy/PluginVC.cc
@@ -936,6 +936,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 7ce9a85..c8519e2 100644
--- a/proxy/PluginVC.h
+++ b/proxy/PluginVC.h
@@ -101,6 +101,7 @@ public:
   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 8d33294..e9a21dc 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 8ba8420..c58dbe3 100644
--- a/proxy/ProtocolProbeSessionAccept.h
+++ b/proxy/ProtocolProbeSessionAccept.h
@@ -53,6 +53,8 @@ public:
   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 8e40cbc..061802d 100644
--- a/proxy/http/HttpConfig.cc
+++ b/proxy/http/HttpConfig.cc
@@ -958,6 +958,7 @@ load_negative_caching_var(RecRecord const *r, void *cookie)
 void
 HttpConfig::startup()
 {
+  extern void SSLConfigInit(IpMap * map);
   http_rsb = RecAllocateRawStatBlock((int)http_stat_count);
   register_stat_callbacks();
 
@@ -975,6 +976,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 3057f58..644e586 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"
@@ -793,6 +794,8 @@ public:
 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 ab17791..2362949 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 c8bb547..6dce383 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -5520,7 +5520,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 3cae78f..9feebe5 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -919,6 +919,9 @@ public:
       }
       internal_msg_buffer_size = 0;
     }
+
+    NetVConnection::ProxyProtocol pp_info;
+
   }; // End of State struct.
 
   static void HandleBlindTunnel(State *s);


Mime
View raw message