Skip to content

Commit b92a6b3

Browse files
TS-4593: Extend IPAllow to filter outbound connections and be controlled from remap. (#1)
Merged
1 parent a8ee66c commit b92a6b3

13 files changed

Lines changed: 191 additions & 48 deletions

File tree

doc/admin-guide/files/ip_allow.config.en.rst

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ ip_allow.config
2222
.. configfile:: ip_allow.config
2323

2424
The :file:`ip_allow.config` file controls client access to the Traffic
25-
Server proxy cache. You can specify ranges of IP addresses that are
26-
allowed to use the Traffic Server as a web proxy cache. After you modify
27-
the :file:`ip_allow.config` file, navigate to the Traffic Server bin
28-
directory and run the :option:`traffic_ctl config reload` command to apply changes. When
25+
Server proxy cache and Traffic Server connections to the servers. You
26+
can specify ranges of IP addresses that are allowed to use the Traffic
27+
Server as a web proxy cache or that are allowed to be remapped by
28+
Traffic Server. After you modify the :file:`ip_allow.config` file,
29+
navigate to the Traffic Server bin directory and run the
30+
:option:`traffic_ctl config reload` command to apply changes. When
2931
you apply the changes to a node in a cluster, Traffic Server
3032
automatically applies the changes to all other nodes in the cluster.
3133

@@ -36,13 +38,18 @@ Each line in the :file:`ip_allow.config` file must have the following
3638
format::
3739

3840
src_ip=<range of IP addresses> action=<action> [method=<list of methods separated by '|'>]
41+
dest_ip=<range of IP addresses> action=<action> [method=<list of methods separated by '|'>]
3942

4043
where src_ip is the IP address or range of IP addresses of the
41-
client(s). The action ``ip_allow`` enables the specified client(s) to
42-
access the Traffic Server proxy cache, and ``ip_deny`` denies the
43-
specified client(s) to access the Traffic Server proxy cache. Multiple
44-
method keywords can be specified (method=GET method=HEAD), or multiple
45-
methods can be separated by an '\|' (method=GET\|HEAD). The method
44+
client(s) and dest_ip is the IP address or range of IP addresses of the
45+
server(s). When src_ip is indicated, the action ``ip_allow`` enables
46+
the specified client(s) to access the Traffic Server proxy cache,
47+
and ``ip_deny`` denies the specified client(s) to access the Traffic
48+
Server proxy cache. When dest_ip is indicated, the action ``ip_allow``
49+
enables the Traffic Server to access the specified server(s), and
50+
``ip_deny`` denies the Traffic Server to access the specified server(s).
51+
Multiple method keywords can be specified (method=GET method=HEAD), or
52+
multiple methods can be separated by an '\|' (method=GET\|HEAD). The method
4653
keyword is optional and it is defaulted to ALL. This supports ANY string
4754
as the HTTP method, meaning no validation is done to check whether it
4855
is a valid HTTP method. This allows you to create filters for any method
@@ -78,3 +85,11 @@ the Traffic Server proxy cache::
7885

7986
src_ip=123.45.6.0-123.45.6.123 action=ip_deny
8087

88+
The following example enables the Traffic Server to access all servers::
89+
90+
dest_ip=0.0.0.0-255.255.255.255 action=ip_allow
91+
92+
The following example denies the Traffic Server to access all servers
93+
on a specific subnet::
94+
95+
dest_ip=10.0.0.0-10.0.0.255 action=ip_deny

iocore/net/SessionAccept.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ SessionAccept::testIpAllowPolicy(sockaddr const *client_ip)
3131
IpAllow::scoped_config ipallow;
3232
const AclRecord *acl_record = nullptr;
3333
if (ipallow) {
34-
acl_record = ipallow->match(client_ip);
35-
if (acl_record && acl_record->isEmpty()) {
34+
acl_record = ipallow->match(client_ip, IpAllow::SRC_ADDR);
35+
if (acl_record && acl_record->isEmpty() && ipallow->isAcceptCheckEnabled()) {
3636
acl_record = nullptr;
3737
}
3838
}

lib/ts/MatcherUtils.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ processDurationString(char *str, int *seconds)
401401

402402
const matcher_tags http_dest_tags = {"dest_host", "dest_domain", "dest_ip", "url_regex", "url", "host_regex", true};
403403

404-
const matcher_tags ip_allow_tags = {nullptr, nullptr, "src_ip", nullptr, nullptr, nullptr, false};
404+
const matcher_tags ip_allow_src_tags = {nullptr, nullptr, "src_ip", nullptr, nullptr, nullptr, false};
405+
406+
const matcher_tags ip_allow_dest_tags = {nullptr, nullptr, "dest_ip", nullptr, nullptr, nullptr, true};
405407

406408
const matcher_tags socks_server_tags = {nullptr, nullptr, "dest_ip", nullptr, nullptr, nullptr, false};
407409

lib/ts/MatcherUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ struct matcher_tags {
108108
};
109109

110110
extern const matcher_tags http_dest_tags;
111-
extern const matcher_tags ip_allow_tags;
111+
extern const matcher_tags ip_allow_src_tags;
112+
extern const matcher_tags ip_allow_dest_tags;
112113
extern const matcher_tags socks_server_tags;
113114

114115
const char *parseConfigLine(char *line, matcher_line *p_line, const matcher_tags *tags);

proxy/IPAllow.cc

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ enum AclOp {
4545

4646
const AclRecord IpAllow::ALL_METHOD_ACL(AclRecord::ALL_METHOD_MASK);
4747

48-
int IpAllow::configid = 0;
48+
int IpAllow::configid = 0;
49+
bool IpAllow::accept_check_p = true; // initializing global flag for fast deny
4950

5051
static ConfigUpdateHandler<IpAllow> *ipAllowUpdate;
5152

@@ -108,12 +109,11 @@ IpAllow::~IpAllow()
108109
}
109110

110111
void
111-
IpAllow::Print()
112+
IpAllow::PrintMap(IpMap *map)
112113
{
113114
std::ostringstream s;
114-
s << _map.getCount() << " ACL entries";
115-
s << '.';
116-
for (IpMap::iterator spot(_map.begin()), limit(_map.end()); spot != limit; ++spot) {
115+
s << map->getCount() << " ACL entries.";
116+
for (IpMap::iterator spot(map->begin()), limit(map->end()); spot != limit; ++spot) {
117117
char text[INET6_ADDRSTRLEN];
118118
AclRecord const *ar = static_cast<AclRecord const *>(spot->data());
119119

@@ -156,6 +156,15 @@ IpAllow::Print()
156156
Debug("ip-allow", "%s", s.str().c_str());
157157
}
158158

159+
void
160+
IpAllow::Print()
161+
{
162+
Debug("ip-allow", "Printing src map");
163+
PrintMap(&_src_map);
164+
Debug("ip-allow", "Printing dest map");
165+
PrintMap(&_dest_map);
166+
}
167+
159168
int
160169
IpAllow::BuildTable()
161170
{
@@ -171,7 +180,7 @@ IpAllow::BuildTable()
171180
bool alarmAlready = false;
172181

173182
// Table should be empty
174-
ink_assert(_map.getCount() == 0);
183+
ink_assert(_src_map.getCount() == 0 && _dest_map.getCount() == 0);
175184

176185
file_buf = readIntoBuffer(config_file_path, module_name, nullptr);
177186

@@ -190,6 +199,8 @@ IpAllow::BuildTable()
190199
}
191200

192201
if (*line != '\0' && *line != '#') {
202+
const matcher_tags &ip_allow_tags =
203+
strstr(line, ip_allow_dest_tags.match_ip) != nullptr ? ip_allow_dest_tags : ip_allow_src_tags;
193204
errPtr = parseConfigLine(line, &line_info, &ip_allow_tags);
194205

195206
if (errPtr != nullptr) {
@@ -211,6 +222,7 @@ IpAllow::BuildTable()
211222
uint32_t acl_method_mask = 0;
212223
AclRecord::MethodSet nonstandard_methods;
213224
bool deny_nonstandard_methods = false;
225+
bool is_dest_ip = (strcasecmp(line_info.line[0][line_info.dest_entry], "dest_ip") == 0);
214226
AclOp op = ACL_OP_DENY; // "shut up", I explained to the compiler.
215227
bool op_found = false, method_found = false;
216228
for (int i = 0; i < MATCHER_MAX_TOKENS; i++) {
@@ -272,10 +284,11 @@ IpAllow::BuildTable()
272284
}
273285

274286
if (method_found) {
275-
_acls.push_back(AclRecord(acl_method_mask, line_num, nonstandard_methods, deny_nonstandard_methods));
276-
// Color with index because at this point the address
277-
// is volatile.
278-
_map.fill(&addr1, &addr2, reinterpret_cast<void *>(_acls.length() - 1));
287+
Vec<AclRecord> &acls = is_dest_ip ? _dest_acls : _src_acls;
288+
IpMap &map = is_dest_ip ? _dest_map : _src_map;
289+
acls.push_back(AclRecord(acl_method_mask, line_num, nonstandard_methods, deny_nonstandard_methods));
290+
// Color with index in acls because at this point the address is volatile.
291+
map.fill(&addr1, &addr2, reinterpret_cast<void *>(acls.length() - 1));
279292
} else {
280293
snprintf(errBuf, sizeof(errBuf), "%s discarding %s entry at line %d : %s", module_name, config_file_path, line_num,
281294
"Invalid action/method specified"); // changed by YTS Team, yamsat bug id -59022
@@ -288,13 +301,14 @@ IpAllow::BuildTable()
288301
line = tokLine(nullptr, &tok_state);
289302
}
290303

291-
if (_map.getCount() == 0) {
304+
if (_src_map.getCount() == 0 && _dest_map.getCount() == 0) { // TODO: check
292305
Warning("%s No entries in %s. All IP Addresses will be blocked", module_name, config_file_path);
293306
} else {
294307
// convert the coloring from indices to pointers.
295-
for (IpMap::iterator spot(_map.begin()), limit(_map.end()); spot != limit; ++spot) {
296-
spot->setData(&_acls[reinterpret_cast<size_t>(spot->data())]);
297-
}
308+
for (auto &item : _src_map)
309+
item.setData(&_src_acls[reinterpret_cast<size_t>(item.data())]);
310+
for (auto &item : _dest_map)
311+
item.setData(&_dest_acls[reinterpret_cast<size_t>(item.data())]);
298312
}
299313

300314
if (is_debug_tag_set("ip-allow")) {

proxy/IPAllow.h

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,14 @@ class IpAllow : public ConfigInfo
110110

111111
public:
112112
typedef IpAllow self; ///< Self reference type.
113+
// indicator for whether we should be checking the acl record for src ip or dest ip
114+
enum match_key_t { SRC_ADDR, DEST_ADDR };
113115

114116
IpAllow(const char *config_var, const char *name, const char *action_val);
115117
~IpAllow();
116118
void Print();
117-
AclRecord *match(IpEndpoint const *ip) const;
118-
AclRecord *match(sockaddr const *ip) const;
119+
AclRecord *match(IpEndpoint const *ip, match_key_t key) const;
120+
AclRecord *match(sockaddr const *ip, match_key_t key) const;
119121

120122
static void startup();
121123
static void reconfigure();
@@ -130,35 +132,59 @@ class IpAllow : public ConfigInfo
130132
return &ALL_METHOD_ACL;
131133
}
132134

135+
/* @return The previous accept check state
136+
* This is a global variable that is independent of
137+
* the ip_allow configuration
138+
*/
139+
static bool
140+
enableAcceptCheck(bool state)
141+
{
142+
bool temp = accept_check_p;
143+
accept_check_p = state;
144+
return temp;
145+
}
146+
/* @return The current accept check state
147+
* This is a global variable that is independent of
148+
* the ip_allow configuration
149+
*/
150+
static bool
151+
isAcceptCheckEnabled()
152+
{
153+
return accept_check_p;
154+
}
155+
133156
typedef ConfigProcessor::scoped_config<IpAllow, IpAllow> scoped_config;
134157

135158
private:
136159
static int configid;
137160
static const AclRecord ALL_METHOD_ACL;
161+
static bool accept_check_p;
138162

163+
void PrintMap(IpMap *map);
139164
int BuildTable();
140165

141166
char config_file_path[PATH_NAME_MAX];
142167
const char *module_name;
143168
const char *action;
144-
IpMap _map;
145-
Vec<AclRecord> _acls;
169+
IpMap _src_map;
170+
IpMap _dest_map;
171+
Vec<AclRecord> _src_acls;
172+
Vec<AclRecord> _dest_acls;
146173
};
147174

148175
inline AclRecord *
149-
IpAllow::match(IpEndpoint const *ip) const
176+
IpAllow::match(IpEndpoint const *ip, match_key_t key) const
150177
{
151-
return this->match(&ip->sa);
178+
return this->match(&ip->sa, key);
152179
}
153180

154181
inline AclRecord *
155-
IpAllow::match(sockaddr const *ip) const
182+
IpAllow::match(sockaddr const *ip, match_key_t key) const
156183
{
157-
void *raw;
158-
if (_map.contains(ip, &raw)) {
159-
return static_cast<AclRecord *>(raw);
160-
}
161-
return nullptr;
184+
void *raw = nullptr;
185+
const IpMap &map = (key == SRC_ADDR) ? _src_map : _dest_map;
186+
map.contains(ip, &raw);
187+
return static_cast<AclRecord *>(raw);
162188
}
163189

164190
#endif

proxy/InkAPI.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7298,7 +7298,7 @@ const char *
72987298
TSMatcherParseSrcIPConfigLine(char *line, TSMatcherLine ml)
72997299
{
73007300
sdk_assert(sdk_sanity_check_null_ptr((void *)line) == TS_SUCCESS);
7301-
return parseConfigLine(line, (matcher_line *)ml, &ip_allow_tags);
7301+
return parseConfigLine(line, (matcher_line *)ml, &ip_allow_src_tags);
73027302
}
73037303

73047304
char *

proxy/http/HttpSM.cc

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
#include "HttpPages.h"
4444

45+
#include "IPAllow.h"
4546
//#include "I_Auth.h"
4647
//#include "HttpAuthParams.h"
4748
#include "congest/Congestion.h"
@@ -4730,7 +4731,50 @@ HttpSM::do_http_server_open(bool raw)
47304731
milestones[TS_MILESTONE_SERVER_FIRST_CONNECT] = milestones[TS_MILESTONE_SERVER_CONNECT];
47314732
}
47324733

4733-
if (t_state.pCongestionEntry != nullptr) {
4734+
// Check for remap rule. If so, only apply ip_allow filter if it is activated (ip_allow_check_enabled_p set).
4735+
// Otherwise, if no remap rule is defined, apply the ip_allow filter.
4736+
if (!t_state.url_remap_success || t_state.url_map.getMapping()->ip_allow_check_enabled_p) {
4737+
// Method allowed on dest IP address check
4738+
sockaddr *server_ip = &t_state.current.server->dst_addr.sa;
4739+
IpAllow::scoped_config ip_allow;
4740+
4741+
if (ip_allow) {
4742+
const AclRecord *acl_record = ip_allow->match(server_ip, IpAllow::DEST_ADDR);
4743+
bool deny_request = false; // default is fail open.
4744+
int method = t_state.hdr_info.server_request.method_get_wksidx();
4745+
4746+
if (acl_record) {
4747+
// If empty, nothing is allowed, deny. Conversely if all methods are allowed it's OK, do not deny.
4748+
// Otherwise the method has to be checked specifically.
4749+
if (acl_record->isEmpty()) {
4750+
deny_request = true;
4751+
} else if (acl_record->_method_mask != AclRecord::ALL_METHOD_MASK) {
4752+
if (method != -1) {
4753+
deny_request = !acl_record->isMethodAllowed(method);
4754+
} else {
4755+
int method_str_len;
4756+
const char *method_str = t_state.hdr_info.server_request.method_get(&method_str_len);
4757+
deny_request = !acl_record->isNonstandardMethodAllowed(std::string(method_str, method_str_len));
4758+
}
4759+
}
4760+
}
4761+
4762+
if (deny_request) {
4763+
if (is_debug_tag_set("ip-allow")) {
4764+
ip_text_buffer ipb;
4765+
Warning("server '%s' prohibited by ip-allow policy", ats_ip_ntop(server_ip, ipb, sizeof(ipb)));
4766+
Debug("ip-allow", "Denial on %s:%s with mask %x", ats_ip_ntop(&t_state.current.server->dst_addr.sa, ipb, sizeof(ipb)),
4767+
hdrtoken_index_to_wks(method), acl_record ? acl_record->_method_mask : 0x0);
4768+
}
4769+
t_state.current.attempts = t_state.txn_conf->connect_attempts_max_retries; // prevent any more retries with this IP
4770+
call_transact_and_set_next_state(HttpTransact::Forbidden);
4771+
return;
4772+
}
4773+
}
4774+
}
4775+
4776+
// Congestion Check
4777+
if (t_state.pCongestionEntry != NULL) {
47344778
if (t_state.pCongestionEntry->F_congested() &&
47354779
(!t_state.pCongestionEntry->proxy_retry(milestones[TS_MILESTONE_SERVER_CONNECT]))) {
47364780
t_state.congestion_congested_or_failed = 1;

proxy/http/HttpTransact.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,16 @@ HttpTransact::BadRequest(State *s)
565565
TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
566566
}
567567

568+
void
569+
HttpTransact::Forbidden(State *s)
570+
{
571+
DebugTxn("http_trans", "[Forbidden]"
572+
"IpAllow marked request forbidden");
573+
bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
574+
build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied", NULL);
575+
TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
576+
}
577+
568578
void
569579
HttpTransact::HandleBlindTunnel(State *s)
570580
{
@@ -6576,6 +6586,12 @@ HttpTransact::process_quick_http_filter(State *s, int method)
65766586
return;
65776587
}
65786588

6589+
// if ipallow rules are disabled by remap then don't modify anything
6590+
url_mapping *mp = s->url_map.getMapping();
6591+
if (mp && !mp->ip_allow_check_enabled_p) {
6592+
return;
6593+
}
6594+
65796595
if (s->state_machine->ua_session) {
65806596
const AclRecord *acl_record = s->state_machine->ua_session->get_acl_record();
65816597
bool deny_request = (acl_record == nullptr);

proxy/http/HttpTransact.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,7 @@ class HttpTransact
12001200
static void StartAuth(State *s);
12011201
static void HandleRequestAuthorized(State *s);
12021202
static void BadRequest(State *s);
1203+
static void Forbidden(State *s);
12031204
static void HandleFiltering(State *s);
12041205
static void DecideCacheLookup(State *s);
12051206
static void LookupSkipOpenServer(State *s);

0 commit comments

Comments
 (0)