From 95fa4698f150867dee28e8f0eda4c09aa565c6d7 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 1 Feb 2010 13:05:50 +0100 Subject: [PATCH] [MEDIUM] add support for anonymous ACLs Anonymous ACLs allow the declaration of rules which rely directly on ACL expressions without passing via the declaration of an ACL. Example : With named ACLs : acl site_dead nbsrv(dynamic) lt 2 acl site_dead nbsrv(static) lt 2 monitor fail if site_dead With anonymous ACLs : monitor fail if { nbsrv(dynamic) lt 2 } || { nbsrv(static) lt 2 } --- doc/configuration.txt | 30 +++++++++++++++++++++++++++ src/acl.c | 47 ++++++++++++++++++++++++++++++++++--------- src/cfgparse.c | 46 ++++++++++++++++++------------------------ 3 files changed, 88 insertions(+), 35 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index ccb7e1cc2..4e6f63102 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -6216,6 +6216,36 @@ and to every request on the "img", "video", "download" and "ftp" hosts : use_backend static if host_static or host_www url_static use_backend www if host_www +It is also possible to form rules using "anonymous ACLs". Those are unnamed ACL +expressions that are built on the fly without needing to be declared. They must +be enclosed between braces, with a space before and after each brace (because +the braces must be seen as independant words). Example : + + The following rule : + + acl missing_cl hdr_cnt(Content-length) eq 0 + block if METH_POST missing_cl + + Can also be written that way : + + block if METH_POST { hdr_cnt(Content-length) eq 0 } + +It is generally not recommended to use this construct because it's a lot easier +to leave errors in the configuration when written that way. However, for very +simple rules matching only one source IP address for instance, it can make more +sense to use them than to declare ACLs with random names. Another example of +good use is the following : + + With named ACLs : + + acl site_dead nbsrv(dynamic) lt 2 + acl site_dead nbsrv(static) lt 2 + monitor fail if site_dead + + With anonymous ACLs : + + monitor fail if { nbsrv(dynamic) lt 2 } || { nbsrv(static) lt 2 } + See section 4.2 for detailed help on the "block" and "use_backend" keywords. diff --git a/src/acl.c b/src/acl.c index 007751de6..c6c7484a3 100644 --- a/src/acl.c +++ b/src/acl.c @@ -963,16 +963,45 @@ struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int p continue; } - /* search for in the known ACL names. If we do not find - * it, let's look for it in the default ACLs, and if found, add - * it to the list of ACLs of this proxy. This makes it possible - * to override them. - */ - cur_acl = find_acl_by_name(word, known_acl); - if (cur_acl == NULL) { - cur_acl = find_acl_default(word, known_acl); - if (cur_acl == NULL) + if (strcmp(word, "{") == 0) { + /* we may have a complete ACL expression between two braces, + * find the last one. + */ + int arg_end = arg + 1; + const char **args_new; + + while (*args[arg_end] && strcmp(args[arg_end], "}") != 0) + arg_end++; + + if (!*args[arg_end]) goto out_free_suite; + + args_new = calloc(1, (arg_end - arg + 1) * sizeof(*args_new)); + if (!args_new) + goto out_free_suite; + + args_new[0] = ".noname"; + memcpy(args_new + 1, args + arg + 1, (arg_end - arg) * sizeof(*args_new)); + args_new[arg_end - arg] = ""; + cur_acl = parse_acl(args_new, known_acl); + free(args_new); + + if (!cur_acl) + goto out_free_suite; + arg = arg_end; + } + else { + /* search for in the known ACL names. If we do not find + * it, let's look for it in the default ACLs, and if found, add + * it to the list of ACLs of this proxy. This makes it possible + * to override them. + */ + cur_acl = find_acl_by_name(word, known_acl); + if (cur_acl == NULL) { + cur_acl = find_acl_default(word, known_acl); + if (cur_acl == NULL) + goto out_free_suite; + } } cur_term = (struct acl_term *)calloc(1, sizeof(*cur_term)); diff --git a/src/cfgparse.c b/src/cfgparse.c index a57f1bf9a..4263f9432 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -4733,42 +4733,36 @@ int check_config_validity() } if (curproxy->uri_auth && curproxy->uri_auth->userlist) { - const char *uri_auth_compat_acl[3] = { ".internal-stats-auth-ok", "http_auth(.internal-stats-userlist)", ""}; - const char *uri_auth_compat_req[][4] = { - { "allow", "if", ".internal-stats-auth-ok", ""}, - { "auth", "", "", ""}, - { 0 }, - }; + const char *uri_auth_compat_req[10]; struct req_acl_rule *req_acl; - int i; + int i = 0; - if (parse_acl(uri_auth_compat_acl, &curproxy->acl) == NULL) { - Alert("Error compiling internal auth-compat acl.\n"); - cfgerr++; - goto out_uri_auth_compat; - } + /* build the ACL condition from scratch. We're relying on anonymous ACLs for that */ + uri_auth_compat_req[i++] = "auth"; if (curproxy->uri_auth->auth_realm) { - uri_auth_compat_req[1][1] = "realm"; - uri_auth_compat_req[1][2] = curproxy->uri_auth->auth_realm; - } else - uri_auth_compat_req[1][1] = ""; - - for (i = 0; *uri_auth_compat_req[i]; i++) { - req_acl = parse_auth_cond(uri_auth_compat_req[i], "internal-stats-auth-compat", i, curproxy); - if (!req_acl) { - cfgerr++; - break; - } - - LIST_ADDQ(&curproxy->uri_auth->req_acl, &req_acl->list); + uri_auth_compat_req[i++] = "realm"; + uri_auth_compat_req[i++] = curproxy->uri_auth->auth_realm; } + uri_auth_compat_req[i++] = "unless"; + uri_auth_compat_req[i++] = "{"; + uri_auth_compat_req[i++] = "http_auth(.internal-stats-userlist)"; + uri_auth_compat_req[i++] = "}"; + uri_auth_compat_req[i++] = ""; + + req_acl = parse_auth_cond(uri_auth_compat_req, "internal-stats-auth-compat", 0, curproxy); + if (!req_acl) { + cfgerr++; + break; + } + + LIST_ADDQ(&curproxy->uri_auth->req_acl, &req_acl->list); + if (curproxy->uri_auth->auth_realm) { free(curproxy->uri_auth->auth_realm); curproxy->uri_auth->auth_realm = NULL; } - } out_uri_auth_compat: