]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: http-ana: Use proxy's error replies to emit 401/407 responses
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 27 May 2020 07:57:28 +0000 (09:57 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 28 May 2020 13:07:20 +0000 (15:07 +0200)
There is no reason to not use proxy's error replies to emit 401/407
responses. The function http_reply_40x_unauthorized(), responsible to emit those
responses, is not really complex. It only adds a
WWW-Authenticate/Proxy-Authenticate header to a generic message.

So now, error replies can be defined for 401 and 407 status codes, using
errorfile or http-error directives. When an http-request auth rule is evaluated,
the corresponding error reply is used. For 401 responses, all occurrences of the
WWW-Authenticate header are removed and replaced by a new one with a basic
authentication challenge for the configured realm. For 407 responses, the same
is done on the Proxy-Authenticate header. If the error reply must not be
altered, "http-request return" rule must be used instead.

doc/configuration.txt
include/common/http.h
src/http.c
src/http_ana.c

index 674acd2fdc45f8cfa79db02bbee05692bd9b709a..8a67f4d12139386ff004aea952d39178a2ef50b7 100644 (file)
@@ -2522,8 +2522,8 @@ errorfile <code> <file>
 
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429,
-              500, 502, 503, and 504.
+              generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+              425, 429, 500, 502, 503, and 504.
 
     <file>    designates a file containing the full HTTP response. It is
               recommended to follow the common practice of appending ".http" to
@@ -3859,8 +3859,8 @@ errorfile <code> <file>
                                  yes   |    yes   |   yes  |   yes
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
-              502, 503, and 504.
+              generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+              425, 429, 500, 502, 503, and 504.
 
     <file>    designates a file containing the full HTTP response. It is
               recommended to follow the common practice of appending ".http" to
@@ -3908,8 +3908,8 @@ errorfiles <name> [<code> ...]
     <name>  is the name of an existing http-errors section.
 
     <code>  is a HTTP status code. Several status code may be listed.
-            Currently, HAProxy is capable of generating codes 200, 400, 403,
-            404, 405, 408, 410, 425, 429, 500, 502, 503, and 504.
+            Currently, HAProxy is capable of generating codes 200, 400, 401,
+            403, 404, 405, 407, 408, 410, 425, 429, 500, 502, 503, and 504.
 
   Errors defined in the http-errors section with the name <name> are imported
   in the current proxy. If no status code is specified, all error files of the
@@ -3934,8 +3934,8 @@ errorloc302 <code> <url>
                                  yes   |    yes   |   yes  |   yes
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
-              502, 503, and 504.
+              generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+              425, 429, 500, 502, 503, and 504.
 
     <url>     it is the exact contents of the "Location" header. It may contain
               either a relative URI to an error page hosted on the same site,
@@ -3966,8 +3966,8 @@ errorloc303 <code> <url>
                                  yes   |    yes   |   yes  |   yes
   Arguments :
     <code>    is the HTTP status code. Currently, HAProxy is capable of
-              generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
-              502, 503, and 504.
+              generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+              425, 429, 500, 502, 503, and 504.
 
     <url>     it is the exact contents of the "Location" header. It may contain
               either a relative URI to an error page hosted on the same site,
@@ -4942,8 +4942,8 @@ http-error status <code> [content-type <type>]
   Arguments :
     staus <code>         is the HTTP status code. It must be specified.
                          Currently, HAProxy is capable of generating codes
-                         200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
-                         502, 503, and 504.
+                         200, 400, 401, 403, 404, 405, 407, 408, 410, 425, 429,
+                         500, 502, 503, and 504.
 
     content-type <type>  is the response content type, for instance
                          "text/plain". This parameter is ignored and should be
@@ -5095,6 +5095,14 @@ http-request auth [realm <realm>] [ { if | unless } <condition> ]
   "realm" parameter is supported, it sets the authentication realm that is
   returned with the response (typically the application's name).
 
+  The corresponding proxy's error message is used. It may be customized using
+  an "errorfile" or an "http-error" directive. For 401 responses, all
+  occurrences of the WWW-Authenticate header are removed and replaced by a new
+  one with a basic authentication challenge for realm "<realm>". For 407
+  responses, the same is done on the Proxy-Authenticate header. If the error
+  message must not be altered, consider to use "http-request return" rule
+  instead.
+
   Example:
         acl auth_ok http_auth_group(L1) G1
         http-request auth unless auth_ok
index 6f083d43553aec0271fd801413dd43ce10be839b..d31f5e915b850d3da8b6153844d48ab5a2480dc2 100644 (file)
@@ -82,9 +82,11 @@ enum ht_auth_m {
 enum {
        HTTP_ERR_200 = 0,
        HTTP_ERR_400,
+       HTTP_ERR_401,
        HTTP_ERR_403,
        HTTP_ERR_404,
        HTTP_ERR_405,
+       HTTP_ERR_407,
        HTTP_ERR_408,
        HTTP_ERR_410,
        HTTP_ERR_421,
index ec6b4a2826e0cadcf0cf251c6a5cfa8c4da4be8f..a1e519793c67115ac0fb96b4d29171461be3172e 100644 (file)
@@ -215,9 +215,11 @@ const char *HTTP_407_fmt =
 const int http_err_codes[HTTP_ERR_SIZE] = {
        [HTTP_ERR_200] = 200,  /* used by "monitor-uri" */
        [HTTP_ERR_400] = 400,
+       [HTTP_ERR_401] = 401,
        [HTTP_ERR_403] = 403,
        [HTTP_ERR_404] = 404,
        [HTTP_ERR_405] = 405,
+       [HTTP_ERR_407] = 407,
        [HTTP_ERR_408] = 408,
        [HTTP_ERR_410] = 410,
        [HTTP_ERR_421] = 421,
@@ -248,6 +250,15 @@ const char *http_err_msgs[HTTP_ERR_SIZE] = {
        "\r\n"
        "<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n",
 
+       [HTTP_ERR_401] =
+       "HTTP/1.1 401 Unauthorized\r\n"
+       "Content-length: 112\r\n"
+       "Cache-Control: no-cache\r\n"
+       "Connection: close\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n"
+       "<html><body><h1>401 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n",
+
        [HTTP_ERR_403] =
        "HTTP/1.1 403 Forbidden\r\n"
        "Content-length: 93\r\n"
@@ -275,6 +286,15 @@ const char *http_err_msgs[HTTP_ERR_SIZE] = {
        "\r\n"
        "<html><body><h1>405 Method Not Allowed</h1>\nA request was made of a resource using a request method not supported by that resource\n</body></html>\n",
 
+       [HTTP_ERR_407] =
+       "HTTP/1.1 407 Unauthorized\r\n"
+       "Content-length: 112\r\n"
+       "Cache-Control: no-cache\r\n"
+       "Connection: close\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n"
+       "<html><body><h1>407 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n",
+
        [HTTP_ERR_408] =
        "HTTP/1.1 408 Request Time-out\r\n"
        "Content-length: 110\r\n"
@@ -396,9 +416,11 @@ int http_get_status_idx(unsigned int status)
        switch (status) {
        case 200: return HTTP_ERR_200;
        case 400: return HTTP_ERR_400;
+       case 401: return HTTP_ERR_401;
        case 403: return HTTP_ERR_403;
        case 404: return HTTP_ERR_404;
        case 405: return HTTP_ERR_405;
+       case 407: return HTTP_ERR_407;
        case 408: return HTTP_ERR_408;
        case 410: return HTTP_ERR_410;
        case 421: return HTTP_ERR_421;
index f7da2682101e1582461134dac1574ed1cf355522..e239f7c31343f7b66161f195e66a16e0df325c21 100644 (file)
@@ -4910,61 +4910,54 @@ static int http_reply_40x_unauthorized(struct stream *s, const char *auth_realm)
 {
        struct channel *res = &s->res;
        struct htx *htx = htx_from_buf(&res->buf);
-       struct htx_sl *sl;
-       struct ist code, body;
-       int status;
-       unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11);
+       struct http_reply *reply;
+       struct http_hdr_ctx ctx;
+       struct ist hdr;
 
        if (!(s->txn->flags & TX_USE_PX_CONN)) {
-               status = 401;
-               code = ist("401");
-               body = ist("<html><body><h1>401 Unauthorized</h1>\n"
-                          "You need a valid user and password to access this content.\n"
-                          "</body></html>\n");
+               s->txn->status = 401;
+               hdr = ist("WWW-Authenticate");
        }
        else {
-               status = 407;
-               code = ist("407");
-               body = ist("<html><body><h1>407 Unauthorized</h1>\n"
-                          "You need a valid user and password to access this content.\n"
-                          "</body></html>\n");
+               s->txn->status = 407;
+               hdr = ist("Proxy-Authenticate");
        }
-
-       sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
-                           ist("HTTP/1.1"), code, ist("Unauthorized"));
-       if (!sl)
-               goto fail;
-       sl->info.res.status = status;
-       s->txn->status = status;
+       reply = http_error_message(s);
+       channel_htx_truncate(res, htx);
 
        if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
                goto fail;
 
-        if (!htx_add_header(htx, ist("Content-length"), ist("112")) ||
-           !htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
-           !htx_add_header(htx, ist("Connection"), ist("close")) ||
-           !htx_add_header(htx, ist("Content-Type"), ist("text/html")))
-               goto fail;
-       if (status == 401 && !htx_add_header(htx, ist("WWW-Authenticate"), ist2(trash.area, trash.data)))
-               goto fail;
-       if (status == 407 && !htx_add_header(htx, ist("Proxy-Authenticate"), ist2(trash.area, trash.data)))
-               goto fail;
-       if (!htx_add_endof(htx, HTX_BLK_EOH))
+       /* Write the generic 40x message */
+       if (http_reply_to_htx(s, htx, reply) == -1)
                goto fail;
 
-       while (body.len) {
-               size_t sent = htx_add_data(htx, body);
-               if (!sent)
-                       goto fail;
-               body.ptr += sent;
-               body.len -= sent;
-       }
+       /* Remove all existing occurrences of the XXX-Authenticate header */
+       ctx.blk = NULL;
+       while (http_find_header(htx, hdr, &ctx, 1))
+               http_remove_header(htx, &ctx);
 
-       if (!htx_add_endof(htx, HTX_BLK_EOM))
+       /* Now a the right XXX-Authenticate header */
+       if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
                goto fail;
 
+       /* Finally forward the reply */
+       htx_to_buf(htx, &res->buf);
        if (!http_forward_proxy_resp(s, 1))
                goto fail;
+
+       /* Note: Only eval on the request */
+       s->logs.tv_request = now;
+       s->req.analysers &= AN_REQ_FLT_END;
+
+       if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
+               _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
+
+       if (!(s->flags & SF_ERR_MASK))
+               s->flags |= SF_ERR_LOCAL;
+       if (!(s->flags & SF_FINST_MASK))
+               s->flags |= SF_FINST_R;
+
        return 0;
 
   fail: