>From 5368532099b8e8c2c6970df8a2d1463a7eaa72bc Mon Sep 17 00:00:00 2001
From: Krzysztof Piotr Oledzki <[email protected]>
Date: Tue, 27 Jan 2009 16:47:15 +0100
Subject: [MEDIUM] access control (block) rework - rfc
Allow more advanced access control:
- rename 'block' to 'deny'
- implement 'allow'
- implement 'order': allow,deny and deny,allow (default)
- implement new check_cond_list() helper function
RFC quality patch.
TODO: documentation
---
include/proto/acl.h | 5 +++++
include/types/proxy.h | 7 ++++++-
src/acl.c | 19 +++++++++++++++++++
src/cfgparse.c | 33 +++++++++++++++++++++++++++++----
src/haproxy.c | 8 +++++++-
src/proto_http.c | 39 +++++++++++++++++++++++++--------------
6 files changed, 91 insertions(+), 20 deletions(-)
diff --git a/include/proto/acl.h b/include/proto/acl.h
index fffce48..8ccc8f0 100644
--- a/include/proto/acl.h
+++ b/include/proto/acl.h
@@ -92,6 +92,11 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px,
struct session *l4, v
*/
struct acl *cond_find_require(struct acl_cond *cond, unsigned int require);
+/* Walk through <cond_list> and return 0 if all tests fail or 1 if at last one
test succeeds.
+ * This function checks the polarity required by IF/UNLESS.
+ */
+int check_cond_list(struct list *cond_list, struct proxy *px, struct session
*l4, void *l7, int dir);
+
/* Return a pointer to the ACL <name> within the list starting at <head>, or
* NULL if not found.
*/
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 5d0869c..b70a7e4 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -112,6 +112,9 @@
#define PR_O2_SPLIC_AUT 0x00000004 /* automatically use linux
kernel's splice() */
#define PR_O2_SPLIC_ANY
(PR_O2_SPLIC_REQ|PR_O2_SPLIC_RTR|PR_O2_SPLIC_AUT)
+#define PR_ORDER_DENY_ALLOW 0 /* last type of match wins: first
evaluate deny_cond then allow_cond, allow by default */
+#define PR_ORDER_ALLOW_DENY 1 /* last type of match wins: first
evaluate allow_cond then deny_cond, deny by default */
+
/* This structure is used to apply fast weighted round robin on a server group
*/
struct fwrr_group {
struct eb_root curr; /* tree for servers in "current" time range */
@@ -130,13 +133,15 @@ struct proxy {
int options; /* PR_O_REDISP, PR_O_TRANSP,
... */
int options2; /* PR_O2_* */
int mode; /* mode = PR_MODE_TCP,
PR_MODE_HTTP or PR_MODE_HEALTH */
+ int order; /* order = PR_ORDER_DENY_ALLOW
or PR_ORDER_ALLOW_DENY */
struct sockaddr_in dispatch_addr; /* the default address to
connect to */
union {
struct proxy *be; /* default backend, or NULL if
none set */
char *name; /* default backend name during
config parse */
} defbe;
struct list acl; /* ACL declared on this proxy */
- struct list block_cond; /* early blocking conditions
(chained) */
+ struct list allow_cond; /* early allow blocking
conditions (chained) */
+ struct list deny_cond; /* early deny blocking
conditions (chained) */
struct list redirect_rules; /* content redirecting rules
(chained) */
struct list switching_rules; /* content switching rules
(chained) */
struct { /* TCP request processing */
diff --git a/src/acl.c b/src/acl.c
index ed41e91..8cab8df 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1119,6 +1119,25 @@ struct acl *cond_find_require(struct acl_cond *cond,
unsigned int require)
return NULL;
}
+/* Walk through <cond_list> and return 0 if all tests fail or 1 if at last one
test succeeds.
+ * This function checks the polarity required by IF/UNLESS.
+ */
+int check_cond_list(struct list *cond_list, struct proxy *px, struct session
*l4, void *l7, int dir) {
+
+ struct acl_cond *cond;
+
+ list_for_each_entry(cond, cond_list, list) {
+ int ret = acl_exec_cond(cond, px, l4, l7, dir);
+
+ if (cond->pol == ACL_COND_UNLESS)
+ ret = !ret;
+
+ if (ret)
+ return 1;
+ }
+
+ return 0;
+}
/************************************************************************/
/* All supported keywords must be declared here. */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index f98d07e..46f4ba7 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -561,13 +561,15 @@ static void init_default_instance()
memset(&defproxy, 0, sizeof(defproxy));
defproxy.mode = PR_MODE_TCP;
defproxy.state = PR_STNEW;
+ defproxy.order = PR_ORDER_DENY_ALLOW;
defproxy.maxconn = cfg_maxpconn;
defproxy.conn_retries = CONN_RETRIES;
defproxy.logfac1 = defproxy.logfac2 = -1; /* log disabled */
LIST_INIT(&defproxy.pendconns);
LIST_INIT(&defproxy.acl);
- LIST_INIT(&defproxy.block_cond);
+ LIST_INIT(&defproxy.allow_cond);
+ LIST_INIT(&defproxy.deny_cond);
LIST_INIT(&defproxy.mon_fail_cond);
LIST_INIT(&defproxy.switching_rules);
@@ -641,7 +643,8 @@ int cfg_parse_listen(const char *file, int linenum, char
**args, int inv)
proxy = curproxy;
LIST_INIT(&curproxy->pendconns);
LIST_INIT(&curproxy->acl);
- LIST_INIT(&curproxy->block_cond);
+ LIST_INIT(&curproxy->allow_cond);
+ LIST_INIT(&curproxy->deny_cond);
LIST_INIT(&curproxy->redirect_rules);
LIST_INIT(&curproxy->mon_fail_cond);
LIST_INIT(&curproxy->switching_rules);
@@ -668,6 +671,7 @@ int cfg_parse_listen(const char *file, int linenum, char
**args, int inv)
curproxy->state = defproxy.state;
curproxy->options = defproxy.options;
curproxy->options2 = defproxy.options2;
+ curproxy->order = defproxy.order;
curproxy->lbprm.algo = defproxy.lbprm.algo;
curproxy->except_net = defproxy.except_net;
curproxy->except_mask = defproxy.except_mask;
@@ -1109,7 +1113,19 @@ int cfg_parse_listen(const char *file, int linenum, char
**args, int inv)
}
curproxy->conn_retries = atol(args[1]);
}
- else if (!strcmp(args[0], "block")) { /* early blocking based on ACLs
*/
+ else if (!strcmp(args[0], "order")) {
+ if (!strcasecmp(args[1], "allow,deny"))
+ curproxy->order = PR_ORDER_ALLOW_DENY;
+ else if (!strcasecmp(args[1], "deny,allow"))
+ curproxy->order = PR_ORDER_DENY_ALLOW;
+ else {
+ Alert("parsing [%s:%d]: unknow order '%s'.\n",
+ file, linenum, args[1]);
+
+ return -1;
+ }
+ }
+ else if (!strcmp(args[0], "block") || !strcmp(args[0], "allow") ||
!strcmp(args[0], "deny")) { /* early blocking based on ACLs */
int pol = ACL_COND_NONE;
struct acl_cond *cond;
@@ -1134,8 +1150,17 @@ int cfg_parse_listen(const char *file, int linenum, char
**args, int inv)
file, linenum);
return -1;
}
+
cond->line = linenum;
- LIST_ADDQ(&curproxy->block_cond, &cond->list);
+
+ if (!strcmp(args[0], "block"))
+ Alert("parsing [%s:%d]: '%s' is deprecated, please use
'deny' instead.\n",
+ file, linenum, args[0]);
+
+ if (!strcmp(args[0], "allow"))
+ LIST_ADDQ(&curproxy->allow_cond, &cond->list);
+ else
+ LIST_ADDQ(&curproxy->deny_cond, &cond->list);
}
else if (!strcmp(args[0], "redirect")) {
int pol = ACL_COND_NONE;
diff --git a/src/haproxy.c b/src/haproxy.c
index abf6eef..e7b8855 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -687,7 +687,13 @@ void deinit(void)
for (i = 0; i < p->nb_rspadd; i++)
free(p->rsp_add[i]);
- list_for_each_entry_safe(cond, condb, &p->block_cond, list) {
+ list_for_each_entry_safe(cond, condb, &p->allow_cond, list) {
+ LIST_DEL(&cond->list);
+ prune_acl_cond(cond);
+ free(cond);
+ }
+
+ list_for_each_entry_safe(cond, condb, &p->deny_cond, list) {
LIST_DEL(&cond->list);
prune_acl_cond(cond);
free(cond);
diff --git a/src/proto_http.c b/src/proto_http.c
index ecbc887..34399aa 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1847,10 +1847,10 @@ int http_process_request(struct session *s, struct
buffer *req)
*/
do {
- struct acl_cond *cond;
struct redirect_rule *rule;
struct proxy *rule_set = s->be;
cur_proxy = s->be;
+ int denyreq = 0;
/* first check whether we have some ACLs set to redirect this
request */
list_for_each_entry(rule, &cur_proxy->redirect_rules, list) {
@@ -1958,21 +1958,32 @@ int http_process_request(struct session *s, struct
buffer *req)
}
}
- /* first check whether we have some ACLs set to block this
request */
- list_for_each_entry(cond, &cur_proxy->block_cond, list) {
- int ret = acl_exec_cond(cond, cur_proxy, s, txn,
ACL_DIR_REQ);
+ /* check whether we should block this request */
+ if (cur_proxy->order == PR_ORDER_ALLOW_DENY) {
+ denyreq = 1;
- ret = acl_pass(ret);
- if (cond->pol == ACL_COND_UNLESS)
- ret = !ret;
+ if (check_cond_list(&cur_proxy->allow_cond, cur_proxy,
s, txn, ACL_DIR_REQ))
+ denyreq = 0;
- if (ret) {
- txn->status = 403;
- /* let's log the request time */
- s->logs.tv_request = now;
- stream_int_retnclose(req->prod,
error_message(s, HTTP_ERR_403));
- goto return_prx_cond;
- }
+ if (check_cond_list(&cur_proxy->deny_cond, cur_proxy,
s, txn, ACL_DIR_REQ))
+ denyreq = 1;
+
+ } else {
+
+ if (check_cond_list(&cur_proxy->deny_cond, cur_proxy,
s, txn, ACL_DIR_REQ))
+ denyreq = 1;
+
+ if (check_cond_list(&cur_proxy->allow_cond, cur_proxy,
s, txn, ACL_DIR_REQ))
+ denyreq = 0;
+
+ }
+
+ if (denyreq) {
+ txn->status = 403;
+ /* let's log the request time */
+ s->logs.tv_request = now;
+ stream_int_retnclose(req->prod, error_message(s,
HTTP_ERR_403));
+ goto return_prx_cond;
}
/* try headers filters */
--
1.6.0.4