Bluish Coder

Programming Languages, Martials Arts and Computers. The Weblog of Chris Double.


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.

Tags


This site is accessable over tor as hidden service mh7mkfvezts5j6yu.onion, or Freenet using key:
USK@1ORdIvjL2H1bZblJcP8hu2LjjKtVB-rVzp8mLty~5N4,8hL85otZBbq0geDsSKkBK4sKESL2SrNVecFZz9NxGVQ,AQACAAE/bluishcoder/-44/


Tags

Archives
Links