2006-05-25
Handling X-Forwarded-For in Yaws
I run a Yaws webserver behind a Pound proxy. This works really well but all the IP addresses logged by Yaws appear as the localhost (127.0.0.1). This is due to Yaws picking up the IP address of the proxy as it forwards the request.
Pound sends the original IP address as a header in the HTTP request called X-Forwarded-For. This finally bugged me enough to modify Yaws to display the X-Forwarded-For IP address if the header was there, otherwise display the normal IP address.
The 'quick hack' change was quite simple. I modified the include/yaws_api.hrl
file to add x_fowarded_for
to the headers
record:
-record(headers, {
connection,
accept,
host,
if_modified_since,
if_match,
if_none_match,
if_range,
if_unmodified_since,
range,
referer,
user_agent,
accept_ranges,
cookie = [],
keep_alive,
location,
content_length,
content_type,
content_encoding,
authorization,
transfer_encoding,
x_forwarded_for,
other = [] %% misc other headers
}).
The idea being that this value will be set by Yaws when it first parses the HTTP request headers. This is done by modifying the http_collect_headers function in 'src/yaws.erl' to contain the following code:
{ok, {http_header, _Num, 'X-Forwarded-For',_, X}} ->
http_collect_headers(CliSock, Req, H#headers{x_forwarded_for = X},SSL)
This was basically a copy and paste of the way the other headers are handled. In the code that handles the logging I just check for the header and output the IP address if it is there, otherwise use the IP address from the request itself. This is done by a helper function I added to src/yaws_server.erl
:
x_forwarded_for_or_ip(Ip, Item)->
case Item of
undefined -> Ip;
Item -> yaws:parse_ip(Item)
end.
The 'Item' passed to this function is the x_forwarded_for
element of the 'headers' record. If the 'X-Forwarded-For' header exists I parse the string into an IP address using yaws:parse_ip/1
.
All that remains is to change the maybe_access_log/3
in src/yaws_server.erl
to call this helper function:
TrueIp = x_forwarded_for_or_ip(Ip, H#headers.x_forwarded_for),
yaws_log:accesslog(SC#sconf.servername, TrueIp, User,
[Meth, $\s, Path, $\s, Ver],
Status, Len, Referrer, UserAgent);
Now all my IP's appear correctly forwarded from Pound in the Yaws server.