Permalink
Browse files

add implementation of X-Forwarded-For header (Fabian Linzberger)

* Add x_forwarded_for_log_proxy_whitelist global config parameter and
  parsing for it.

* Add x_forwarded_for header in record definition and collection
  (based on code by Chris Double he mentions on
  http://www.bluishcoder.co.nz/2006/04/handling-x-forwarded-for-in-yaws.html)

* For yaws_server:maybe_access_log/3: if originating IP is present in
  x_forwarded_for_log_proxy_whitelist, log last IP found in
  x-forwarded-for header instead of originating IP
  • Loading branch information...
lefant authored and vinoski committed Oct 6, 2010
1 parent 43d6620 commit a1e2e7463ccf382f701071893beb8f5160ef23b4
Showing with 63 additions and 10 deletions.
  1. +17 −5 include/yaws.hrl
  2. +1 −0 include/yaws_api.hrl
  3. +3 −1 src/yaws.erl
  4. +18 −1 src/yaws_config.erl
  5. +24 −3 src/yaws_server.erl
View
@@ -78,17 +78,23 @@
max_num_cached_files = 400,
max_num_cached_bytes = 1000000, %% 1 MEG
max_size_cached_file = 8000,
- max_connections = nolimit, %% max number of TCP connections
- process_options = [], %% Override default connection handler processes
- %% spawn options for performance/memory tuning.
- %% [] | [{fullsweep_after, Number}, {min_heap_size, Size}]
- %% other options such as monitor, link are ignored.
+ max_connections = nolimit, % max number of TCP connections
+ process_options = [], % Override default connection handler
+ % processes spawn options for
+ % performance/memory tuning. [] |
+ % [{fullsweep_after,Number},
+ % {min_heap_size, Size}] other options such
+ % as monitor, link are ignored.
large_file_chunk_size = 10240,
mnesia_dir = [],
log_wrap_size = 10000000, % wrap logs after 10M
cache_refresh_secs = 30, % seconds (auto zero when debug)
include_dir = [], %% list of inc dirs for .yaws files
phpexe = "/usr/bin/php-cgi", %% cgi capable php executable
+ x_forwarded_for_log_proxy_whitelist = [], % list of proxy server
+ % ips we will replace with the last element
+ % of the list in the X-Forwarded-For http
+ % header in logs
yaws, %% server string
id = "default", %% string identifying this instance of yaws
enable_soap = false, %% start yaws_soap_srv iff true
@@ -320,3 +326,9 @@
%% Typically used in error printouts as in:
%% error_logger:format("Err ~p at ~p~n", [Reason, ?stack()])
-define(stack(), try throw(1) catch _:_ -> erlang:get_stacktrace() end).
+
+
+%%% The following is for emacs, do not remove
+%%% Local Variables:
+%%% comment-column: 36
+%%% End:
View
@@ -68,6 +68,7 @@
content_encoding,
authorization,
transfer_encoding,
+ x_forwarded_for,
other = [] %% misc other headers
}).
View
@@ -1980,7 +1980,9 @@ http_collect_headers(CliSock, Req, H, SSL, Count) when Count < 1000 ->
http_collect_headers(CliSock, Req,
H#headers{authorization = parse_auth(X)},
SSL, Count+1);
-
+ {ok, {http_header, _Num, 'X-Forwarded-For', _, X}} ->
+ http_collect_headers(CliSock, Req, H#headers{x_forwarded_for=X},
+ SSL, Count+1);
{ok, http_eoh} ->
H;
View
@@ -774,7 +774,14 @@ fload(FD, globals, GC, C, Cs, Lno, Chars) ->
%% just ignore - not relevant any longer
fload(FD, globals, GC,
C, Cs, Lno+1, Next);
-
+ ["x_forwarded_for_log_proxy_whitelist", '=' | Suffixes] ->
+ case ip_list_parser(Suffixes, []) of
+ error ->
+ {error, ?F("Expect IP address at line ~w:", [Lno])};
+ {ok, Addrs} ->
+ GC2 = GC#gconf{x_forwarded_for_log_proxy_whitelist = Addrs},
+ fload(FD, globals, GC2, C, Cs, Lno+1, Next)
+ end;
['<', "server", Server, '>'] -> %% first server
fload(FD, server, GC, #sconf{servername = Server},
Cs, Lno+1, Next);
@@ -1942,3 +1949,13 @@ delete_sconf(SC) ->
update_gconf(GC) ->
ok = gen_server:call(yaws_server, {update_gconf, GC}, infinity).
+
+ip_list_parser([], Addrs) ->
+ {ok,lists:reverse(Addrs)};
+ip_list_parser([IP|IPs], Addrs) ->
+ case inet_parse:address(IP) of
+ {error, _} ->
+ error;
+ {ok, Addr} ->
+ ip_list_parser(IPs, [Addr|Addrs])
+ end.
View
@@ -1303,9 +1303,8 @@ maybe_auth_log(Item, ARG) ->
yaws_log:authlog(SC#sconf.servername, IP, Path, Item)
end.
-
-
maybe_access_log(Ip, Req, H) ->
+ GC=get(gc),
SC=get(sc),
case ?sc_has_access_log(SC) of
true ->
@@ -1342,7 +1341,29 @@ maybe_access_log(Ip, Req, H) ->
_ ->
"-"
end,
- yaws_log:accesslog(SC#sconf.servername, Ip, User,
+ RealIp = case GC#gconf.x_forwarded_for_log_proxy_whitelist of
+ [] ->
+ Ip;
+ ProxyIps ->
+ case lists:member(Ip, ProxyIps) of
+ false ->
+ Ip;
+ true ->
+ FwdFor = H#headers.x_forwarded_for,
+ case yaws:split_sep(FwdFor, $,) of
+ [] -> Ip;
+ IPs ->
+ LastIp = lists:last(IPs),
+ case inet_parse:address(LastIp) of
+ {error, _} ->
+ "invalid ip: " ++ LastIp;
+ {ok, ClientIp} ->
+ ClientIp
+ end
+ end
+ end
+ end,
+ yaws_log:accesslog(SC#sconf.servername, RealIp, User,
[Meth, $\s, Path, $\s, Ver],
Status, Len, Referrer, UserAgent);
false ->

0 comments on commit a1e2e74

Please sign in to comment.