From: Christopher Faulet Date: Wed, 27 May 2020 07:57:28 +0000 (+0200) Subject: MINOR: http-ana: Use proxy's error replies to emit 401/407 responses X-Git-Tag: v2.2-dev9~182 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=612f2eafe907a4a6c07486274c90dc867e4e9e95;p=thirdparty%2Fhaproxy.git MINOR: http-ana: Use proxy's error replies to emit 401/407 responses 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. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 674acd2fdc..8a67f4d121 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2522,8 +2522,8 @@ errorfile Arguments : 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. 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 yes | yes | yes | yes Arguments : 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. 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 [ ...] is the name of an existing http-errors section. 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 are imported in the current proxy. If no status code is specified, all error files of the @@ -3934,8 +3934,8 @@ errorloc302 yes | yes | yes | yes Arguments : 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. 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 yes | yes | yes | yes Arguments : 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. 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 [content-type ] Arguments : staus 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 is the response content type, for instance "text/plain". This parameter is ignored and should be @@ -5095,6 +5095,14 @@ http-request auth [realm ] [ { if | unless } ] "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 "". 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 diff --git a/include/common/http.h b/include/common/http.h index 6f083d4355..d31f5e915b 100644 --- a/include/common/http.h +++ b/include/common/http.h @@ -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, diff --git a/src/http.c b/src/http.c index ec6b4a2826..a1e519793c 100644 --- a/src/http.c +++ b/src/http.c @@ -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" "

400 Bad request

\nYour browser sent an invalid request.\n\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" + "

401 Unauthorized

\nYou need a valid user and password to access this content.\n\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" "

405 Method Not Allowed

\nA request was made of a resource using a request method not supported by that resource\n\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" + "

407 Unauthorized

\nYou need a valid user and password to access this content.\n\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; diff --git a/src/http_ana.c b/src/http_ana.c index f7da268210..e239f7c313 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -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("

401 Unauthorized

\n" - "You need a valid user and password to access this content.\n" - "\n"); + s->txn->status = 401; + hdr = ist("WWW-Authenticate"); } else { - status = 407; - code = ist("407"); - body = ist("

407 Unauthorized

\n" - "You need a valid user and password to access this content.\n" - "\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: