This is an automated email from the ASF dual-hosted git repository.
cmcfarlen pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new 5472248d70 header_rewrite: Add INBOUND_IP_SOURCE controller (#12246)
(#12349)
5472248d70 is described below
commit 5472248d70d2b8f8d698990e58bed0d41550b3b4
Author: Chris McFarlen <[email protected]>
AuthorDate: Thu Jul 10 18:42:09 2025 -0500
header_rewrite: Add INBOUND_IP_SOURCE controller (#12246) (#12349)
* header_rewrite: Add INBOUND_IP_SOURCE controller
* Fix documentation format
* Fix documentation format
* Fix documentation format
* Initialize private slot index
* Add examples
Conflicts:
doc/admin-guide/plugins/header_rewrite.en.rst
(cherry picked from commit f449d9837cc4a16e8650bcbb16e2c980b3824922)
Co-authored-by: Masakazu Kitajo <[email protected]>
---
doc/admin-guide/plugins/header_rewrite.en.rst | 48 +++++++++++++++++++++++----
plugins/header_rewrite/conditions.cc | 44 +++++++++++++++++++-----
plugins/header_rewrite/conditions.h | 32 +++++++++++++++---
plugins/header_rewrite/operators.cc | 12 +++++++
plugins/header_rewrite/operators.h | 1 +
plugins/header_rewrite/statement.h | 11 +++++-
6 files changed, 128 insertions(+), 20 deletions(-)
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst
b/doc/admin-guide/plugins/header_rewrite.en.rst
index 74df5954b6..441e726c38 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -1100,12 +1100,16 @@ set-plugin-cntl
This operator lets you control the fundamental behavior of this plugin for a
particular transaction.
The available controllers are:
-================== =====================
=============================================================================================
-Controller Operators/Conditions Description
-================== =====================
=============================================================================================
-TIMEZONE ``NOW`` If ``GMT`` is passed, the operators
and conditions use GMT regardles of the timezone setting
- on your system. The default value is
``LOCAL``.
-================== =====================
=============================================================================================
++===================+========================+==============================================================================================+
+| Controller | Operators/Conditions | Description
|
++===================+========================+==============================================================================================+
+| TIMEZONE | ``NOW`` | If ``GMT`` is passed, the
operators and conditions use GMT regardles of the timezone setting |
+| | | on your system. The default
value is ``LOCAL``. |
++===================+========================+==============================================================================================+
+| INBOUND_IP_SOURCE | ``IP``, ``INBOUND``, | Selects which IP address to use
for the operators and conditions. Available sources are |
+| | ``CIDR``, and ``GEO`` | ``PEER`` (Uses the IP address
of the peer), and ``PROXY`` (Uses the IP address from PROXY |
+| | | protocol)
|
++===================+========================+==============================================================================================+
Operator Flags
--------------
@@ -1618,3 +1622,35 @@ limiting to the request.::
cond %{REMAP_PSEUDO_HOOK} [AND]
cond %{CLIENT-HEADER:Some-Special-Header} ="yes"
run-plugin rate_limit.so "--limit=300 --error=429"
+
+Check the ``PATH`` file extension
+---------------------------------
+
+This rule will deny all requests for URIs with the ``.php`` file extension::
+
+ cond %{REMAP_PSEUDO_HOOK} [AND]
+ cond %{CLIENT-URL:PATH} ="php" [EXT,NOCASE]
+ set-status 403
+
+Use GMT regardless of system timezone setting
+---------------------------------------------
+
+This rule will change the behavior of %{NOW}. It will always return time in
GMT.
+
+ cond %{READ_REQUEST_HDR_HOOK}
+ set-plugin-cntl TIMEZONE GMT
+
+ cond %{SEND_RESPONSE_HDR_HOOK}
+ set-header hour %{NOW:HOUR}
+
+Use IP address provided by PROXY protocol
+-----------------------------------------
+
+This rule will change the behavior of all header_rewrite conditions which use
the client's IP address on a connection.
+Those will pick the address provided by PROXY protocol, instead of the peer's
address.
+
+ cond %{READ_REQUEST_HDR_HOOK}
+ set-plugin-cntl INBOUND_IP_SOURCE PROXY
+
+ cond %{SEND_RESPONSE_HDR_HOOK}
+ set-header real-ip %{INBOUND:REMOTE-ADDR}
diff --git a/plugins/header_rewrite/conditions.cc
b/plugins/header_rewrite/conditions.cc
index 6542771200..4cd109fa9e 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -34,6 +34,8 @@
#include "conditions.h"
#include "lulu.h"
+static const sockaddr *getClientAddr(TSHttpTxn txnp, int txn_private_slot);
+
// ConditionStatus
void
ConditionStatus::initialize(Parser &p)
@@ -553,7 +555,7 @@ ConditionIp::eval(const Resources &res)
switch (_ip_qual) {
case IP_QUAL_CLIENT:
- addr = TSHttpTxnClientAddrGet(res.txnp);
+ addr = getClientAddr(res.txnp, _txn_private_slot);
break;
case IP_QUAL_INBOUND:
addr = TSHttpTxnIncomingAddrGet(res.txnp);
@@ -591,7 +593,7 @@ ConditionIp::append_value(std::string &s, const Resources
&res)
switch (_ip_qual) {
case IP_QUAL_CLIENT:
- ip_set = (nullptr != getIP(TSHttpTxnClientAddrGet(res.txnp), ip));
+ ip_set = (nullptr != getIP(getClientAddr(res.txnp, _txn_private_slot),
ip));
break;
case IP_QUAL_INBOUND:
ip_set = (nullptr != getIP(TSHttpTxnIncomingAddrGet(res.txnp), ip));
@@ -824,9 +826,9 @@ void
ConditionGeo::append_value(std::string &s, const Resources &res)
{
if (is_int_type()) {
- s += std::to_string(get_geo_int(TSHttpTxnClientAddrGet(res.txnp)));
+ s += std::to_string(get_geo_int(getClientAddr(res.txnp,
_txn_private_slot)));
} else {
- s += get_geo_string(TSHttpTxnClientAddrGet(res.txnp));
+ s += get_geo_string(getClientAddr(res.txnp, _txn_private_slot));
}
Dbg(pi_dbg_ctl, "Appending GEO() to evaluation value -> %s", s.c_str());
}
@@ -838,7 +840,7 @@ ConditionGeo::eval(const Resources &res)
Dbg(pi_dbg_ctl, "Evaluating GEO()");
if (is_int_type()) {
- int64_t geo = get_geo_int(TSHttpTxnClientAddrGet(res.txnp));
+ int64_t geo = get_geo_int(getClientAddr(res.txnp, _txn_private_slot));
ret = static_cast<const Matchers<int64_t> *>(_matcher)->test(geo, res);
} else {
@@ -995,7 +997,7 @@ ConditionCidr::eval(const Resources &res)
void
ConditionCidr::append_value(std::string &s, const Resources &res)
{
- struct sockaddr const *addr = TSHttpTxnClientAddrGet(res.txnp);
+ struct sockaddr const *addr = getClientAddr(res.txnp, _txn_private_slot);
if (addr) {
switch (addr->sa_family) {
@@ -1101,7 +1103,7 @@ ConditionInbound::eval(const Resources &res)
addr = TSHttpTxnIncomingAddrGet(res.txnp);
break;
case NET_QUAL_REMOTE_ADDR:
- addr = TSHttpTxnClientAddrGet(res.txnp);
+ addr = getClientAddr(res.txnp, _txn_private_slot);
break;
default:
// Only support actual IP addresses of course...
@@ -1148,10 +1150,10 @@ ConditionInbound::append_value(std::string &s, const
Resources &res, NetworkSess
zret = text;
} break;
case NET_QUAL_REMOTE_ADDR: {
- zret = getIP(TSHttpTxnClientAddrGet(res.txnp), text);
+ zret = getIP(getClientAddr(res.txnp, _txn_private_slot), text);
} break;
case NET_QUAL_REMOTE_PORT: {
- uint16_t port = getPort(TSHttpTxnClientAddrGet(res.txnp));
+ uint16_t port = getPort(getClientAddr(res.txnp, _txn_private_slot));
snprintf(text, sizeof(text), "%d", port);
zret = text;
} break;
@@ -1608,3 +1610,27 @@ ConditionLastCapture::eval(const Resources &res)
return static_cast<const MatcherType *>(_matcher)->test(s, res);
}
+
+static const struct sockaddr *
+getClientAddr(TSHttpTxn txnp, int txn_private_slot)
+{
+ const struct sockaddr *addr = nullptr;
+ int addr_len;
+
+ PrivateSlotData private_data;
+ private_data.raw = reinterpret_cast<uint64_t>(TSUserArgGet(txnp,
txn_private_slot));
+ switch (private_data.ip_source) {
+ case IP_SRC_PEER:
+ addr = TSHttpTxnClientAddrGet(txnp);
+ break;
+ case IP_SRC_PROXY:
+ TSVConnPPInfoGet(TSHttpSsnClientVConnGet(TSHttpTxnSsnGet(txnp)),
TS_PP_INFO_SRC_ADDR, reinterpret_cast<const char **>(&addr),
+ &addr_len);
+ break;
+ default:
+ Dbg(pi_dbg_ctl, "Unknown IP source (%d) was specified",
private_data.ip_source);
+ addr = TSHttpTxnClientAddrGet(txnp);
+ break;
+ }
+ return addr;
+}
diff --git a/plugins/header_rewrite/conditions.h
b/plugins/header_rewrite/conditions.h
index 863a009470..8d3edfec93 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -360,6 +360,12 @@ public:
void append_value(std::string &s, const Resources &res) override;
protected:
+ bool
+ need_txn_private_slot() const override
+ {
+ return true;
+ }
+
bool eval(const Resources &res) override;
private:
@@ -447,6 +453,12 @@ private:
virtual std::string get_geo_string(const sockaddr *addr) const;
protected:
+ bool
+ need_txn_private_slot() const override
+ {
+ return true;
+ }
+
bool eval(const Resources &res) override;
GeoQualifiers _geo_qual = GEO_QUAL_COUNTRY;
bool _int_type = false;
@@ -494,6 +506,12 @@ public:
void append_value(std::string &s, const Resources &res) override;
protected:
+ bool
+ need_txn_private_slot() const override
+ {
+ return true;
+ }
+
bool eval(const Resources &res) override;
private:
@@ -517,18 +535,24 @@ public:
ConditionInbound(self &) = delete;
self &operator=(self &) = delete;
- void initialize(Parser &p) override;
- void set_qualifier(const std::string &q) override;
- void append_value(std::string &s, const Resources &res) override;
- static void append_value(std::string &s, const Resources &res,
NetworkSessionQualifiers qual);
+ void initialize(Parser &p) override;
+ void set_qualifier(const std::string &q) override;
+ void append_value(std::string &s, const Resources &res) override;
static constexpr const char *TAG = "INBOUND";
protected:
+ bool
+ need_txn_private_slot() const override
+ {
+ return true;
+ }
+
bool eval(const Resources &res) override;
private:
NetworkSessionQualifiers _net_qual = NET_QUAL_STACK;
+ void append_value(std::string &s, const Resources &res,
NetworkSessionQualifiers qual);
};
class ConditionStringLiteral : public Condition
diff --git a/plugins/header_rewrite/operators.cc
b/plugins/header_rewrite/operators.cc
index 6d6a27b889..46709e2a3f 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -1211,6 +1211,15 @@ OperatorSetPluginCntl::initialize(Parser &p)
} else {
TSError("[%s] Unknown value for TIMZEONE control: %s", PLUGIN_NAME,
value.c_str());
}
+ } else if (name == "INBOUND_IP_SOURCE") {
+ _name = PluginCtrl::INBOUND_IP_SOURCE;
+ if (value == "PEER") {
+ _value = IP_SRC_PEER;
+ } else if (value == "PROXY") {
+ _value = IP_SRC_PROXY;
+ } else {
+ TSError("[%s] Unknown value for INBOUND_IP_SOURCE control: %s",
PLUGIN_NAME, value.c_str());
+ }
}
}
@@ -1238,6 +1247,9 @@ OperatorSetPluginCntl::exec(const Resources &res) const
case PluginCtrl::TIMEZONE:
private_data.timezone = _value;
break;
+ case PluginCtrl::INBOUND_IP_SOURCE:
+ private_data.ip_source = _value;
+ break;
}
Dbg(pi_dbg_ctl, " Setting plugin control %d to %d",
static_cast<int>(_name), _value);
diff --git a/plugins/header_rewrite/operators.h
b/plugins/header_rewrite/operators.h
index 2d273b19b9..b0943b7e19 100644
--- a/plugins/header_rewrite/operators.h
+++ b/plugins/header_rewrite/operators.h
@@ -466,6 +466,7 @@ public:
enum class PluginCtrl {
TIMEZONE,
+ INBOUND_IP_SOURCE,
};
protected:
diff --git a/plugins/header_rewrite/statement.h
b/plugins/header_rewrite/statement.h
index bf1020451b..acbdcd69d0 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -208,8 +208,17 @@ private:
union PrivateSlotData {
uint64_t raw;
struct {
- uint64_t timezone : 1; // TIMEZONE_LOCAL, or TIMEZONE_GMT
+ uint64_t timezone : 1; // TIMEZONE_LOCAL, or TIMEZONE_GMT
+ uint64_t ip_source : 2; // IP_SRC_PEER, IP_SRC_PROXY, IP_SRC_FORWARDED, or
IP_SRC_PLUGIN
+ uint64_t unused : 61;
};
};
enum { TIMEZONE_LOCAL, TIMEZONE_GMT };
+
+enum {
+ IP_SRC_PEER, // Immediate connection
+ IP_SRC_PROXY, // PROXY protocl
+ // IP_SRC_FORWARDED, // Forwarded header field (TS core needs to support
the header first. It can be done by a plugin as well.)
+ // IP_SRC_PLUGIN // Plugin (Needs TS API to set and get a verified client
IP address)
+};