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.
This commit is contained in:
Christopher Faulet 2020-01-21 11:06:48 +01:00 committed by Willy Tarreau
parent e9ff8992a1
commit 2f5339079b
5 changed files with 40 additions and 2 deletions

View File

@ -3340,7 +3340,7 @@ compression offload
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
[ postonly ] [ preserve ] [ httponly ] [ secure ] [ postonly ] [ preserve ] [ httponly ] [ secure ]
[ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
[ dynamic ] [ dynamic ] [ attr <value> ]*
Enable cookie-based persistence in a backend. Enable cookie-based persistence in a backend.
May be used in sections : defaults | frontend | listen | backend May be used in sections : defaults | frontend | listen | backend
yes | no | yes | yes yes | no | yes | yes
@ -3499,6 +3499,11 @@ cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
The cookie will be regenerated each time the IP address change, The cookie will be regenerated each time the IP address change,
and is only generated for IPv4/IPv6. 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 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 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 indicated after the "cookie" keyword in a "server" statement. If no cookie

View File

@ -338,6 +338,7 @@ struct proxy {
int cookie_len; /* strlen(cookie_name), computed only once */ int cookie_len; /* strlen(cookie_name), computed only once */
char *cookie_domain; /* domain used to insert the cookie */ char *cookie_domain; /* domain used to insert the cookie */
char *cookie_name; /* name of the cookie to look for */ 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 */ char *dyncookie_key; /* Secret key used to generate dynamic persistent cookies */
unsigned int cookie_maxidle; /* max idle time for this cookie */ unsigned int cookie_maxidle; /* max idle time for this cookie */
unsigned int cookie_maxlife; /* max life time for this cookie */ unsigned int cookie_maxlife; /* max life time for this cookie */

View File

@ -326,6 +326,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_name = strdup(defproxy.rdp_cookie_name);
curproxy->rdp_cookie_len = defproxy.rdp_cookie_len; curproxy->rdp_cookie_len = defproxy.rdp_cookie_len;
if (defproxy.cookie_attrs)
curproxy->cookie_attrs = strdup(defproxy.cookie_attrs);
if (defproxy.lbprm.arg_str) if (defproxy.lbprm.arg_str)
curproxy->lbprm.arg_str = strdup(defproxy.lbprm.arg_str); curproxy->lbprm.arg_str = strdup(defproxy.lbprm.arg_str);
@ -476,6 +478,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
free(defproxy.rdp_cookie_name); free(defproxy.rdp_cookie_name);
free(defproxy.dyncookie_key); free(defproxy.dyncookie_key);
free(defproxy.cookie_domain); free(defproxy.cookie_domain);
free(defproxy.cookie_attrs);
free(defproxy.lbprm.arg_str); free(defproxy.lbprm.arg_str);
free(defproxy.capture_name); free(defproxy.capture_name);
free(defproxy.monitor_uri); free(defproxy.monitor_uri);
@ -988,9 +991,34 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
err_code |= ERR_WARN; err_code |= ERR_WARN;
curproxy->ck_opts |= PR_CK_DYNAMIC; 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 <value> 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 { 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]); file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;

View File

@ -2339,6 +2339,7 @@ void deinit(void)
free(p->check_req); free(p->check_req);
free(p->cookie_name); free(p->cookie_name);
free(p->cookie_domain); free(p->cookie_domain);
free(p->cookie_attrs);
free(p->lbprm.arg_str); free(p->lbprm.arg_str);
free(p->capture_name); free(p->capture_name);
free(p->monitor_uri); free(p->monitor_uri);

View File

@ -2068,6 +2068,9 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s
if (s->be->ck_opts & PR_CK_SECURE) if (s->be->ck_opts & PR_CK_SECURE)
chunk_appendf(&trash, "; 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)))) if (unlikely(!http_add_header(htx, ist("Set-Cookie"), ist2(trash.area, trash.data))))
goto return_int_err; goto return_int_err;