diff --git a/include/types/proxy.h b/include/types/proxy.h index bdb42b904..fabc93640 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -170,6 +170,7 @@ struct proxy { char *name; /* default backend name during config parse */ } defbe; struct list acl; /* ACL declared on this proxy */ + struct list req_acl; /* request ACL: allow/deny/http-auth */ struct list block_cond; /* early blocking conditions (chained) */ struct list redirect_rules; /* content redirecting rules (chained) */ struct list switching_rules; /* content switching rules (chained) */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 5c181f16c..ef34760fd 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -882,6 +882,7 @@ static void init_new_proxy(struct proxy *p) memset(p, 0, sizeof(struct proxy)); LIST_INIT(&p->pendconns); LIST_INIT(&p->acl); + LIST_INIT(&p->req_acl); LIST_INIT(&p->block_cond); LIST_INIT(&p->redirect_rules); LIST_INIT(&p->mon_fail_cond); @@ -1927,6 +1928,31 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } curproxy->conn_retries = atol(args[1]); } + else if (!strcmp(args[0], "http-request")) { /* request access control: allow/deny/auth */ + struct req_acl_rule *req_acl; + + if (curproxy == &defproxy) { + Alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + + if (!LIST_ISEMPTY(&curproxy->req_acl) && !LIST_PREV(&curproxy->req_acl, struct req_acl_rule *, list)->cond) { + Warning("parsing [%s:%d]: previous '%s' action has no condition attached, further entries are NOOP.\n", + file, linenum, args[0]); + err_code |= ERR_WARN; + } + + req_acl = parse_auth_cond((const char **)args + 1, file, linenum, &curproxy->acl, &curproxy->acl_requires); + + if (!req_acl) { + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + + LIST_ADDQ(&curproxy->req_acl, &req_acl->list); + } else if (!strcmp(args[0], "block")) { /* early blocking based on ACLs */ if (curproxy == &defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); diff --git a/src/haproxy.c b/src/haproxy.c index 0393ebfee..905febfae 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -859,6 +859,8 @@ void deinit(void) l = l_next; }/* end while(l) */ + req_acl_free(&p->req_acl); + pool_destroy2(p->req_cap_pool); pool_destroy2(p->rsp_cap_pool); pool_destroy2(p->hdr_idx_pool); diff --git a/src/proto_http.c b/src/proto_http.c index b2dd232ce..59952a435 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1351,7 +1351,6 @@ const char *http_parse_stsline(struct http_msg *msg, const char *msg_buf, return NULL; } - /* * This function parses a request line between and , starting with * parser state . Only states HTTP_MSG_RQMETH, HTTP_MSG_RQMETH_SP, @@ -2723,6 +2722,7 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s struct http_txn *txn = &s->txn; struct http_msg *msg = &txn->req; struct acl_cond *cond; + struct req_acl_rule *req_acl, *req_acl_final = NULL; struct redirect_rule *rule; struct cond_wordlist *wl; int del_ka, del_cl; @@ -2762,6 +2762,29 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s } } + list_for_each_entry(req_acl, &px->req_acl, list) { + int ret = 1; + + if (req_acl->action >= PR_REQ_ACL_ACT_MAX) + continue; + + /* check condition, but only if attached */ + if (req_acl->cond) + ret = acl_exec_cond(req_acl->cond, px, s, txn, ACL_DIR_REQ); + + if (ret) { + req_acl_final = req_acl; + break; + } + } + + if (req_acl_final && req_acl_final->action == PR_REQ_ACL_ACT_DENY) { + txn->status = 403; + s->logs.tv_request = now; + stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403)); + goto return_prx_cond; + } + /* try headers filters */ if (px->req_exp != NULL) { if (apply_filters_to_request(s, req, px) < 0) @@ -2864,6 +2887,16 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s goto return_bad_req; } + if (req_acl_final && req_acl_final->action == PR_REQ_ACL_ACT_HTTP_AUTH) { + struct chunk msg; + + sprintf(trash, HTTP_401_fmt, req_acl->http_auth.realm?req_acl->http_auth.realm:px->id); + chunk_initlen(&msg, trash, sizeof(trash), strlen(trash)); + txn->status = 401; + stream_int_retnclose(req->prod, &msg); + goto return_prx_cond; + } + /* check if stats URI was requested, and if an auth is needed */ if (px->uri_auth != NULL && (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {