Hi Ciprian,
On 09/02/2019 11:11, Ciprian Dorin Craciun wrote:
> First of all I understand that the `errorfile` (and related
> `errorloc`) are for HAProxy's own generated errors.
>
> However given how powerful the ACL system is, and the availability of
> `http-request deny deny_status <code>`, one can leverage all this and
> implement a powerful WAF.
>
> For example last week I tried to "hide" some URL's and pretend they
> are 404's directly from HAProxy (as it seems that the backend server I
> was using doesn't support this feature...) However when I tried to
> use `deny_status 404` it actually replied with a 403 (and the custom
> page for that error). Now this is not a big issue, however it might
> give an "attacker" a hint that "something" is at that URL...
>
> ----
>
> Therefore I took a look at the `errorfile` related code and how it is
> currently implemented:
>
>
> https://github.com/haproxy/haproxy/blob/61ea7dc005bc490ed3e5298ade3d932926fdb9f7/include/common/http.h#L82-L96
>
> https://github.com/haproxy/haproxy/blob/61ea7dc005bc490ed3e5298ade3d932926fdb9f7/src/http.c#L218-L231
>
> https://github.com/haproxy/haproxy/blob/61ea7dc005bc490ed3e5298ade3d932926fdb9f7/src/http.c#L233
>
> https://github.com/haproxy/haproxy/blob/06f5b6435ba99b7a6a034d27b56192e16249f6f0/src/http_rules.c#L106-L111
>
> https://github.com/haproxy/haproxy/blob/21c741a665f4c7b354e961267824c81ec44f503f/include/types/proxy.h#L408
>
> At a first glance it implements a sort of "associative-array"
> structure, of fixed `HTTP_ERR_SIZE`, with the actual codes being
> statically mapped to indices via `http_err_codes`.
>
> There is a global default array `http_err_msgs` (plus the
> `http_err_chunks` counterpart) and a per-proxy embedded array
> `proxy->errmsg`, both pre-allocated of size `HTTP_ERR_SIZE`.
>
> Therefore adding support for a new status code is quite trivial, just
> create the relevant entries in `http_err_codes` and `http_err_msgs`
> and a new `HTTP_ERR_*` entry. The downside is minimal, just a slight
> increase of `sizeof (struct buffer)` bytes (32) globally and one for
> each proxy, and almost no run-time impact in terms of CPU.
>
> Thus I would suggest adding at least the following:
> * 404 -- not-found -- with an obvious use-case;
> * 402 -- payment-required -- which might be used as a "soft" 403
> alternative, which suggests that although you are "authenticated" and
> issued a valid request, you are not-allowed because of some
> "accounting" reason;
> * 406 -- not-acceptable -- although used for content negotiation, it
> could be "abused" as a page indicating to the user that his "browser"
> (or agent) is not "compatible" with the generated content;
> * 409/410 -- conflict/gone -- alternatives for 403/404 if the user has
> need for them;
> * 451 -- unavailable-for-legal-reasons -- perhaps useful in these GDPR-days?
> * 501 -- not-implemented -- useful perhaps to "hide" some
> not-yet-published API endpoints, or other backend-related
> "blacklisting";
>
> ----
>
> However my proposal would be to allow "user-defined" error codes, the
> main use-case being WAF/CDN custom status codes
> (https://support.cloudflare.com/hc/en-us/sections/200820298-Error-Pages).
>
> Such a change shouldn't be that involved, although for efficiency the
> `proxy->errmsg` should be transformed from an embedded array to an
> array pointer of variable size.
>
> Would such a feature be accepted by the HAProxy developers?
I think the reason of making HAProxy capable of returning a fixed number
of HTTP status codes is to avoid confusion with status codes returned
from Web servers.
For example, it is not the role of a reverse proxy to fetch a Web
resource so returning "404 Not found" won't make much sense and will
make debug harder when trying to identify where the 404 originated from.
That being said, it is still very useful to be able to make a reverse
proxy send any desired response. So you can do that by hard coding the
error you want to send in the errorfile. Example:
errorfile 400 /tmp/400
http-request deny deny_status 400 if { path_beg /test }
Then in your /tmp/400:
HTTP/1.1 404 Not Found
Content-Type: text/html
Content-Length: 128
Connection: Closed
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
</body>
</html>
This way you will be able to send a 404 to the client but you will see
400 in the logs.
> ----
>
> Moreover, dare I say, this feature could be "abused" to serve a few
> "static files" (like `favicon.ico` or `robots.txt`) directly from
> HAProxy without requiring Lua. In fact the most viewed topic on
> HAProxy's forum is exactly about this:
>
>
> https://discourse.haproxy.org/t/how-do-i-serve-a-single-static-file-from-haproxy/32
>
> Ciprian.
>
HAProxy provides a cache, which was designed to perform cache on small
objects (favicon, css...). So this may be what you are looking for.
--
Moemen MHEDHBI