commit fac50825151ac2abc6b71343e3ffa6e0dc06c53d Author: Christopher Faulet Date: Tue Jan 21 11:06:48 2020 +0100 MINOR: proxy/http-ana: Add support of extra attributes for the cookie directive It is now possible to insert any attribute when a cookie is inserted by HAProxy. Any value may be set, no check is performed except the syntax validity (CTRL chars and ';' are forbidden). For instance, it may be used to add the SameSite attribute: cookie SRV insert attr "SameSite=Strict" The attr option may be repeated to add several attributes. This patch should fix the issue #361. (cherry picked from commit 2f5339079b884ac8bdde166add1879ebfd9e433b) Signed-off-by: Christopher Faulet diff --git a/doc/configuration.txt b/doc/configuration.txt index 3f381e386..e0dc49880 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3255,7 +3255,7 @@ compression offload cookie [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain ]* [ maxidle ] [ maxlife ] - [ dynamic ] + [ dynamic ] [ attr ]* Enable cookie-based persistence in a backend. May be used in sections : defaults | frontend | listen | backend yes | no | yes | yes @@ -3414,6 +3414,11 @@ cookie [ rewrite | insert | prefix ] [ indirect ] [ nocache ] The cookie will be regenerated each time the IP address change, and is only generated for IPv4/IPv6. + attr This option tells haproxy to add an extra attribute when a + cookie is inserted. The attribute value can contain any + characters except control ones or ";". This option may be + repeated. + There can be only one persistence cookie per HTTP backend, and it can be declared in a defaults section. The value of the cookie will be the value indicated after the "cookie" keyword in a "server" statement. If no cookie diff --git a/include/types/proxy.h b/include/types/proxy.h index 6ea96b3ad..3661c9a0c 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -338,6 +338,7 @@ struct proxy { int cookie_len; /* strlen(cookie_name), computed only once */ char *cookie_domain; /* domain used to insert the cookie */ char *cookie_name; /* name of the cookie to look for */ + char *cookie_attrs; /* list of attributes to add to the cookie */ char *dyncookie_key; /* Secret key used to generate dynamic persistent cookies */ unsigned int cookie_maxidle; /* max idle time for this cookie */ unsigned int cookie_maxlife; /* max life time for this cookie */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 9975e4687..b1f5c0790 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -323,6 +323,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->rdp_cookie_name = strdup(defproxy.rdp_cookie_name); curproxy->rdp_cookie_len = defproxy.rdp_cookie_len; + if (defproxy.cookie_attrs) + curproxy->cookie_attrs = strdup(defproxy.cookie_attrs); if (defproxy.lbprm.arg_str) curproxy->lbprm.arg_str = strdup(defproxy.lbprm.arg_str); @@ -473,6 +475,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) free(defproxy.rdp_cookie_name); free(defproxy.dyncookie_key); free(defproxy.cookie_domain); + free(defproxy.cookie_attrs); free(defproxy.lbprm.arg_str); free(defproxy.capture_name); free(defproxy.monitor_uri); @@ -986,9 +989,34 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) err_code |= ERR_WARN; curproxy->ck_opts |= PR_CK_DYNAMIC; } + else if (!strcmp(args[cur_arg], "attr")) { + char *val; + if (!*args[cur_arg + 1]) { + ha_alert("parsing [%s:%d]: '%s' expects as argument.\n", + file, linenum, args[cur_arg]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + val = args[cur_arg + 1]; + while (*val) { + if (iscntrl(*val) || *val == ';') { + ha_alert("parsing [%s:%d]: character '%%x%02X' is not permitted in attribute value.\n", + file, linenum, *val); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + val++; + } + /* don't add ';' for the first attribute */ + if (!curproxy->cookie_attrs) + curproxy->cookie_attrs = strdup(args[cur_arg + 1]); + else + memprintf(&curproxy->cookie_attrs, "%s; %s", curproxy->cookie_attrs, args[cur_arg + 1]); + cur_arg++; + } else { - ha_alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache', 'postonly', 'domain', 'maxidle', 'dynamic' and 'maxlife' options.\n", + ha_alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache', 'postonly', 'domain', 'maxidle', 'dynamic', 'maxlife' and 'attr' options.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; diff --git a/src/haproxy.c b/src/haproxy.c index f225a13f8..98d6a9a39 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2352,6 +2352,7 @@ void deinit(void) free(p->check_req); free(p->cookie_name); free(p->cookie_domain); + free(p->cookie_attrs); free(p->lbprm.arg_str); free(p->capture_name); free(p->monitor_uri); diff --git a/src/http_ana.c b/src/http_ana.c index 047ed813a..cb5a60ca9 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2027,6 +2027,9 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s if (s->be->ck_opts & PR_CK_SECURE) chunk_appendf(&trash, "; Secure"); + if (s->be->cookie_attrs) + chunk_appendf(&trash, "; %s", s->be->cookie_attrs); + if (unlikely(!http_add_header(htx, ist("Set-Cookie"), ist2(trash.area, trash.data)))) goto return_bad_resp;