mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-11 16:29:36 +00:00
MEDIUM: http: add http-request 'add-header' and 'set-header' to build headers
These two new statements allow to pass information extracted from the request to the server. It's particularly useful for passing SSL information to the server, but may be used for various other purposes such as combining headers together to emulate internal variables.
This commit is contained in:
parent
b83bc1e1c1
commit
20b0de56d4
@ -2606,22 +2606,54 @@ http-check send-state
|
||||
|
||||
See also : "option httpchk", "http-check disable-on-404"
|
||||
|
||||
http-request { allow | deny | auth [realm <realm>] }
|
||||
http-request { allow | deny | auth [realm <realm>] |
|
||||
add-header <name> <fmt> | set-header <name> <fmt> }
|
||||
[ { if | unless } <condition> ]
|
||||
Access control for Layer 7 requests
|
||||
|
||||
May be used in sections: defaults | frontend | listen | backend
|
||||
no | yes | yes | yes
|
||||
|
||||
These set of options allow to fine control access to a
|
||||
frontend/listen/backend. Each option may be followed by if/unless and acl.
|
||||
First option with matched condition (or option without condition) is final.
|
||||
For "deny" a 403 error will be returned, for "allow" normal processing is
|
||||
performed, for "auth" a 401/407 error code is returned so the client
|
||||
should be asked to enter a username and password.
|
||||
The http-request statement defines a set of rules which apply to layer 7
|
||||
processing. The rules are evaluated in their declaration order when they are
|
||||
met in a frontend, listen or backend section. Any rule may optionally be
|
||||
followed by an ACL-based condition, in which case it will only be evaluated
|
||||
if the condition is true.
|
||||
|
||||
There is no fixed limit to the number of http-request statements per
|
||||
instance.
|
||||
The first keyword is the rule's action. Currently supported actions include :
|
||||
- "allow" : this stops the evaluation of the rules and lets the request
|
||||
pass the check. No further "http-request" rules are evaluated.
|
||||
|
||||
- "deny" : this stops the evaluation of the rules and immediately rejects
|
||||
the request and emits an HTTP 403 error. No further "http-request" rules
|
||||
are evaluated.
|
||||
|
||||
- "auth" : this stops the evaluation of the rules and immediately responds
|
||||
with an HTTP 401 or 407 error code to invite the user to present a valid
|
||||
user name and password. No further "http-request" rules are evaluated. An
|
||||
optional "realm" parameter is supported, it sets the authentication realm
|
||||
that is returned with the response (typically the application's name).
|
||||
|
||||
- "add-header" appends an HTTP header field whose name is specified in
|
||||
<name> and whose value is defined by <fmt> which follows the log-format
|
||||
rules (see Custom Log Format in section 8.2.4). This is particularly
|
||||
useful to pass connection-specific information to the server (eg: the
|
||||
client's SSL certificate), or to combine several headers into one. This
|
||||
rule is not final, so it is possible to add other similar rules. Note
|
||||
that header addition is performed immediately, so one rule might reuse
|
||||
the resulting header from a previous rule.
|
||||
|
||||
- "set-header" does the same as "add-header" except that the header name
|
||||
is first removed if it existed. This is useful when passing security
|
||||
information to the server, where the header must not be manipulated by
|
||||
external users.
|
||||
|
||||
There is no limit to the number of http-request statements per instance.
|
||||
|
||||
It is important to know that http-request rules are processed very early in
|
||||
the HTTP processing, just after "block" rules and before "reqdel" or "reqrep"
|
||||
rules. That way, headers added by "add-header"/"set-header" are visible by
|
||||
almost all further ACL rules.
|
||||
|
||||
Example:
|
||||
acl nagios src 192.168.129.3
|
||||
@ -2635,9 +2667,19 @@ http-request { allow | deny | auth [realm <realm>] }
|
||||
|
||||
Example:
|
||||
acl auth_ok http_auth_group(L1) G1
|
||||
|
||||
http-request auth unless auth_ok
|
||||
|
||||
Example:
|
||||
http-request set-header X-Haproxy-Current-Date %T
|
||||
http-request set-header X-SSL %[ssl_fc]
|
||||
http-request set-header X-SSL-Session_ID %[ssl_fc_session_id]
|
||||
http-request set-header X-SSL-Client-Verify %[ssl_c_verify]
|
||||
http-request set-header X-SSL-Client-DN %{+Q}[ssl_c_s_dn]
|
||||
http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)]
|
||||
http-request set-header X-SSL-Issuer %{+Q}[ssl_c_i_dn]
|
||||
http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
|
||||
http-request set-header X-SSL-Client-NotAfter %{+Q}[ssl_c_notafter]
|
||||
|
||||
See also : "stats http-request", section 3.4 about userlists and section 7
|
||||
about ACL usage.
|
||||
|
||||
|
@ -240,8 +240,10 @@ enum {
|
||||
HTTP_REQ_ACT_UNKNOWN = 0,
|
||||
HTTP_REQ_ACT_ALLOW,
|
||||
HTTP_REQ_ACT_DENY,
|
||||
HTTP_REQ_ACT_HTTP_AUTH,
|
||||
HTTP_REQ_ACT_MAX
|
||||
HTTP_REQ_ACT_AUTH,
|
||||
HTTP_REQ_ACT_ADD_HDR,
|
||||
HTTP_REQ_ACT_SET_HDR,
|
||||
HTTP_REQ_ACT_MAX /* must always be last */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -347,6 +349,11 @@ struct http_req_rule {
|
||||
struct {
|
||||
char *realm;
|
||||
} auth; /* arg used by "auth" */
|
||||
struct {
|
||||
char *name; /* header name */
|
||||
int name_len; /* header name's length */
|
||||
struct list fmt; /* log-format compatible expression */
|
||||
} hdr_add; /* args used by "add-header" and "set-header" */
|
||||
} arg; /* arguments used by some actions */
|
||||
};
|
||||
|
||||
|
@ -2585,8 +2585,12 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!LIST_ISEMPTY(&curproxy->http_req_rules) && !LIST_PREV(&curproxy->http_req_rules, struct http_req_rule *, list)->cond) {
|
||||
Warning("parsing [%s:%d]: previous '%s' action has no condition attached, further entries are NOOP.\n",
|
||||
if (!LIST_ISEMPTY(&curproxy->http_req_rules) &&
|
||||
!LIST_PREV(&curproxy->http_req_rules, struct http_req_rule *, list)->cond &&
|
||||
(LIST_PREV(&curproxy->http_req_rules, struct http_req_rule *, list)->action == HTTP_REQ_ACT_ALLOW ||
|
||||
LIST_PREV(&curproxy->http_req_rules, struct http_req_rule *, list)->action == HTTP_REQ_ACT_DENY ||
|
||||
LIST_PREV(&curproxy->http_req_rules, struct http_req_rule *, list)->action == HTTP_REQ_ACT_AUTH)) {
|
||||
Warning("parsing [%s:%d]: previous '%s' action is final and has no condition attached, further entries are NOOP.\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
|
@ -3061,13 +3061,16 @@ int http_handle_stats(struct session *s, struct channel *req)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns a pointer to the first rule which forbids access (deny or http_auth),
|
||||
* or NULL if everything's OK.
|
||||
/* Executes the http-request rules <rules> for session <s>, proxy <px> and
|
||||
* transaction <txn>. Returns NULL if it executed all rules, or a pointer to
|
||||
* the last rule if it had to stop before the end (auth, deny, allow). It may
|
||||
* set the TX_CLDENY on txn->flags if it encounters a deny rule.
|
||||
*/
|
||||
static inline struct http_req_rule *
|
||||
static struct http_req_rule *
|
||||
http_check_access_rule(struct proxy *px, struct list *rules, struct session *s, struct http_txn *txn)
|
||||
{
|
||||
struct http_req_rule *rule;
|
||||
struct hdr_ctx ctx;
|
||||
|
||||
list_for_each_entry(rule, rules, list) {
|
||||
int ret = 1;
|
||||
@ -3085,13 +3088,36 @@ http_check_access_rule(struct proxy *px, struct list *rules, struct session *s,
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (rule->action == HTTP_REQ_ACT_ALLOW)
|
||||
return NULL; /* no problem */
|
||||
else
|
||||
return rule; /* most likely a deny or auth rule */
|
||||
switch (rule->action) {
|
||||
case HTTP_REQ_ACT_ALLOW:
|
||||
return rule;
|
||||
case HTTP_REQ_ACT_DENY:
|
||||
txn->flags |= TX_CLDENY;
|
||||
return rule;
|
||||
case HTTP_REQ_ACT_AUTH:
|
||||
return rule;
|
||||
case HTTP_REQ_ACT_SET_HDR:
|
||||
ctx.idx = 0;
|
||||
/* remove all occurrences of the header */
|
||||
while (http_find_header2(rule->arg.hdr_add.name, rule->arg.hdr_add.name_len,
|
||||
txn->req.chn->buf->p, &txn->hdr_idx, &ctx)) {
|
||||
http_remove_header2(&txn->req, &txn->hdr_idx, &ctx);
|
||||
}
|
||||
/* now fall through to header addition */
|
||||
|
||||
case HTTP_REQ_ACT_ADD_HDR:
|
||||
chunk_printf(&trash, "%s: ", rule->arg.hdr_add.name);
|
||||
memcpy(trash.str, rule->arg.hdr_add.name, rule->arg.hdr_add.name_len);
|
||||
trash.len = rule->arg.hdr_add.name_len;
|
||||
trash.str[trash.len++] = ':';
|
||||
trash.str[trash.len++] = ' ';
|
||||
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
|
||||
http_header_add_tail2(&txn->req, &txn->hdr_idx, trash.str, trash.len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return rule;
|
||||
}
|
||||
|
||||
/* This stream analyser runs all HTTP request processing which is common to
|
||||
@ -3163,7 +3189,7 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
|
||||
do_stats = 0;
|
||||
|
||||
/* return a 403 if either rule has blocked */
|
||||
if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_DENY) {
|
||||
if (txn->flags & TX_CLDENY) {
|
||||
txn->status = 403;
|
||||
s->logs.tv_request = now;
|
||||
stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_403));
|
||||
@ -3267,7 +3293,7 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
|
||||
/* we can be blocked here because the request needs to be authenticated,
|
||||
* either to pass or to access stats.
|
||||
*/
|
||||
if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_HTTP_AUTH) {
|
||||
if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_AUTH) {
|
||||
char *realm = http_req_last_rule->arg.auth.realm;
|
||||
|
||||
if (!realm)
|
||||
@ -7970,7 +7996,7 @@ void free_http_req_rules(struct list *r) {
|
||||
|
||||
list_for_each_entry_safe(pr, tr, r, list) {
|
||||
LIST_DEL(&pr->list);
|
||||
if (pr->action == HTTP_REQ_ACT_HTTP_AUTH)
|
||||
if (pr->action == HTTP_REQ_ACT_AUTH)
|
||||
free(pr->arg.auth.realm);
|
||||
|
||||
free(pr);
|
||||
@ -7995,7 +8021,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
|
||||
rule->action = HTTP_REQ_ACT_DENY;
|
||||
cur_arg = 1;
|
||||
} else if (!strcmp(args[0], "auth")) {
|
||||
rule->action = HTTP_REQ_ACT_HTTP_AUTH;
|
||||
rule->action = HTTP_REQ_ACT_AUTH;
|
||||
cur_arg = 1;
|
||||
|
||||
while(*args[cur_arg]) {
|
||||
@ -8006,8 +8032,23 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
|
||||
} else
|
||||
break;
|
||||
}
|
||||
} else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) {
|
||||
rule->action = *args[0] == 'a' ? HTTP_REQ_ACT_ADD_HDR : HTTP_REQ_ACT_SET_HDR;
|
||||
cur_arg = 1;
|
||||
|
||||
if (!*args[cur_arg] || !*args[cur_arg+1] || *args[cur_arg+2]) {
|
||||
Alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
|
||||
file, linenum, args[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rule->arg.hdr_add.name = strdup(args[cur_arg]);
|
||||
rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
|
||||
LIST_INIT(&rule->arg.hdr_add.fmt);
|
||||
parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, PR_MODE_HTTP);
|
||||
cur_arg += 2;
|
||||
} else {
|
||||
Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', but got '%s'%s.\n",
|
||||
Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'add-header', 'set-header', but got '%s'%s.\n",
|
||||
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
|
||||
return NULL;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user