]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] redirect: add support for "set-cookie" and "clear-cookie"
authorWilly Tarreau <w@1wt.eu>
Wed, 19 Nov 2008 20:07:09 +0000 (21:07 +0100)
committerWilly Tarreau <w@1wt.eu>
Sun, 7 Dec 2008 22:46:38 +0000 (23:46 +0100)
It is now possible to set or clear a cookie during a redirection. This
is useful for logout pages, or for protecting against some DoSes. Check
the documentation for the options supported by the "redirect" keyword.

(cherry-picked from commit 4af993822e880d8c932f4ad6920db4c9242b0981)

doc/configuration.txt
include/types/proxy.h
src/cfgparse.c
src/proto_http.c
tests/test-redirect.cfg

index 2467007f9941c0147cdb500e69d3fbbb1ca48d77..ca6f293ef03793de106ba9f03b7f4c3ca55f30e3 100644 (file)
@@ -2336,34 +2336,68 @@ no option transparent
             "transparent" option of the "bind" keyword.
 
 
-redirect location <to> [code <code>] {if | unless} <condition>
-redirect prefix <to> [drop-query] [code <code>] {if | unless} <condition>
+redirect location <to> [code <code>] <option> {if | unless} <condition>
+redirect prefix   <to> [code <code>] <option> {if | unless} <condition>
   Return an HTTP redirection if/unless a condition is matched
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    yes   |   yes  |   yes
 
   If/unless the condition is matched, the HTTP request will lead to a redirect
-  response. There are currently two types of redirections : "location" and
-  "prefix". With "location", the exact value in <to> is placed into the HTTP
-  "Location" header. With "prefix", the "Location" header is built from the
-  concatenation of <to> and the URI. If the optional "drop-query" keyword is
-  used in a prefix-based redirection, then the location will be set without any
-  possible query-string, which is useful for directing users to a non-secure
-  page for instance. The "prefix" mode is particularly suited for global site
-  redirections.
-
-  The code is optional. It indicates in <code> which type of HTTP redirection
-  is desired. Only codes 301, 302 and 303 are supported. 302 is used if no code
-  is specified.
+  response.
+
+  Arguments :
+    <to>      With "redirect location", the exact value in <to> is placed into
+              the HTTP "Location" header. In case of "redirect prefix", the
+              "Location" header is built from the concatenation of <to> and the
+              complete URI, including the query string, unless the "drop-query"
+              option is specified (see below).
+
+    <code>    The code is optional. It indicates which type of HTTP redirection
+              is desired. Only codes 301, 302 and 303 are supported, and 302 is
+              used if no code is specified. 301 means "Moved permanently", and
+              a browser may cache the Location. 302 means "Moved permanently"
+              and means that the browser should not cache the redirection. 303
+              is equivalent to 302 except that the browser will fetch the
+              location with a GET method.
+
+    <option>  There are several options which can be specified to adjust the
+              expected behaviour of a redirection :
+
+      - "drop-query"
+        When this keyword is used in a prefix-based redirection, then the
+        location will be set without any possible query-string, which is useful
+        for directing users to a non-secure page for instance. It has no effect
+        with a location-type redirect.
+
+      - "set-cookie NAME[=value]"
+        A "Set-Cookie" header will be added with NAME (and optionally "=value")
+        to the response. This is sometimes used to indicate that a user has
+        been seen, for instance to protect against some types of DoS. No other
+        cookie option is added, so the cookie will be a session cookie. Note
+        that for a browser, a sole cookie name without an equal sign is
+        different from a cookie with an equal sign.
+
+      - "clear-cookie NAME[=]"
+        A "Set-Cookie" header will be added with NAME (and optionally "="), but
+        with the "Max-Age" attribute set to zero. This will tell the browser to
+        delete this cookie. It is useful for instance on logout pages. It is
+        important to note that clearing the cookie "NAME" will not remove a
+        cookie set with "NAME=value". You have to clear the cookie "NAME=" for
+        that, because the browser makes the difference.
 
   Example: move the login URL only to HTTPS.
         acl clear      dst_port  80
         acl secure     dst_port  8080
         acl login_page url_beg   /login
+        acl logout     url_beg   /logout
         acl uid_given  url_reg   /login?userid=[^&]+
+        acl cookie_set hdr_sub(cookie) SEEN=1
+
+        redirect prefix   https://mysite.com set-cookie SEEN=1 if !cookie_set
         redirect prefix   https://mysite.com           if login_page !secure
         redirect prefix   http://mysite.com drop-query if login_page !uid_given
         redirect location http://mysite.com/           if !login_page secure
+        redirect location / clear-cookie USERID=       if logout
 
   See section 2.3 about ACL usage.
 
index a7d2c404f2cc1d6502f63e261932079b97089a90..90d5c4e5262c04d87ad24fedb38823577894f5b5 100644 (file)
@@ -267,6 +267,8 @@ struct redirect_rule {
        char *rdr_str;
        int code;
        unsigned int flags;
+       int cookie_len;
+       char *cookie_str;
 };
 
 extern struct proxy *proxy;
index ede1b22f65ff9650fe0453cf066cdffb7aaf305f..8668dfb19a57cbf98535298aa71fc36a2cc37c0d 100644 (file)
@@ -1118,6 +1118,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                int type = REDIRECT_TYPE_NONE;
                int code = 302;
                char *destination = NULL;
+               char *cookie = NULL;
+               int cookie_set = 0;
                unsigned int flags = REDIRECT_FLAG_NONE;
 
                cur_arg = 1;
@@ -1144,6 +1146,28 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                                cur_arg++;
                                destination = args[cur_arg];
                        }
+                       else if (!strcmp(args[cur_arg], "set-cookie")) {
+                               if (!*args[cur_arg + 1]) {
+                                       Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
+                                             file, linenum, args[0], args[cur_arg]);
+                                       return -1;
+                               }
+
+                               cur_arg++;
+                               cookie = args[cur_arg];
+                               cookie_set = 1;
+                       }
+                       else if (!strcmp(args[cur_arg], "clear-cookie")) {
+                               if (!*args[cur_arg + 1]) {
+                                       Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
+                                             file, linenum, args[0], args[cur_arg]);
+                                       return -1;
+                               }
+
+                               cur_arg++;
+                               cookie = args[cur_arg];
+                               cookie_set = 0;
+                       }
                        else if (!strcmp(args[cur_arg],"code")) {
                                if (!*args[cur_arg + 1]) {
                                        Alert("parsing [%s:%d] : '%s': missing HTTP code.\n",
@@ -1202,6 +1226,20 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                rule->cond = cond;
                rule->rdr_str = strdup(destination);
                rule->rdr_len = strlen(destination);
+               if (cookie) {
+                       /* depending on cookie_set, either we want to set the cookie, or to clear it.
+                        * a clear consists in appending "; Max-Age=0" at the end.
+                        */
+                       rule->cookie_len = strlen(cookie);
+                       if (cookie_set)
+                               rule->cookie_str = strdup(cookie);
+                       else {
+                               rule->cookie_str = malloc(rule->cookie_len + 12);
+                               memcpy(rule->cookie_str, cookie, rule->cookie_len);
+                               memcpy(rule->cookie_str + rule->cookie_len, "; Max-Age=0", 12);
+                               rule->cookie_len += 11;
+                       }
+               }
                rule->type = type;
                rule->code = code;
                rule->flags = flags;
index 6a9f7054aae245cb3977c945833aea208b8530e3..2b72504c483a54d72b0ec5a53294d9500ac9da3e 100644 (file)
@@ -1932,6 +1932,15 @@ int http_process_request(struct session *s, struct buffer *req)
                                        break;
                                }
 
+                               if (rule->cookie_len) {
+                                       memcpy(rdr.str + rdr.len, "\r\nSet-Cookie: ", 14);
+                                       rdr.len += 14;
+                                       memcpy(rdr.str + rdr.len, rule->cookie_str, rule->cookie_len);
+                                       rdr.len += rule->cookie_len;
+                                       memcpy(rdr.str + rdr.len, "\r\n", 2);
+                                       rdr.len += 2;
+                               }
+
                                /* add end of headers */
                                memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
                                rdr.len += 4;
index a3bf2ab0d9acd1308a2c0adb0355e74e285676f4..780132b0aa3f4282b11a39ef8b884faa4daf77ea 100644 (file)
@@ -18,10 +18,17 @@ listen  sample1
        acl        url_test1 url_reg test1
        acl        url_test2 url_reg test2
        acl        url_test3 url_reg test3
+       acl        url_test4 url_reg test4
+
+       acl        seen hdr_sub(cookie) SEEN=1
+
        redirect   location /abs/test code 301 if url_test1
        redirect   prefix   /pfx/test code 302 if url_test2
        redirect   prefix   /pfx/test code 303 drop-query if url_test3
 
+       redirect   location /test4 code 302 set-cookie   SEEN=1 if url_test4 !seen
+       redirect   location /      code 302 clear-cookie SEEN=  if url_test4 seen
+
        ### unconditional redirection
        #redirect   location https://example.com/ if TRUE