From: Christopher Faulet Date: Mon, 4 Sep 2017 13:41:09 +0000 (+0200) Subject: MEDIUM: spoe: Add support of ACLS to enable or disable sending of SPOE messages X-Git-Tag: v1.8-rc1~187 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57583e474e3ee73d960403f2515437b6349ab7f1;p=thirdparty%2Fhaproxy.git MEDIUM: spoe: Add support of ACLS to enable or disable sending of SPOE messages Now, it is possible to conditionnaly send a SPOE message by adding an ACL-based condition on the "event" line, in a "spoe-message" section. Here is the example coming for the SPOE documentation: spoe-message get-ip-reputation args ip=src event on-client-session if ! { src -f /etc/haproxy/whitelist.lst } To avoid mixin with proxy's ACLs, each SPOE message has its private ACL list. It possible to declare named ACLs in "spoe-message" section, using the same syntax than for proxies. So we can rewrite the previous example to use a named ACL: spoe-message get-ip-reputation args ip=src acl ip-whitelisted src -f /etc/haproxy/whitelist.lst event on-client-session if ! ip-whitelisted ACL-based conditions are executed in the context of the stream that handle the client and the server connections. --- diff --git a/doc/SPOE.txt b/doc/SPOE.txt index 0f6ed14a51..611b228786 100644 --- a/doc/SPOE.txt +++ b/doc/SPOE.txt @@ -1,7 +1,7 @@ ----------------------------------------------- Stream Processing Offload Engine (SPOE) - Version 1.1 - ( Last update: 2017-02-27 ) + Version 1.2 + ( Last update: 2017-09-22 ) ----------------------------------------------- Author : Christopher Faulet Contact : cfaulet at haproxy dot com @@ -351,12 +351,20 @@ spoe-message Here you define a message that can be referenced in a "spoe-agent" section. Following keywords are supported : + - acl - args - event See also: "spoe-agent" section. +acl [flags] [operator] ... + + Declare or complete an access list. + + See section 7 about ACL usage in the HAProxy Configuration Manual. + + args [name=] ... Define arguments passed into the SPOE message. @@ -371,11 +379,17 @@ args [name=] ... args frontend=fe_id src dst -event - Set the event that triggers sending of the message. +event [ { if | unless } ] + Set the event that triggers sending of the message. It may optionally be + followed by an ACL-based condition, in which case it will only be evaluated + if the condition is true. + + ACL-based conditions are executed in the context of the stream that handle + the client and the server connections. - Argument : - is the event name. + Arguments : + is the event name. + is a standard ACL-based condition. Supported events are: - on-client-session @@ -387,7 +401,8 @@ event - on-backend-http-request - on-http-response - See section "Events & Messages". + See section "Events & Messages" for more details about supported events. + See section 7 about ACL usage in the HAProxy Configuration Manual. 2.4. Example ------------- @@ -441,7 +456,7 @@ and 0 a blacklisted IP with no doubt). spoe-message get-ip-reputation args ip=src - event on-client-session + event on-client-session if ! { src -f /etc/haproxy/whitelist.lst } 3. SPOP specification diff --git a/include/types/spoe.h b/include/types/spoe.h index edbb459048..193c2f64b8 100644 --- a/include/types/spoe.h +++ b/include/types/spoe.h @@ -184,6 +184,8 @@ struct spoe_message { struct list args; /* Arguments added when the SPOE messages is sent */ struct list list; /* Used to chain SPOE messages */ + struct list acls; /* ACL declared on this message */ + struct acl_cond *cond; /* acl condition to meet */ enum spoe_event event; /* SPOE_EV_* */ }; diff --git a/src/flt_spoe.c b/src/flt_spoe.c index abaded156c..dbc9d30755 100644 --- a/src/flt_spoe.c +++ b/src/flt_spoe.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -107,18 +108,28 @@ spoe_release_msg_placeholder(struct spoe_msg_placeholder *mp) static void spoe_release_message(struct spoe_message *msg) { - struct spoe_arg *arg, *back; + struct spoe_arg *arg, *argback; + struct acl *acl, *aclback; if (!msg) return; free(msg->id); free(msg->conf.file); - list_for_each_entry_safe(arg, back, &msg->args, list) { + list_for_each_entry_safe(arg, argback, &msg->args, list) { release_sample_expr(arg->expr); free(arg->name); LIST_DEL(&arg->list); free(arg); } + list_for_each_entry_safe(acl, aclback, &msg->acls, list) { + LIST_DEL(&acl->list); + prune_acl(acl); + free(acl); + } + if (msg->cond) { + prune_acl_cond(msg->cond); + free(msg->cond); + } free(msg); } @@ -2070,7 +2081,8 @@ spoe_queue_context(struct spoe_context *ctx) **************************************************************************/ /* Encode SPOE messages for a specific event. Info in frag_ctx>, if any, * are used to handle fragmented content. On success it returns 1. If an error - * occurred, -1 is returned. */ + * occurred, -1 is returned. If nothing has been encoded, it returns 0 (this is + * only possible for unfragmented payload). */ static int spoe_encode_messages(struct stream *s, struct spoe_context *ctx, struct list *messages, int dir) @@ -2099,6 +2111,19 @@ spoe_encode_messages(struct stream *s, struct spoe_context *ctx, ctx->frag_ctx.curoff = UINT_MAX; encode_message: + if (msg->cond) { + int ret = 1; + + ret = acl_exec_cond(msg->cond, s->be, s->sess, s, dir|SMP_OPT_FINAL); + ret = acl_pass(ret); + if (msg->cond->pol == ACL_COND_UNLESS) + ret = !ret; + + /* the rule does not match */ + if (!ret) + continue; + } + /* Resume encoding of a SPOE argument */ if (ctx->frag_ctx.curarg != NULL) { arg = ctx->frag_ctx.curarg; @@ -2149,6 +2174,10 @@ spoe_encode_messages(struct stream *s, struct spoe_context *ctx, } } + /* nothing has been encoded for an unfragmented payload */ + if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) && p == ctx->buffer->p) + goto skip; + SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p" " - encode %s messages - spoe_appctx=%p" "- max_size=%u - encoded=%ld\n", @@ -2163,6 +2192,7 @@ spoe_encode_messages(struct stream *s, struct spoe_context *ctx, ctx->frag_ctx.curarg = NULL; ctx->frag_ctx.curoff = 0; ctx->frag_ctx.flags = SPOE_FRM_FL_FIN; + return 1; too_big: @@ -2184,6 +2214,13 @@ spoe_encode_messages(struct stream *s, struct spoe_context *ctx, ctx->flags |= SPOE_CTX_FL_FRAGMENTED; ctx->frag_ctx.flags &= ~SPOE_FRM_FL_FIN; return 1; + + skip: + SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p" + " - skip the frame because nothing has been encoded\n", + (int)now.tv_sec, (int)now.tv_usec, + agent->id, __FUNCTION__, s); + return 0; } @@ -2477,6 +2514,8 @@ spoe_process_event(struct stream *s, struct spoe_context *ctx, ret = spoe_encode_messages(s, ctx, &(ctx->messages[ev]), dir); if (ret < 0) goto error; + if (!ret) + goto skip; ctx->state = SPOE_CTX_ST_SENDING_MSGS; } @@ -3320,6 +3359,7 @@ cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm) curmsg->conf.line = linenum; curmsg->nargs = 0; LIST_INIT(&curmsg->args); + LIST_INIT(&curmsg->acls); LIST_ADDQ(&curmsgs, &curmsg->list); } else if (!strcmp(args[0], "args")) { @@ -3365,14 +3405,29 @@ cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm) curproxy->conf.args.file = NULL; curproxy->conf.args.line = 0; } + else if (!strcmp(args[0], "acl")) { + err = invalid_char(args[1]); + if (err) { + Alert("parsing [%s:%d] : character '%c' is not permitted in acl name '%s'.\n", + file, linenum, *err, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + if (parse_acl((const char **)args + 1, &curmsg->acls, &errmsg, &curproxy->conf.args, file, linenum) == NULL) { + Alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n", + file, linenum, args[1], errmsg); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } else if (!strcmp(args[0], "event")) { if (!*args[1]) { Alert("parsing [%s:%d] : missing event name.\n", file, linenum); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - if (alertif_too_many_args(1, file, linenum, args, &err_code)) - goto out; + /* if (alertif_too_many_args(1, file, linenum, args, &err_code)) */ + /* goto out; */ if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS])) curmsg->event = SPOE_EV_ON_CLIENT_SESS; @@ -3398,6 +3453,29 @@ cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm) err_code |= ERR_ALERT | ERR_FATAL; goto out; } + + if (strcmp(args[2], "if") == 0 || strcmp(args[2], "unless") == 0) { + struct acl_cond *cond; + + cond = build_acl_cond(file, linenum, &curmsg->acls, + curproxy, (const char **)args+2, + &errmsg); + if (cond == NULL) { + Alert("parsing [%s:%d] : error detected while " + "parsing an 'event %s' condition : %s.\n", + file, linenum, args[1], errmsg); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + curmsg->cond = cond; + } + else if (*args[2]) { + Alert("parsing [%s:%d]: 'event %s' expects either 'if' " + "or 'unless' followed by a condition but found '%s'.\n", + file, linenum, args[1], args[2]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } } else if (!*args[0]) { Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",