From 1391a664454d74c08be3da44e481da23395d3adf Mon Sep 17 00:00:00 2001 From: Chris McFarlen Date: Mon, 15 Dec 2025 20:38:21 -0600 Subject: [PATCH 1/2] Restore old feature to copy PP src IP to remote ip of NetVConn, but this time the copy is configurable. Expand autest to cover new setup Introduce get_client_addr to NetVConn, implement rchi log field, improve proxy protocol autests. --- .../configuration/proxy-protocol.en.rst | 3 ++ doc/admin-guide/files/records.yaml.en.rst | 1 + doc/admin-guide/logging/formatting.en.rst | 8 +-- include/iocore/net/AcceptOptions.h | 3 +- include/iocore/net/NetVConnection.h | 51 +++++++++++++++++-- include/proxy/ProxySession.h | 3 ++ include/proxy/ProxyTransaction.h | 17 +++++++ include/proxy/logging/LogAccess.h | 2 + include/records/RecHttp.h | 41 ++++++++------- include/ts/ts.h | 1 + src/api/InkAPI.cc | 10 +++- src/iocore/net/Server.cc | 6 ++- src/iocore/net/UnixNetAccept.cc | 6 +-- src/proxy/ProxySession.cc | 13 +++++ src/proxy/http/HttpProxyServerMain.cc | 11 ++-- src/proxy/logging/Log.cc | 15 ++++++ src/proxy/logging/LogAccess.cc | 30 ++++++++++- src/records/RecHttp.cc | 35 ++++++++----- .../autest-site/trafficserver.test.ext | 12 +++-- .../proxy_protocol/gold/access-cp.gold | 7 +++ .../proxy_protocol/gold/access-nocp.gold | 7 +++ .../proxy_protocol/gold/access.gold | 7 --- .../proxy_protocol/proxy_protocol.test.py | 35 ++++++++----- 23 files changed, 247 insertions(+), 77 deletions(-) create mode 100644 tests/gold_tests/proxy_protocol/gold/access-cp.gold create mode 100644 tests/gold_tests/proxy_protocol/gold/access-nocp.gold delete mode 100644 tests/gold_tests/proxy_protocol/gold/access.gold diff --git a/doc/admin-guide/configuration/proxy-protocol.en.rst b/doc/admin-guide/configuration/proxy-protocol.en.rst index 9aea649e351..4263d9e7717 100644 --- a/doc/admin-guide/configuration/proxy-protocol.en.rst +++ b/doc/admin-guide/configuration/proxy-protocol.en.rst @@ -56,6 +56,9 @@ By default, |TS| uses client's IP address that is from the peer when it applies enable PROXY protocol and want to apply ACL against the IP address delivered by PROXY protocol, you need to have ``PROXY`` in :ts:cv:`proxy.config.acl.subjects`. +If you specify the server_ports flag `pp-clnt` then the client IP address used for the +transaction will be the one provided by proxy protocol. + 1. HTTP Forwarded Header The client IP address in the PROXY protocol header is passed to the origin server via an HTTP `Forwarded: diff --git a/doc/admin-guide/files/records.yaml.en.rst b/doc/admin-guide/files/records.yaml.en.rst index 360cfc5fc0d..2221d4ceeed 100644 --- a/doc/admin-guide/files/records.yaml.en.rst +++ b/doc/admin-guide/files/records.yaml.en.rst @@ -786,6 +786,7 @@ HTTP Engine ip-resolve Value IP address resolution style. proto Value List of supported session protocols. pp Enable Proxy Protocol. + pp-clnt Use the Proxy Protocol SRC IP as the client IP. ssl SSL terminated. quic QUIC terminated. tr-full Fully transparent (inbound and outbound) diff --git a/doc/admin-guide/logging/formatting.en.rst b/doc/admin-guide/logging/formatting.en.rst index 3df372979fb..bac1fbeeb68 100644 --- a/doc/admin-guide/logging/formatting.en.rst +++ b/doc/admin-guide/logging/formatting.en.rst @@ -499,16 +499,19 @@ incoming/outgoing ports, and network interfaces used during transactions. Field Source Description ===== ============== ========================================================== chi Client IP address of the client's host. If :ref:`Proxy Protocol ` - is used, this represents the IP address of the peer, rather than - the client IP behind the peer. + is configured with the pp-clnt flag, this represents the proxy protocol SRC IP address, otherwise + the IP is that of the connected peer. chih Client IP address of the client's host, in hexadecimal. chiv Client IP address of the client's host verified by a plugin. If not available, ``chi`` is used. +rchi Remote Client This is alway the IP address of the inbound remote peer. +rchh Remote Client The IP address of the inbound remote peer in hexadecimal. hii Proxy IP address for the proxy's incoming interface (to which the client connected). hiih Proxy IP address for the proxy's incoming interface (to which the client connected), in hexadecimal. chp Client Port number of the client's host. +rchp Remote Client Port number of the inbound remote peer. php Proxy Response TCP port number from which |TS| serviced the request. pqsi Proxy Request IP address from which |TS| issued the proxy request to the origin server. Cache hits will result in a value of ``0``. @@ -529,7 +532,6 @@ ppd Proxy Protocol Destination IP received via Proxy Protocol context from the Dest IP to the |TS| ppa Proxy Protocol The Authority TLV from Proxy Protocol context from the LB Authority to the |TS| - ===== ============== ========================================================== .. note:: diff --git a/include/iocore/net/AcceptOptions.h b/include/iocore/net/AcceptOptions.h index ed3c1d727d6..53000273412 100644 --- a/include/iocore/net/AcceptOptions.h +++ b/include/iocore/net/AcceptOptions.h @@ -85,5 +85,6 @@ struct AcceptOptions { bool f_mptcp = false; /// Proxy Protocol enabled - bool f_proxy_protocol = false; + bool f_proxy_protocol = false; + bool f_proxy_protocol_client_src = false; }; diff --git a/include/iocore/net/NetVConnection.h b/include/iocore/net/NetVConnection.h index 9f3178fa75f..df02a766692 100644 --- a/include/iocore/net/NetVConnection.h +++ b/include/iocore/net/NetVConnection.h @@ -26,6 +26,7 @@ #include "iocore/net/NetVCOptions.h" #include "iocore/net/ProxyProtocol.h" +#include #include #include @@ -282,6 +283,12 @@ class NetVConnection : public VConnection, public PluginUserArgs mptcp_state; /// Set if the next write IO that empties the write buffer should generate an event. @@ -693,6 +709,33 @@ NetVConnection::_set_service(QUICSupport *instance) this->_set_service(NetVConnection::Service::QUIC, instance); } +inline sockaddr const * +NetVConnection::get_client_addr() +{ + if (pp_info.version != ProxyProtocolVersion::UNDEFINED && is_proxy_protocol_cp_src) { + return get_proxy_protocol_src_addr(); + } else { + return get_remote_addr(); + } +} + +inline IpEndpoint const & +NetVConnection::get_client_endpoint() +{ + if (pp_info.version != ProxyProtocolVersion::UNDEFINED && is_proxy_protocol_cp_src) { + return pp_info.src_addr; + } else { + return remote_addr; + } +} + +/// @return The remote port in host order. +inline uint16_t +NetVConnection::get_client_port() +{ + return ats_ip_port_host_order(this->get_client_addr()); +} + inline sockaddr const * NetVConnection::get_remote_addr() { diff --git a/include/proxy/ProxySession.h b/include/proxy/ProxySession.h index d3267045de6..a94bdf4dd45 100644 --- a/include/proxy/ProxySession.h +++ b/include/proxy/ProxySession.h @@ -23,6 +23,7 @@ #pragma once +#include "tscore/ink_inet.h" #include "tscore/ink_platform.h" #include "tscore/ink_resolver.h" #include "tscore/TSSystemState.h" @@ -116,6 +117,8 @@ class ProxySession : public VConnection, public PluginUserArgs virtual uint64_t get_received_frame_count(uint64_t type) const; // Replicate NetVConnection API + virtual sockaddr const *get_client_addr() const; + virtual uint16_t get_client_port() const; virtual sockaddr const *get_remote_addr() const; virtual sockaddr const *get_local_addr(); diff --git a/include/proxy/ProxyTransaction.h b/include/proxy/ProxyTransaction.h index 8238b1c5c91..c81f73be981 100644 --- a/include/proxy/ProxyTransaction.h +++ b/include/proxy/ProxyTransaction.h @@ -24,6 +24,7 @@ #pragma once #include "proxy/ProxySession.h" +#include #include class HttpSM; @@ -131,6 +132,10 @@ class ProxyTransaction : public VConnection void set_verified_client_addr(const sockaddr *addr); sockaddr const *get_verified_client_addr() const; + // this is the client address for the transaction, which may not be the same as the connection remote address + sockaddr const *get_client_addr() const; + uint16_t get_client_port() const; + // This function must return a non-negative number that is different for two in-progress transactions with the same proxy_ssn // session. // @@ -336,6 +341,18 @@ ProxyTransaction::get_verified_client_addr() const return reinterpret_cast(&_verified_addr); } +inline sockaddr const * +ProxyTransaction::get_client_addr() const +{ + return _proxy_ssn ? _proxy_ssn->get_client_addr() : nullptr; +} + +inline uint16_t +ProxyTransaction::get_client_port() const +{ + return _proxy_ssn ? _proxy_ssn->get_client_port() : 0; +} + inline void ProxyTransaction::set_verified_client_addr(const sockaddr *addr) { diff --git a/include/proxy/logging/LogAccess.h b/include/proxy/logging/LogAccess.h index b71c91ae883..0cdd6e1098c 100644 --- a/include/proxy/logging/LogAccess.h +++ b/include/proxy/logging/LogAccess.h @@ -126,6 +126,8 @@ class LogAccess int marshal_host_interface_ip(char *); // STR int marshal_client_host_ip_verified(char *); // STR int marshal_client_host_port(char *); // INT + int marshal_remote_host_ip(char *); // STR + int marshal_remote_host_port(char *); // STR int marshal_client_auth_user_name(char *); // STR int marshal_client_req_timestamp_sec(char *); // INT int marshal_client_req_timestamp_ms(char *); // INT diff --git a/include/records/RecHttp.h b/include/records/RecHttp.h index 3efa034bacd..6f6b24c3283 100644 --- a/include/records/RecHttp.h +++ b/include/records/RecHttp.h @@ -265,6 +265,8 @@ struct HttpProxyPort { uint8_t m_family = AF_INET; ///< IP address family. /// True if proxy protocol is required on incoming requests. bool m_proxy_protocol = false; + /// True if the port should use the proxy protocol src address as the connection remote address + bool m_proxy_protocol_client_src = false; /// True if inbound connects (from client) are transparent. bool m_inbound_transparent_p = false; /// True if outbound connections (to origin servers) are transparent. @@ -415,25 +417,26 @@ struct HttpProxyPort { static const char *const DEFAULT_VALUE; // Keywords (lower case versions, but compares should be case insensitive) - static const char *const OPT_FD_PREFIX; ///< Prefix for file descriptor value. - static const char *const OPT_OUTBOUND_IP_PREFIX; ///< Prefix for inbound IP address. - static const char *const OPT_INBOUND_IP_PREFIX; ///< Prefix for outbound IP address. - static const char *const OPT_IPV6; ///< IPv6. - static const char *const OPT_IPV4; ///< IPv4 - static const char *const OPT_TRANSPARENT_INBOUND; ///< Inbound transparent. - static const char *const OPT_TRANSPARENT_OUTBOUND; ///< Outbound transparent. - 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_ALLOW_PLAIN; ///< Backup to plain HTTP. - static const char *const OPT_SSL; ///< SSL (experimental) - static const char *const OPT_QUIC; ///< QUIC (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. - static const char *const OPT_HOST_RES_PREFIX; ///< Set DNS family preference. - static const char *const OPT_PROTO_PREFIX; ///< Transport layer protocols. - static const char *const OPT_MPTCP; ///< MPTCP. + static const char *const OPT_FD_PREFIX; ///< Prefix for file descriptor value. + static const char *const OPT_OUTBOUND_IP_PREFIX; ///< Prefix for inbound IP address. + static const char *const OPT_INBOUND_IP_PREFIX; ///< Prefix for outbound IP address. + static const char *const OPT_IPV6; ///< IPv6. + static const char *const OPT_IPV4; ///< IPv4 + static const char *const OPT_TRANSPARENT_INBOUND; ///< Inbound transparent. + static const char *const OPT_TRANSPARENT_OUTBOUND; ///< Outbound transparent. + 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_ALLOW_PLAIN; ///< Backup to plain HTTP. + static const char *const OPT_SSL; ///< SSL (experimental) + static const char *const OPT_QUIC; ///< QUIC (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. + static const char *const OPT_HOST_RES_PREFIX; ///< Set DNS family preference. + static const char *const OPT_PROTO_PREFIX; ///< Transport layer protocols. + static const char *const OPT_MPTCP; ///< MPTCP. + static const char *const OPT_PROXY_PROTO_CLIENT_SRC_IP; ///< The Proxy protocol SRC IP address is used as the client's IP address static std::vector &m_global; ///< Global ("default") data. diff --git a/include/ts/ts.h b/include/ts/ts.h index 3227f1cf18d..d62d5d81561 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -2036,6 +2036,7 @@ TSVConn TSTransformOutputVConnGet(TSVConn connp); /* -------------------------------------------------------------------------- Net VConnections */ struct sockaddr const *TSNetVConnRemoteAddrGet(TSVConn vc); +struct sockaddr const *TSNetVConnClientAddrGet(TSVConn vc); /** Opens a network connection to the host specified by ip on the port diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc index cabfb5309d6..efa24ff8667 100644 --- a/src/api/InkAPI.cc +++ b/src/api/InkAPI.cc @@ -4692,7 +4692,7 @@ TSHttpSsnClientAddrGet(TSHttpSsn ssnp) if (cs == nullptr) { return nullptr; } - return cs->get_remote_addr(); + return cs->get_client_addr(); } sockaddr const * TSHttpTxnClientAddrGet(TSHttpTxn txnp) @@ -6168,6 +6168,14 @@ TSNetVConnRemoteAddrGet(TSVConn connp) return vc->get_remote_addr(); } +sockaddr const * +TSNetVConnClientAddrGet(TSVConn connp) +{ + sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); + NetVConnection *vc = reinterpret_cast(connp); + return vc->get_client_addr(); +} + TSAction TSNetConnect(TSCont contp, sockaddr const *addr) { diff --git a/src/iocore/net/Server.cc b/src/iocore/net/Server.cc index 3cd85df3a08..fff587df2b3 100644 --- a/src/iocore/net/Server.cc +++ b/src/iocore/net/Server.cc @@ -291,7 +291,11 @@ Server::setup_fd_for_listen(bool non_blocking, const NetProcessor::AcceptOptions } if (opt.f_proxy_protocol) { - Dbg(dbg_ctl_proxyprotocol, "Proxy Protocol enabled."); + if (opt.f_proxy_protocol_client_src) { + Dbg(dbg_ctl_proxyprotocol, "Proxy Protocol enabled(Using src IP as remote IP)."); + } else { + Dbg(dbg_ctl_proxyprotocol, "Proxy Protocol enabled."); + } } #if defined(TCP_MAXSEG) diff --git a/src/iocore/net/UnixNetAccept.cc b/src/iocore/net/UnixNetAccept.cc index 94a88648542..cea9df78792 100644 --- a/src/iocore/net/UnixNetAccept.cc +++ b/src/iocore/net/UnixNetAccept.cc @@ -145,7 +145,7 @@ net_accept(NetAccept *na, void *ep, bool blockable) vc->submit_time = ink_get_hrtime(); 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_is_proxy_protocol(na->opt.f_proxy_protocol, na->opt.f_proxy_protocol_client_src); vc->set_context(NET_VCONNECTION_IN); if (na->opt.f_mptcp) { vc->set_mptcp_state(); // Try to get the MPTCP state, and update accordingly @@ -432,7 +432,7 @@ NetAccept::do_blocking_accept(EThread *t) vc->submit_time = ink_get_hrtime(); vc->action_ = *action_; vc->set_is_transparent(opt.f_inbound_transparent); - vc->set_is_proxy_protocol(opt.f_proxy_protocol); + vc->set_is_proxy_protocol(opt.f_proxy_protocol, opt.f_proxy_protocol_client_src); vc->options.sockopt_flags = opt.sockopt_flags; vc->options.packet_mark = opt.packet_mark; vc->options.packet_tos = opt.packet_tos; @@ -598,7 +598,7 @@ NetAccept::acceptFastEvent(int event, void *ep) vc->submit_time = ink_get_hrtime(); vc->action_ = *action_; vc->set_is_transparent(opt.f_inbound_transparent); - vc->set_is_proxy_protocol(opt.f_proxy_protocol); + vc->set_is_proxy_protocol(opt.f_proxy_protocol, opt.f_proxy_protocol_client_src); vc->options.sockopt_flags = opt.sockopt_flags; vc->options.packet_mark = opt.packet_mark; vc->options.packet_tos = opt.packet_tos; diff --git a/src/proxy/ProxySession.cc b/src/proxy/ProxySession.cc index fd1620b4095..c310fbeeca4 100644 --- a/src/proxy/ProxySession.cc +++ b/src/proxy/ProxySession.cc @@ -26,6 +26,7 @@ #include "proxy/ProxySession.h" #include "iocore/net/TLSBasicSupport.h" #include "private/SSLProxySession.h" +#include std::map> ProtocolSessionCreateMap; @@ -286,6 +287,18 @@ ProxySession::get_version(HTTPHdr &hdr) const return hdr.version_get(); } +sockaddr const * +ProxySession::get_client_addr() const +{ + return _vc ? _vc->get_client_addr() : nullptr; +} + +uint16_t +ProxySession::get_client_port() const +{ + return _vc ? _vc->get_client_endpoint().host_order_port() : 0; +} + sockaddr const * ProxySession::get_remote_addr() const { diff --git a/src/proxy/http/HttpProxyServerMain.cc b/src/proxy/http/HttpProxyServerMain.cc index d726937ee77..3f9dc8e984f 100644 --- a/src/proxy/http/HttpProxyServerMain.cc +++ b/src/proxy/http/HttpProxyServerMain.cc @@ -143,11 +143,12 @@ make_net_accept_options(const HttpProxyPort *port, unsigned nthreads) #endif if (port) { - net.f_inbound_transparent = port->m_inbound_transparent_p; - net.f_mptcp = port->m_mptcp; - net.ip_family = port->m_family; - net.local_port = port->m_port; - net.f_proxy_protocol = port->m_proxy_protocol; + net.f_inbound_transparent = port->m_inbound_transparent_p; + net.f_mptcp = port->m_mptcp; + net.ip_family = port->m_family; + net.local_port = port->m_port; + net.f_proxy_protocol = port->m_proxy_protocol; + net.f_proxy_protocol_client_src = port->m_proxy_protocol_client_src; if (port->m_inbound_ip.isValid()) { net.local_ip = port->m_inbound_ip; diff --git a/src/proxy/logging/Log.cc b/src/proxy/logging/Log.cc index ca56f27c5ac..35d2c871874 100644 --- a/src/proxy/logging/Log.cc +++ b/src/proxy/logging/Log.cc @@ -347,6 +347,21 @@ Log::init_fields() global_field_list.add(field, false); field_symbol_hash.emplace("chiv", field); + // remote client (Not necessarily the requesting client IP - See proxy protocol) + field = new LogField("remote_host_ip", "rchi", LogField::IP, &LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_str); + global_field_list.add(field, false); + field_symbol_hash.emplace("rchi", field); + + field = new LogField("remote_host_port", "rchp", LogField::sINT, &LogAccess::marshal_remote_host_port, + &LogAccess::unmarshal_int_to_str); + global_field_list.add(field, false); + field_symbol_hash.emplace("rchp", field); + + field = + new LogField("remote_host_ip_hex", "rchh", LogField::IP, &LogAccess::marshal_remote_host_ip, &LogAccess::unmarshal_ip_to_hex); + global_field_list.add(field, false); + field_symbol_hash.emplace("rchh", field); + // interface ip field = diff --git a/src/proxy/logging/LogAccess.cc b/src/proxy/logging/LogAccess.cc index 50991eada35..0754be1c215 100644 --- a/src/proxy/logging/LogAccess.cc +++ b/src/proxy/logging/LogAccess.cc @@ -1441,6 +1441,21 @@ LogAccess::marshal_plugin_identity_tag(char *buf) int LogAccess::marshal_client_host_ip(char *buf) +{ + if (m_http_sm) { + auto txn = m_http_sm->get_ua_txn(); + if (txn) { + sockaddr const *addr = txn->get_client_addr(); + if (addr && ats_is_ip(addr)) { + return marshal_ip(buf, addr); + } + } + } + return INK_MIN_ALIGN; +} + +int +LogAccess::marshal_remote_host_ip(char *buf) { return marshal_ip(buf, &m_http_sm->t_state.client_info.src_addr.sa); } @@ -1463,7 +1478,7 @@ LogAccess::marshal_client_host_ip_verified(char *buf) } } } - return marshal_ip(buf, &m_http_sm->t_state.client_info.src_addr.sa); + return marshal_client_host_ip(buf); } /*------------------------------------------------------------------------- @@ -1658,6 +1673,19 @@ LogAccess::marshal_proxy_protocol_authority(char *buf) -------------------------------------------------------------------------*/ int LogAccess::marshal_client_host_port(char *buf) +{ + if (m_http_sm) { + auto txn = m_http_sm->get_ua_txn(); + if (txn) { + uint16_t port = txn->get_client_port(); + marshal_int(buf, port); + } + } + return INK_MIN_ALIGN; +} + +int +LogAccess::marshal_remote_host_port(char *buf) { if (buf) { uint16_t port = m_http_sm->t_state.client_info.src_addr.host_order_port(); diff --git a/src/records/RecHttp.cc b/src/records/RecHttp.cc index 64a828b73f9..57cb822c994 100644 --- a/src/records/RecHttp.cc +++ b/src/records/RecHttp.cc @@ -182,20 +182,21 @@ const char *const HttpProxyPort::OPT_INBOUND_IP_PREFIX = "ip-in"; const char *const HttpProxyPort::OPT_HOST_RES_PREFIX = "ip-resolve"; const char *const HttpProxyPort::OPT_PROTO_PREFIX = "proto"; -const char *const HttpProxyPort::OPT_IPV6 = "ipv6"; -const char *const HttpProxyPort::OPT_IPV4 = "ipv4"; -const char *const HttpProxyPort::OPT_TRANSPARENT_INBOUND = "tr-in"; -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_ALLOW_PLAIN = "allow-plain"; -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"; -const char *const HttpProxyPort::OPT_MPTCP = "mptcp"; -const char *const HttpProxyPort::OPT_QUIC = "quic"; +const char *const HttpProxyPort::OPT_IPV6 = "ipv6"; +const char *const HttpProxyPort::OPT_IPV4 = "ipv4"; +const char *const HttpProxyPort::OPT_TRANSPARENT_INBOUND = "tr-in"; +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_ALLOW_PLAIN = "allow-plain"; +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"; +const char *const HttpProxyPort::OPT_MPTCP = "mptcp"; +const char *const HttpProxyPort::OPT_QUIC = "quic"; +const char *const HttpProxyPort::OPT_PROXY_PROTO_CLIENT_SRC_IP = "pp-clnt"; // File local constants. namespace @@ -465,6 +466,8 @@ HttpProxyPort::processOptions(const char *opts) } else { Warning("Multipath TCP requested [%s] in port descriptor '%s' but it is not supported by this host.", item, opts); } + } else if (0 == strcasecmp(OPT_PROXY_PROTO_CLIENT_SRC_IP, item)) { + m_proxy_protocol_client_src = true; } else if (nullptr != (value = this->checkPrefix(item, OPT_HOST_RES_PREFIX, OPT_HOST_RES_PREFIX_LEN))) { this->processFamilyPreference(value); host_res_set_p = true; @@ -659,6 +662,10 @@ HttpProxyPort::print(char *out, size_t n) zret += snprintf(out + zret, n - zret, ":%s", OPT_PROXY_PROTO); } + if (m_proxy_protocol_client_src) { + zret += snprintf(out + zret, n - zret, ":%s", OPT_PROXY_PROTO_CLIENT_SRC_IP); + } + 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/tests/gold_tests/autest-site/trafficserver.test.ext b/tests/gold_tests/autest-site/trafficserver.test.ext index 4cc7a2704fe..0cbe0d511fe 100755 --- a/tests/gold_tests/autest-site/trafficserver.test.ext +++ b/tests/gold_tests/autest-site/trafficserver.test.ext @@ -58,7 +58,8 @@ def MakeATSProcess( log_data=default_log_data, use_traffic_out=True, dump_runroot=True, - enable_proxy_protocol=False): + enable_proxy_protocol=False, + enable_proxy_protocol_cp_src=False): """Create a traffic server process. :param block_for_debug: if True, causes traffic_server to run with the @@ -431,11 +432,14 @@ def MakeATSProcess( port_str += " {ssl_port}:quic {ssl_portv6}:quic:ipv6".format( ssl_port=p.Variables.ssl_port, ssl_portv6=p.Variables.ssl_portv6) if enable_proxy_protocol: - port_str += f" {p.Variables.proxy_protocol_port}:pp {p.Variables.proxy_protocol_portv6}:pp:ipv6" + ppcfg = "pp" + if enable_proxy_protocol_cp_src: + ppcfg += ":pp-clnt" + port_str += f" {p.Variables.proxy_protocol_port}:{ppcfg} {p.Variables.proxy_protocol_portv6}:pp:ipv6" if enable_tls: - port_str += f" {p.Variables.proxy_protocol_ssl_port}:pp:ssl {p.Variables.proxy_protocol_ssl_portv6}:pp:ssl:ipv6" + port_str += f" {p.Variables.proxy_protocol_ssl_port}:{ppcfg}:ssl {p.Variables.proxy_protocol_ssl_portv6}:{ppcfg}:ssl:ipv6" if enable_uds: - port_str += f" {uds_path}:pp" + port_str += f" {uds_path}:{ppcfg}" elif enable_uds: port_str += f" {uds_path}" #p.Env['PROXY_CONFIG_HTTP_SERVER_PORTS'] = port_str diff --git a/tests/gold_tests/proxy_protocol/gold/access-cp.gold b/tests/gold_tests/proxy_protocol/gold/access-cp.gold new file mode 100644 index 00000000000..b84f06e4d2f --- /dev/null +++ b/tests/gold_tests/proxy_protocol/gold/access-cp.gold @@ -0,0 +1,7 @@ +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 127.0.0.1 127.0.0.1 +198.51.100.1 198.51.100.1 127.0.0.1 +127.0.0.1 0 127.0.0.1 +127.0.0.1 0 127.0.0.1 diff --git a/tests/gold_tests/proxy_protocol/gold/access-nocp.gold b/tests/gold_tests/proxy_protocol/gold/access-nocp.gold new file mode 100644 index 00000000000..7d705c4735c --- /dev/null +++ b/tests/gold_tests/proxy_protocol/gold/access-nocp.gold @@ -0,0 +1,7 @@ +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 127.0.0.1 127.0.0.1 +127.0.0.1 198.51.100.1 127.0.0.1 +127.0.0.1 0 127.0.0.1 +127.0.0.1 0 127.0.0.1 diff --git a/tests/gold_tests/proxy_protocol/gold/access.gold b/tests/gold_tests/proxy_protocol/gold/access.gold deleted file mode 100644 index ba2abf3cf79..00000000000 --- a/tests/gold_tests/proxy_protocol/gold/access.gold +++ /dev/null @@ -1,7 +0,0 @@ -127.0.0.1 127.0.0.1 -127.0.0.1 127.0.0.1 -127.0.0.1 127.0.0.1 -127.0.0.1 127.0.0.1 -127.0.0.1 198.51.100.1 -127.0.0.1 0 -127.0.0.1 0 diff --git a/tests/gold_tests/proxy_protocol/proxy_protocol.test.py b/tests/gold_tests/proxy_protocol/proxy_protocol.test.py index 430569428dc..bd008b9a6f0 100644 --- a/tests/gold_tests/proxy_protocol/proxy_protocol.test.py +++ b/tests/gold_tests/proxy_protocol/proxy_protocol.test.py @@ -27,15 +27,21 @@ class ProxyProtocolInTest: replay_file = "replay/proxy_protocol_in.replay.yaml" - def __init__(self): - self.setupOriginServer() - self.setupTS() - - def setupOriginServer(self): - self.server = Test.MakeVerifierServerProcess("pp-in-server", self.replay_file) - - def setupTS(self): - self.ts = Test.MakeATSProcess("ts_in", enable_tls=True, enable_cache=False, enable_proxy_protocol=True) + def __init__(self, name, enable_cp=False): + self.setupOriginServer(name) + self.setupTS(name, enable_cp) + self.name = name + + def setupOriginServer(self, name): + self.server = Test.MakeVerifierServerProcess(f"pp-in-server-{name}", self.replay_file) + + def setupTS(self, name, enable_cp): + self.ts = Test.MakeATSProcess( + f"ts_in_{name}", + enable_tls=True, + enable_cache=False, + enable_proxy_protocol=True, + enable_proxy_protocol_cp_src=enable_cp) self.ts.addDefaultSSLFiles() self.ts.Disk.ssl_multicert_config.AddLine("dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key") @@ -57,7 +63,7 @@ def setupTS(self): logging: formats: - name: access - format: '% %' + format: '% % %' logs: - filename: access @@ -65,9 +71,9 @@ def setupTS(self): '''.split("\n")) def runTraffic(self): - tr = Test.AddTestRun("Verify correct handling of incoming PROXY header.") + tr = Test.AddTestRun(f"Verify correct handling of incoming PROXY header. {self.name}") tr.AddVerifierClientProcess( - "pp-in-client", + f"pp-in-client-{self.name}", self.replay_file, http_ports=[self.ts.Variables.proxy_protocol_port], https_ports=[self.ts.Variables.proxy_protocol_ssl_port]) @@ -80,7 +86,7 @@ def checkAccessLog(self): """ check access log """ - Test.Disk.File(os.path.join(self.ts.Variables.LOGDIR, 'access.log'), exists=True, content='gold/access.gold') + Test.Disk.File(os.path.join(self.ts.Variables.LOGDIR, 'access.log'), exists=True, content=f"gold/access-{self.name}.gold") # Wait for log file to appear, then wait one extra second to make sure # TS is done writing it. @@ -219,7 +225,8 @@ def run(self) -> None: self.setLogExpectations(tr) -ProxyProtocolInTest().run() +ProxyProtocolInTest("nocp", False).run() +ProxyProtocolInTest("cp", True).run() # non-tunnling HTTP to origin ProxyProtocolOutTest(pp_version=-1, is_tunnel=False, is_tls_to_origin=False).run() From 547969ef942d0c0670059c699abdb911d1b762a6 Mon Sep 17 00:00:00 2001 From: Chris McFarlen Date: Thu, 18 Dec 2025 06:26:26 -0600 Subject: [PATCH 2/2] Use get_client_addr in some places it might matter. --- src/iocore/net/SNIActionPerformer.cc | 2 +- src/iocore/net/SSLClientUtils.cc | 8 ++++---- src/iocore/net/SSLDiags.cc | 2 +- src/iocore/net/SSLNetVConnection.cc | 6 ++---- src/proxy/http2/Http2SessionAccept.cc | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/iocore/net/SNIActionPerformer.cc b/src/iocore/net/SNIActionPerformer.cc index 3b30db815df..470dc0819f7 100644 --- a/src/iocore/net/SNIActionPerformer.cc +++ b/src/iocore/net/SNIActionPerformer.cc @@ -417,7 +417,7 @@ SNI_IpAllow::SNIAction(SSL &ssl, ActionItem::Context const & /* ctx ATS_UNUSED * const sockaddr *client_ip = nullptr; for (int i = 0; i < IpAllow::Subject::MAX_SUBJECTS; ++i) { if (IpAllow::Subject::PEER == IpAllow::subjects[i]) { - client_ip = ssl_vc->get_remote_addr(); + client_ip = ssl_vc->get_client_addr(); break; } else if (IpAllow::Subject::PROXY == IpAllow::subjects[i] && ssl_vc->get_proxy_protocol_version() != ProxyProtocolVersion::UNDEFINED) { diff --git a/src/iocore/net/SSLClientUtils.cc b/src/iocore/net/SSLClientUtils.cc index f1b75ff56e3..e2124b44267 100644 --- a/src/iocore/net/SSLClientUtils.cc +++ b/src/iocore/net/SSLClientUtils.cc @@ -81,7 +81,7 @@ verify_callback(int signature_ok, X509_STORE_CTX *ctx) Dbg(dbg_ctl_ssl_verify, "verification error:num=%d:%s:depth=%d", err, X509_verify_cert_error_string(err), depth); const char *sni_name; char buff[INET6_ADDRSTRLEN]; - ats_ip_ntop(netvc->get_remote_addr(), buff, INET6_ADDRSTRLEN); + ats_ip_ntop(netvc->get_client_addr(), buff, INET6_ADDRSTRLEN); if (netvc->options.sni_servername) { sni_name = netvc->options.sni_servername.get(); } else { @@ -110,7 +110,7 @@ verify_callback(int signature_ok, X509_STORE_CTX *ctx) sni_name = reinterpret_cast(netvc->options.sni_servername.get()); } else { sni_name = reinterpret_cast(buff); - ats_ip_ntop(netvc->get_remote_addr(), buff, INET6_ADDRSTRLEN); + ats_ip_ntop(netvc->get_client_addr(), buff, INET6_ADDRSTRLEN); } if (validate_hostname(cert, sni_name, false, &matched_name)) { Dbg(dbg_ctl_ssl_verify, "Hostname %s verified OK, matched %s", sni_name, matched_name); @@ -118,7 +118,7 @@ verify_callback(int signature_ok, X509_STORE_CTX *ctx) } else { // Name validation failed // Get the server address if we did't already compute it if (netvc->options.sni_servername) { - ats_ip_ntop(netvc->get_remote_addr(), buff, INET6_ADDRSTRLEN); + ats_ip_ntop(netvc->get_client_addr(), buff, INET6_ADDRSTRLEN); } // If we got here the verification failed Warning("SNI (%s) not in certificate. Action=%s server=%s(%s)", sni_name, enforce_mode ? "Terminate" : "Continue", @@ -141,7 +141,7 @@ verify_callback(int signature_ok, X509_STORE_CTX *ctx) sni_name = reinterpret_cast(netvc->options.sni_servername.get()); } else { sni_name = reinterpret_cast(buff); - ats_ip_ntop(netvc->get_remote_addr(), buff, INET6_ADDRSTRLEN); + ats_ip_ntop(netvc->get_client_addr(), buff, INET6_ADDRSTRLEN); } Warning("TS_EVENT_SSL_VERIFY_SERVER plugin failed the origin certificate check for %s. Action=%s SNI=%s", netvc->options.ssl_servername.get(), enforce_mode ? "Terminate" : "Continue", sni_name); diff --git a/src/iocore/net/SSLDiags.cc b/src/iocore/net/SSLDiags.cc index 7883fe6b422..5bc0e0174db 100644 --- a/src/iocore/net/SSLDiags.cc +++ b/src/iocore/net/SSLDiags.cc @@ -164,7 +164,7 @@ SSLDiagnostic(const SourceLocation &loc, bool debug, SSLNetVConnection *vc, cons ip_text_buffer ip_buf = {'\0'}; if (vc) { - ats_ip_ntop(vc->get_remote_addr(), ip_buf, sizeof(ip_buf)); + ats_ip_ntop(vc->get_client_addr(), ip_buf, sizeof(ip_buf)); } es = reinterpret_cast(pthread_self()); diff --git a/src/iocore/net/SSLNetVConnection.cc b/src/iocore/net/SSLNetVConnection.cc index 787936fb76c..c4aeb44a89d 100644 --- a/src/iocore/net/SSLNetVConnection.cc +++ b/src/iocore/net/SSLNetVConnection.cc @@ -356,10 +356,8 @@ SSLNetVConnection::read_raw_data() if (pp_ipmap->count() > 0) { Dbg(dbg_ctl_proxyprotocol, "proxy protocol has a configured allowlist 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. + // Using get_remote_addr() will return the ip of the + // proxy source IP, not the Proxy Protocol client ip. if (!pp_ipmap->contains(swoc::IPAddr(get_remote_addr()))) { Dbg(dbg_ctl_proxyprotocol, "Source IP is NOT in the configured allowlist of trusted IPs - closing connection"); r = -ENOTCONN; // Need a quick close/exit here to refuse the connection!!!!!!!!! diff --git a/src/proxy/http2/Http2SessionAccept.cc b/src/proxy/http2/Http2SessionAccept.cc index f8306c9d1b4..904c98d223a 100644 --- a/src/proxy/http2/Http2SessionAccept.cc +++ b/src/proxy/http2/Http2SessionAccept.cc @@ -46,7 +46,7 @@ Http2SessionAccept::accept(NetVConnection *netvc, MIOBuffer *iobuf, IOBufferRead for (int i = 0; i < IpAllow::Subject::MAX_SUBJECTS; ++i) { if (IpAllow::Subject::PEER == IpAllow::subjects[i]) { - client_ip = netvc->get_remote_addr(); + client_ip = netvc->get_client_addr(); break; } else if (IpAllow::Subject::PROXY == IpAllow::subjects[i] && netvc->get_proxy_protocol_version() != ProxyProtocolVersion::UNDEFINED) {