mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-04-03 01:51:05 +02:00
MEDIUM: http_act: Rework *-headers-bin actions
These actions were added recently and it appeared the way binary headers were retrieved could be simplified. First, there is no reason to retrieve a base64 encoded string. It is possible to rely on the binary string directly. "b64dec" converter can be used to perform a base64 decoding if necessary. Then, using a log-format string is quite overkill and probably conterintuitive. Most of time, the headers will be retrieved from a variable. So a sample expression is easier to use. Thanks to the previous patch, it is quite easy to achieve. This patch relies on the commit "MINOR: action: Add a sample expression field in arguments used by HTTP actions". The documentation was updated accordingly.
This commit is contained in:
parent
2adcdbacc2
commit
8c00df7448
@ -15314,29 +15314,28 @@ add-header <name> <fmt>
|
||||
previous rule.
|
||||
|
||||
|
||||
add-headers-bin <fmt> [ prefix <str> ]
|
||||
add-headers-bin <expr> [ prefix <str> ]
|
||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
- | - | - | - | - | X | X | X
|
||||
|
||||
This is a variant of the "add-header" action where the header names and
|
||||
values are passed as a base64 encoded string of varint encoded header names
|
||||
and values. See the "req.hdrs_bin" sample fetch about the varint format. This
|
||||
is useful when you want to set multiple headers at once, without having to
|
||||
know the header names in advance. Note that these headers have not been
|
||||
validated by the HTTP parser and could lead to emitting invalid messages and
|
||||
in worst cases lead to request smuggling attacks. The number of headers
|
||||
inserted are also of importance, as that is limited by tune.http.maxhdr.
|
||||
Optional prefix will only set the headers from the encoded string that
|
||||
start with <str>.
|
||||
values are passed as a varint encoded binary string. See the "req.hdrs_bin"
|
||||
sample fetch about the varint format. This is useful when you want to set
|
||||
multiple headers at once, without having to know the header names in
|
||||
advance. Note that these headers have not been validated by the HTTP parser
|
||||
and could lead to emitting invalid messages and in worst cases lead to
|
||||
request smuggling attacks. The number of headers inserted are also of
|
||||
importance, as that is limited by tune.http.maxhdr. Optional prefix will
|
||||
only set the headers from the encoded string that start with <str>.
|
||||
|
||||
Example:
|
||||
|
||||
# This would reset the Accept/UA/Host headers to their initial values
|
||||
http-request set-var(txn.oldheaders) req.hdrs_bin,base64
|
||||
http-request set-var(txn.oldheaders) req.hdrs_bin
|
||||
http-request del-header Accept
|
||||
http-request del-header User-Agent
|
||||
http-request del-header Host
|
||||
http-request add-headers-bin %[var(txn.oldheaders)]
|
||||
http-request add-headers-bin var(txn.oldheaders)
|
||||
|
||||
|
||||
allow
|
||||
@ -15481,17 +15480,17 @@ del-header <name> [ -m <meth> ]
|
||||
method is used.
|
||||
|
||||
|
||||
del-headers-bin <fmt> [ -m <meth> ]
|
||||
del-headers-bin <expr> [ -m <meth> ]
|
||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
- | - | - | - | - | X | X | X
|
||||
|
||||
This removes all HTTP header fields whose names are specified in <fmt>. <fmt>
|
||||
is a base64 encoded varint encoded string of all header names that should be
|
||||
deleted. See "add-headers-bin" and "set-headers-bin" for the description of
|
||||
encoding and examples. <meth> is the matching method, applied on all the
|
||||
header names. Supported matching methods are "str" (exact match), "beg"
|
||||
(prefix match), "end" (suffix match) and "sub" (substring match). The "reg"
|
||||
(regex match) is not supported due to unpredictable performance during
|
||||
This removes all HTTP header fields whose names are specified in <expr>.
|
||||
<expr> must return a varint encoded binary string of all header names that
|
||||
should be deleted. See "add-headers-bin" and "set-headers-bin" for the
|
||||
description of encoding and examples. <meth> is the matching method, applied
|
||||
on all the header names. Supported matching methods are "str" (exact match),
|
||||
"beg" (prefix match), "end" (suffix match) and "sub" (substring match). The
|
||||
"reg" (regex match) is not supported due to unpredictable performance during
|
||||
runtime. If not specified, exact matching method is used.
|
||||
|
||||
|
||||
@ -16395,29 +16394,28 @@ set-header <name> <fmt>
|
||||
http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
|
||||
http-request set-header X-SSL-Client-NotAfter %{+Q}[ssl_c_notafter]
|
||||
|
||||
set-headers-bin <fmt> [ prefix <str> ]
|
||||
set-headers-bin <expr> [ prefix <str> ]
|
||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
- | - | - | - | - | X | X | X
|
||||
|
||||
This is a variant of the "set-header" action where the header names and
|
||||
values are passed as a base64 encoded string of varint encoded header names
|
||||
and values. See the "req.hdrs_bin" sample fetch about the varint format. This
|
||||
is useful when you want to set multiple headers at once, without having to
|
||||
know the header names in advance. Note that these headers have not been
|
||||
validated by the HTTP parser and could lead to emitting invalid messages and
|
||||
in worst cases lead to request smuggling attacks. The number of headers
|
||||
inserted are also of importance, as that is limited by tune.http.maxhdr.
|
||||
Optional prefix will only set the headers from the encoded string that
|
||||
start with <str>.
|
||||
values are passed as a varint encoded binary string. See the "req.hdrs_bin"
|
||||
sample fetch about the varint format. This is useful when you want to set
|
||||
multiple headers at once, without having to know the header names in
|
||||
advance. Note that these headers have not been validated by the HTTP parser
|
||||
and could lead to emitting invalid messages and in worst cases lead to
|
||||
request smuggling attacks. The number of headers inserted are also of
|
||||
importance, as that is limited by tune.http.maxhdr. Optional prefix will
|
||||
only set the headers from the encoded string that start with <str>.
|
||||
|
||||
Example:
|
||||
|
||||
# This would reset the Accept/UA/Host headers to their initial values
|
||||
http-request set-var(txn.oldheaders) req.hdrs_bin,base64
|
||||
http-request set-var(txn.oldheaders) req.hdrs_bin
|
||||
http-request del-header Accept
|
||||
http-request del-header User-Agent
|
||||
http-request del-header Host
|
||||
http-request set-headers-bin %[var(txn.oldheaders)]
|
||||
http-request set-headers-bin var(txn.oldheaders)
|
||||
|
||||
|
||||
set-log-level <level>
|
||||
|
||||
241
src/http_act.c
241
src/http_act.c
@ -1489,63 +1489,36 @@ static enum act_return http_action_set_headers_bin(struct act_rule *rule, struct
|
||||
{
|
||||
struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
|
||||
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||
struct sample *hdrs_bin;
|
||||
char *p, *end;
|
||||
enum act_return ret = ACT_RET_CONT;
|
||||
struct buffer *binstring, *decoded = NULL;
|
||||
struct http_hdr_ctx ctx;
|
||||
struct ist n, v;
|
||||
uint64_t sz = 0;
|
||||
size_t offset = 0;
|
||||
int bytes_read;
|
||||
char *oldarea = NULL;
|
||||
|
||||
binstring = alloc_trash_chunk();
|
||||
if (!binstring)
|
||||
goto fail_alloc;
|
||||
decoded = alloc_trash_chunk();
|
||||
if (!decoded)
|
||||
goto fail_alloc;
|
||||
hdrs_bin = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.http.expr, SMP_T_BIN);
|
||||
if (!hdrs_bin)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
oldarea = decoded->area;
|
||||
|
||||
binstring->data = build_logline(s, binstring->area, binstring->size, &rule->arg.http.fmt);
|
||||
|
||||
bytes_read = base64dec(binstring->area, binstring->data, decoded->area, decoded->size);
|
||||
if (bytes_read < 0)
|
||||
goto fail_rewrite;
|
||||
|
||||
decoded->data = bytes_read;
|
||||
|
||||
while (1) {
|
||||
int ret;
|
||||
|
||||
ret = decode_varint(&decoded->area, decoded->area + decoded->data - offset, &sz);
|
||||
if (ret == -1)
|
||||
p = b_orig(&hdrs_bin->data.u.str);
|
||||
end = b_tail(&hdrs_bin->data.u.str);
|
||||
while (p < end) {
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
goto fail_rewrite;
|
||||
offset += ret;
|
||||
|
||||
if (!sz) {
|
||||
ret = decode_varint(&decoded->area, decoded->area + decoded->data - offset, &sz);
|
||||
if (ret == -1)
|
||||
goto fail_rewrite;
|
||||
offset += ret;
|
||||
if (!sz)
|
||||
goto leave;
|
||||
else
|
||||
if (decode_varint(&p, end, &sz) == -1 || sz > 0)
|
||||
goto fail_rewrite;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
n = ist2(decoded->area, sz);
|
||||
offset += sz;
|
||||
decoded->area += sz;
|
||||
n = ist2(p, sz);
|
||||
p += sz;
|
||||
|
||||
ret = decode_varint(&decoded->area, decoded->area + decoded->data - offset, &sz);
|
||||
if (ret == -1)
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
goto fail_rewrite;
|
||||
offset += ret;
|
||||
|
||||
v = ist2(decoded->area, sz);
|
||||
offset += sz;
|
||||
decoded->area += sz;
|
||||
v = ist2(p, sz);
|
||||
p += sz;
|
||||
|
||||
if (istlen(rule->arg.http.str) && !istmatch(n, rule->arg.http.str))
|
||||
continue;
|
||||
@ -1565,19 +1538,11 @@ static enum act_return http_action_set_headers_bin(struct act_rule *rule, struct
|
||||
goto fail_rewrite;
|
||||
}
|
||||
|
||||
leave:
|
||||
free_trash_chunk(binstring);
|
||||
/* decode_varint moves the area pointer, so return it to the correct position */
|
||||
if (decoded)
|
||||
decoded->area = oldarea;
|
||||
free_trash_chunk(decoded);
|
||||
return ret;
|
||||
|
||||
fail_alloc:
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_RESOURCE;
|
||||
/* invalid encoding */
|
||||
ret = ACT_RET_ERR;
|
||||
goto leave;
|
||||
|
||||
leave:
|
||||
return ret;
|
||||
|
||||
fail_rewrite:
|
||||
if (sess->fe_tgcounters)
|
||||
@ -1678,14 +1643,15 @@ static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg
|
||||
static enum act_parse_ret parse_http_set_headers_bin(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
int cap = 0, cur_arg;
|
||||
struct sample_expr *expr;
|
||||
unsigned int where;
|
||||
int cur_arg;
|
||||
|
||||
if (args[*orig_arg-1][0] == 's')
|
||||
rule->action = 0; // set-header
|
||||
else
|
||||
rule->action = 1; // add-header
|
||||
rule->action_ptr = http_action_set_headers_bin;
|
||||
|
||||
rule->release_ptr = release_http_action;
|
||||
lf_expr_init(&rule->arg.http.fmt);
|
||||
|
||||
@ -1695,35 +1661,39 @@ static enum act_parse_ret parse_http_set_headers_bin(const char **args, int *ori
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (rule->from == ACT_F_HTTP_REQ) {
|
||||
px->conf.args.ctx = ARGC_HRQ;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
cap |= SMP_VAL_FE_HRQ_HDR;
|
||||
if (px->cap & PR_CAP_BE)
|
||||
cap |= SMP_VAL_BE_HRQ_HDR;
|
||||
}
|
||||
else{
|
||||
px->conf.args.ctx = ARGC_HRS;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
cap |= SMP_VAL_FE_HRS_HDR;
|
||||
if (px->cap & PR_CAP_BE)
|
||||
cap |= SMP_VAL_BE_HRS_HDR;
|
||||
}
|
||||
if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
|
||||
expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
|
||||
err, &px->conf.args, NULL);
|
||||
if (!expr)
|
||||
return ACT_RET_PRS_ERR;
|
||||
|
||||
where = 0;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
|
||||
if (px->cap & PR_CAP_BE)
|
||||
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
|
||||
|
||||
if (!(expr->fetch->val & where)) {
|
||||
memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
|
||||
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||
release_sample_expr(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
/* Check if an argument is available */
|
||||
if (*args[cur_arg+1] && strcmp(args[cur_arg+1], "prefix") == 0 ) {
|
||||
if(!*args[cur_arg+2]) {
|
||||
if (strcmp(args[cur_arg], "prefix") == 0 ) {
|
||||
cur_arg++;
|
||||
if(!*args[cur_arg]) {
|
||||
memprintf(err, "expects 1 argument: <headers>; or 3 arguments: <headers> prefix <pfx>");
|
||||
release_sample_expr(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
cur_arg += 2;
|
||||
rule->arg.http.str = ist(strdup(args[cur_arg]));
|
||||
cur_arg++;
|
||||
}
|
||||
|
||||
*orig_arg = cur_arg + 1;
|
||||
rule->arg.http.expr = expr;
|
||||
|
||||
*orig_arg = cur_arg;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
@ -1948,54 +1918,26 @@ static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct
|
||||
struct http_hdr_ctx ctx;
|
||||
struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
|
||||
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||
struct sample *hdrs_bin;
|
||||
char *p, *end;
|
||||
enum act_return ret = ACT_RET_CONT;
|
||||
struct buffer *binstring, *decoded = NULL;
|
||||
struct ist n;
|
||||
uint64_t sz = 0;
|
||||
size_t offset = 0;
|
||||
int bytes_read;
|
||||
char *oldarea = NULL;
|
||||
|
||||
binstring = alloc_trash_chunk();
|
||||
if (!binstring)
|
||||
goto fail_alloc;
|
||||
decoded = alloc_trash_chunk();
|
||||
if (!decoded)
|
||||
goto fail_alloc;
|
||||
hdrs_bin = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.http.expr, SMP_T_BIN);
|
||||
if (!hdrs_bin)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
oldarea = decoded->area;
|
||||
|
||||
binstring->data = build_logline(s, binstring->area, binstring->size, &rule->arg.http.fmt);
|
||||
|
||||
bytes_read = base64dec(binstring->area, binstring->data, decoded->area, decoded->size);
|
||||
if (bytes_read < 0) {
|
||||
goto fail_rewrite;
|
||||
}
|
||||
|
||||
decoded->data = bytes_read;
|
||||
|
||||
while (1) {
|
||||
int ret;
|
||||
|
||||
ret = decode_varint(&decoded->area, decoded->area + decoded->data - offset, &sz);
|
||||
if (ret == -1)
|
||||
p = b_orig(&hdrs_bin->data.u.str);
|
||||
end = b_tail(&hdrs_bin->data.u.str);
|
||||
while (p < end) {
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
goto fail_rewrite;
|
||||
offset += ret;
|
||||
if (!sz)
|
||||
goto leave;
|
||||
|
||||
if (!sz) {
|
||||
ret = decode_varint(&decoded->area, decoded->area + decoded->data - offset, &sz);
|
||||
if (ret == -1)
|
||||
goto fail_rewrite;
|
||||
offset += ret;
|
||||
if (!sz)
|
||||
goto leave;
|
||||
else
|
||||
goto fail_rewrite;
|
||||
}
|
||||
|
||||
n = ist2(decoded->area, sz);
|
||||
offset += sz;
|
||||
decoded->area += sz;
|
||||
n = ist2(p, sz);
|
||||
p += sz;
|
||||
|
||||
if (is_immutable_header(n))
|
||||
continue;
|
||||
@ -2024,19 +1966,11 @@ static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
free_trash_chunk(binstring);
|
||||
/* decode_varint moves the area pointer, so return it to the correct position */
|
||||
if (decoded)
|
||||
decoded->area = oldarea;
|
||||
free_trash_chunk(decoded);
|
||||
return ret;
|
||||
|
||||
fail_alloc:
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_RESOURCE;
|
||||
/* invalid encoding */
|
||||
ret = ACT_RET_ERR;
|
||||
goto leave;
|
||||
|
||||
leave:
|
||||
return ret;
|
||||
|
||||
fail_rewrite:
|
||||
if (sess->fe_tgcounters)
|
||||
@ -2064,7 +1998,9 @@ static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct
|
||||
static enum act_parse_ret parse_http_del_headers_bin(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
int cap = 0, cur_arg;
|
||||
struct sample_expr *expr;
|
||||
unsigned int where;
|
||||
int cur_arg;
|
||||
int pat_idx;
|
||||
|
||||
/* set exact matching (-m str) as default */
|
||||
@ -2079,40 +2015,37 @@ static enum act_parse_ret parse_http_del_headers_bin(const char **args, int *ori
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (rule->from == ACT_F_HTTP_REQ) {
|
||||
px->conf.args.ctx = ARGC_HRQ;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
cap |= SMP_VAL_FE_HRQ_HDR;
|
||||
if (px->cap & PR_CAP_BE)
|
||||
cap |= SMP_VAL_BE_HRQ_HDR;
|
||||
}
|
||||
else{
|
||||
px->conf.args.ctx = ARGC_HRS;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
cap |= SMP_VAL_FE_HRS_HDR;
|
||||
if (px->cap & PR_CAP_BE)
|
||||
cap |= SMP_VAL_BE_HRS_HDR;
|
||||
}
|
||||
expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
|
||||
err, &px->conf.args, NULL);
|
||||
if (!expr)
|
||||
return ACT_RET_PRS_ERR;
|
||||
|
||||
if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
|
||||
istfree(&rule->arg.http.str);
|
||||
where = 0;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
|
||||
if (px->cap & PR_CAP_BE)
|
||||
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
|
||||
|
||||
if (!(expr->fetch->val & where)) {
|
||||
memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
|
||||
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||
release_sample_expr(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
|
||||
|
||||
if (strcmp(args[cur_arg+1], "-m") == 0) {
|
||||
if (strcmp(args[cur_arg], "-m") == 0) {
|
||||
cur_arg++;
|
||||
if (!*args[cur_arg+1]) {
|
||||
if (!*args[cur_arg]) {
|
||||
memprintf(err, "-m flag expects exactly 1 argument");
|
||||
release_sample_expr(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
cur_arg++;
|
||||
pat_idx = pat_find_match_name(args[cur_arg]);
|
||||
switch (pat_idx) {
|
||||
case PAT_MATCH_REG:
|
||||
memprintf(err, "-m reg with is unsupported with del-header-bin due to performance reasons");
|
||||
release_sample_expr(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
case PAT_MATCH_STR:
|
||||
case PAT_MATCH_BEG:
|
||||
@ -2122,11 +2055,15 @@ static enum act_parse_ret parse_http_del_headers_bin(const char **args, int *ori
|
||||
break;
|
||||
default:
|
||||
memprintf(err, "-m with unsupported matching method '%s'", args[cur_arg]);
|
||||
release_sample_expr(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
cur_arg++;
|
||||
}
|
||||
|
||||
*orig_arg = cur_arg + 1;
|
||||
rule->arg.http.expr = expr;
|
||||
|
||||
*orig_arg = cur_arg;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user