[MEDIUM] added the "reqtarpit" and "reqitarpit" features
It is now possible to tarpit connections based on regex matches. The tarpit timeout is equal to the contimeout. A 500 server error response is faked, and the logs show the status flags as "PT" which indicate the connection has been tarpitted.
This commit is contained in:
parent
f8306d5391
commit
b8750a82a2
@ -37,6 +37,7 @@
|
||||
#define ACT_REMOVE 2 /* remove the matching header */
|
||||
#define ACT_DENY 3 /* deny the request */
|
||||
#define ACT_PASS 4 /* pass this header without allowing or denying the request */
|
||||
#define ACT_TARPIT 5 /* tarpit the connection matching this request */
|
||||
|
||||
struct hdr_exp {
|
||||
struct hdr_exp *next;
|
||||
|
@ -73,6 +73,7 @@
|
||||
#define SN_FINST_D 0x00004000 /* session ended during data phase */
|
||||
#define SN_FINST_L 0x00005000 /* session ended while pushing last data to client */
|
||||
#define SN_FINST_Q 0x00006000 /* session ended while waiting in queue for a server slot */
|
||||
#define SN_FINST_T 0x00007000 /* session ended tarpitted */
|
||||
#define SN_FINST_MASK 0x00007000 /* mask to get only final session state flags */
|
||||
#define SN_FINST_SHIFT 12 /* bit shift */
|
||||
|
||||
@ -95,6 +96,7 @@
|
||||
#define SN_ASSIGNED 0x00800000 /* no need to assign a server to this session */
|
||||
#define SN_ADDR_SET 0x01000000 /* this session's server address has been set */
|
||||
#define SN_SELF_GEN 0x02000000 /* the proxy generates data for the client (eg: stats) */
|
||||
#define SN_CLTARPIT 0x04000000 /* the session is tarpitted (anti-dos) */
|
||||
|
||||
|
||||
/* WARNING: if new fields are added, they must be initialized in event_accept() */
|
||||
|
@ -1348,6 +1348,26 @@ int cfg_parse_listen(char *file, int linenum, char **args)
|
||||
|
||||
chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
|
||||
}
|
||||
else if (!strcmp(args[0], "reqtarpit")) { /* tarpit a request if a header matches this regex */
|
||||
regex_t *preg;
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*(args[1]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
preg = calloc(1, sizeof(regex_t));
|
||||
if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
|
||||
Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chain_regex(&curproxy->req_exp, preg, ACT_TARPIT, NULL);
|
||||
}
|
||||
else if (!strcmp(args[0], "reqirep")) { /* replace request header from a regex, ignoring case */
|
||||
regex_t *preg;
|
||||
if (curproxy == &defproxy) {
|
||||
@ -1454,6 +1474,26 @@ int cfg_parse_listen(char *file, int linenum, char **args)
|
||||
|
||||
chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
|
||||
}
|
||||
else if (!strcmp(args[0], "reqitarpit")) { /* tarpit a request if a header matches this regex ignoring case */
|
||||
regex_t *preg;
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*(args[1]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
preg = calloc(1, sizeof(regex_t));
|
||||
if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
|
||||
Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chain_regex(&curproxy->req_exp, preg, ACT_TARPIT, NULL);
|
||||
}
|
||||
else if (!strcmp(args[0], "reqadd")) { /* add request header */
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
|
@ -52,7 +52,7 @@ const char *monthname[12] = {
|
||||
};
|
||||
|
||||
const char sess_term_cond[8] = "-cCsSPRI"; /* normal, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal */
|
||||
const char sess_fin_state[8] = "-RCHDLQ7"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, unknown */
|
||||
const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
|
||||
const char sess_cookie[4] = "NIDV"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie */
|
||||
const char sess_set_cookie[8] = "N1I3PD5R"; /* No set-cookie, unknown, Set-Cookie Inserted, unknown,
|
||||
Set-cookie seen and left unchanged (passive), Set-cookie Deleted,
|
||||
|
@ -451,6 +451,17 @@ int process_cli(struct session *t)
|
||||
*/
|
||||
tv_eternity(&req->rex);
|
||||
|
||||
|
||||
/* When a connection is tarpitted, we use the queue timeout for the
|
||||
* tarpit delay, which currently happens to be the server's connect
|
||||
* timeout. If unset, then set it to zero because we really want it
|
||||
* to expire at one moment.
|
||||
*/
|
||||
if (t->flags & SN_CLTARPIT) {
|
||||
tv_delayfrom(&req->cex, &now,
|
||||
t->proxy->contimeout ? t->proxy->contimeout : 0);
|
||||
}
|
||||
|
||||
goto process_data;
|
||||
}
|
||||
|
||||
@ -637,23 +648,27 @@ int process_cli(struct session *t)
|
||||
if (regexec(exp->preg, req->h, MAX_MATCH, pmatch, 0) == 0) {
|
||||
switch (exp->action) {
|
||||
case ACT_ALLOW:
|
||||
if (!(t->flags & SN_CLDENY))
|
||||
if (!(t->flags & (SN_CLDENY | SN_CLTARPIT)))
|
||||
t->flags |= SN_CLALLOW;
|
||||
break;
|
||||
case ACT_REPLACE:
|
||||
if (!(t->flags & SN_CLDENY)) {
|
||||
if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) {
|
||||
int len = exp_replace(trash, req->h, exp->replace, pmatch);
|
||||
ptr += buffer_replace2(req, req->h, ptr, trash, len);
|
||||
}
|
||||
break;
|
||||
case ACT_REMOVE:
|
||||
if (!(t->flags & SN_CLDENY))
|
||||
if (!(t->flags & (SN_CLDENY | SN_CLTARPIT)))
|
||||
delete_header = 1;
|
||||
break;
|
||||
case ACT_DENY:
|
||||
if (!(t->flags & SN_CLALLOW))
|
||||
if (!(t->flags & (SN_CLALLOW | SN_CLTARPIT)))
|
||||
t->flags |= SN_CLDENY;
|
||||
break;
|
||||
case ACT_TARPIT:
|
||||
if (!(t->flags & (SN_CLALLOW | SN_CLDENY)))
|
||||
t->flags |= SN_CLTARPIT;
|
||||
break;
|
||||
case ACT_PASS: /* we simply don't deny this one */
|
||||
break;
|
||||
}
|
||||
@ -678,7 +693,7 @@ int process_cli(struct session *t)
|
||||
*/
|
||||
if (!delete_header &&
|
||||
(t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL)
|
||||
&& !(t->flags & SN_CLDENY) && (ptr >= req->h + 8)
|
||||
&& !(t->flags & (SN_CLDENY|SN_CLTARPIT)) && (ptr >= req->h + 8)
|
||||
&& (strncasecmp(req->h, "Cookie: ", 8) == 0)) {
|
||||
char *p1, *p2, *p3, *p4;
|
||||
char *del_colon, *del_cookie, *colon;
|
||||
@ -938,7 +953,7 @@ int process_cli(struct session *t)
|
||||
} /* end of cookie processing on this header */
|
||||
|
||||
/* let's look if we have to delete this header */
|
||||
if (delete_header && !(t->flags & SN_CLDENY)) {
|
||||
if (delete_header && !(t->flags & (SN_CLDENY|SN_CLTARPIT))) {
|
||||
buffer_replace2(req, req->h, req->lr, NULL, 0);
|
||||
/* WARNING: ptr is not valid anymore, since the header may have
|
||||
* been deleted or truncated ! */
|
||||
@ -1338,6 +1353,27 @@ int process_srv(struct session *t)
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
if (t->flags & SN_CLTARPIT) {
|
||||
/* This connection is being tarpitted. The CLIENT side has
|
||||
* already set the connect expiration date to the right
|
||||
* timeout. We just have to check that it has not expired.
|
||||
*/
|
||||
if (tv_cmp2_ms(&req->cex, &now) > 0)
|
||||
return 0;
|
||||
|
||||
/* We will set the queue timer to the time spent, just for
|
||||
* logging purposes. We fake a 500 server error, so that the
|
||||
* attacker will not suspect his connection has been tarpitted.
|
||||
* It will not cause trouble to the logs because we can exclude
|
||||
* the tarpitted connections by filtering on the 'PT' status flags.
|
||||
*/
|
||||
tv_eternity(&req->cex);
|
||||
t->logs.t_queue = tv_diff(&t->logs.tv_accept, &now);
|
||||
srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T,
|
||||
500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Right now, we will need to create a connection to the server.
|
||||
* We might already have tried, and got a connection pending, in
|
||||
* which case we will not do anything till it's pending. It's up
|
||||
|
Loading…
x
Reference in New Issue
Block a user