Permalink
Browse files

Make the parameter "x_forwarded_for_log_proxy_whitelist" obsolete

This parameter is now ignored and throws a warning when yaws starts.
If necessary, it can be replaced by a simple logger_mod on top of
yaws_log. To explain how to write such module, an example was added
into the www directory (www/logger_mod.yaws).
  • Loading branch information...
1 parent 4dc3b76 commit f31e4b8481c3652956e005c84094143e3cb887f0 @capflam capflam committed May 3, 2012
Showing with 180 additions and 51 deletions.
  1. +2 −15 doc/yaws.tex
  2. +0 −4 include/yaws.hrl
  3. +2 −14 man/yaws.conf.5
  4. +4 −18 src/yaws_config.erl
  5. +1 −0 www/TAB.inc
  6. +171 −0 www/logger_mod.yaws
View
@@ -2290,20 +2290,7 @@ \section{Global Part}
Load all config files in specified directory.
\item \verb+x_forwarded_for_log_proxy_whitelist = ListOfUpstreamProxyServerIps+ ---
-
- In case \Yaws\ is running behind a HTTP proxy or HTTP load
- balancer it may be desirable to configure this proxy to put the IP
- address of the originating client into the
- \textit{X-Forwarded-For} header and have \Yaws\ log this IP
- address as the request's source IP address instead of logging the
- proxy server's IP address over and over again. This setting
- determines which source IP addresses are rewritten in this manner.
-
- For example, if there are two proxies with the IP addresses
- 192.168.0.1 and 192.168.0.2 in front of Yaws, we can specify:
-\begin{verbatim}
- x_forwarded_for_log_proxy_whitelist = 192.168.0.1 192.168.0.2
-\end{verbatim}
+ \textit{This target is deprecated and will be ignored}.
\end{itemize}
@@ -2388,7 +2375,7 @@ \section{Server Part}
return \verb+{true, State}+ and if an error occurred, it must
return \verb+false+.
- \item \verb+Module:close_log(ServerName, Type)+ -- This
+ \item \verb+Module:close_log(ServerName, Type, State)+ -- This
function is called for this virtual server when
\Yaws\ is stopped.
View
@@ -86,10 +86,6 @@
include_dir = [], % list of inc dirs for .yaws files
phpexe = "/usr/bin/php-cgi", % cgi capable php executable
- %% list of proxy server ips we will replace with the last element of
- %% the list in the X-Forwarded-For http header in logs
- x_forwarded_for_log_proxy_whitelist = [],
-
yaws, % server string
id = "default", % string identifying this instance of yaws
View
@@ -244,19 +244,7 @@ Load all config file in specified directory.
.TP
\fBx_forwarded_for_log_proxy_whitelist = ListOfUpstreamProxyServerIps\fR
-In case Yaws is running behind a HTTP proxy or HTTP load balancer it may be
-desirable to configure this proxy to put the IP address of the originating
-client into the \fIX-Forwarded-For\fR header and have Yaws log this IP address
-as the request's source IP address instead of logging the proxy server's IP
-address over and over again. This setting determines which source IP addresses
-are rewritten in this manner.
-
-For example, if there are two proxies with the IP addresses 192.168.0.1 and
-192.168.0.2 in front of Yaws, we can specify:
-
-.nf
- x_forwarded_for_log_proxy_whitelist = 192.168.0.1 192.168.0.2
-.fi
+\fBthis target is deprecated and will be ignored.\fR
.SH SERVER PART
Yaws can virthost several web servers on the same IP address as well as several
@@ -334,7 +322,7 @@ an error occurred, it must return \fIfalse\fR.
.RE
.HP
-\fBModule:close_log(ServerName, Type)\fR
+\fBModule:close_log(ServerName, Type, State)\fR
.RS 12
This function is called for this virtual server when Yaws is stopped.
.RE
View
@@ -861,14 +861,10 @@ 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;
+ ["x_forwarded_for_log_proxy_whitelist", '=' | _] ->
+ error_logger:info_msg("Warning, x_forwarded_for_log_proxy_whitelist"
+ " is deprecated and ignored~n", []),
+ fload(FD, globals, GC, C, Cs, Lno+1, Next);
["ysession_mod", '=', Mod_str] ->
Ysession_mod = list_to_atom(Mod_str),
fload(FD, globals, GC#gconf{ysession_mod = Ysession_mod},
@@ -2348,16 +2344,6 @@ 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.
-
-define(MAXBITS_IPV4, 32).
-define(MAXBITS_IPV6, 128).
View
@@ -57,6 +57,7 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<div class="%%websockets%%"> <a href="/websockets.yaws">Web Sockets</a> </div>
<a href="/shoppingcart/index.yaws">Tiny shopping cart</a>
<div class="%%yapp_intro%%"> <a href="/yapp_intro.yaws">Yaws applications</a></div>
+<div class="%%logger_mod%%"> <a href="/logger_mod.yaws">Write your own logger</a></div>
<h4> Misc </h4>
<div class="%%internals%%"> <a href="/internals.yaws">Internals</a> </div>
View
@@ -0,0 +1,171 @@
+<erl>
+out(A) ->
+ {ssi, "TAB.inc", "%%",[{"logger_mod", "choosen"}]}.
+</erl>
+
+
+<div id="entry">
+
+<h1>Write your own logger</h1>
+
+<p>
+To illustrate how to write a module that handles auth and access logging in
+Yaws, we will get a concrete example: How to log the original client IP behind a
+HTTP reverse proxy.
+</p>
+
+<p>
+When Yaws is running behind a HTTP reverse proxy, the source IP of a connection
+is always the proxy's IP address. It is also this IP address that you will find
+in your log files. In this situation, it may be desirable to log the real client
+IP address by extracting it from the "<a
+href="http://en.wikipedia.org/wiki/X-Forwarded-For"
+target="_blank">X-Forwarded-For</a>" header.
+</p>
+
+<p>
+It can easily be done by writting a simple module to handle access and auth
+logging on top of yaws_log. yaws_log is the default logger of Yaws and it can be
+override by using the directive "logger_mod" in yaws.conf:
+</p>
+
+<div class="box">
+ <verbatim>
+<server www.hyber.org>
+ port = 80
+ listen = 0.0.0.0
+ logger_mod = simple_logger
+ ...
+</server>
+
+ </verbatim>
+</div>
+
+<p>
+In this example, "simple_logger" will be used in place of yaws_log. This module
+must implement the behaviour yaws_logger by exporting the following functions:
+</p>
+
+<div class="box">
+ <verbatim>
+Module:open_log(ServerName, Type, LogDir) -> {true, State} | false.
+ ServerName :: string()
+ Type :: access | auth
+ LogDir :: string()
+ State :: term()
+
+
+Module:close_log(ServerName, Type, State) -> ok.
+ ServerName :: string()
+ Type :: access | auth
+ State :: term()
+
+
+Module:wrap_log(ServerName, Type, State, LogWrapSize) -> NewState.
+ ServerName :: string()
+ Type :: access | auth
+ State, NewState :: term()
+
+
+Module:write_log(ServerName, Type, State, Infos) -> ok.
+ ServerName :: string()
+ Type :: access | auth
+ State :: term()
+ Infos :: {Ip,Req,InHdrs,OutHdrs,Time} | %% when type =:= access
+ {Ip,Path,Item} %% when type =:= auth
+
+ Ip :: inet:ip_address() | inet:hostname() | unknown
+ Req :: #http_request{} %% defined in yaws_api.hrl
+ InHdrs :: #headers{} %% defined in yaws_api.hrl
+ OutHdrs :: #outh{} %% defined in yaws.hrl
+ Time :: non_neg_integer() %% The time taken to serve the request, in microseconds
+ Path :: string() %% the URI path of the request
+ Item :: {ok, User} | 403 | {401, Realm} %% the result of an auth request
+
+ </verbatim>
+</div>
+
+
+<p>
+Back to our example. The only thing we want to do is read the "X-Forwarded-For"
+header to extract the real client IP address. The logging will be delegated to
+yaws_log.
+</p>
+
+<p>
+If the "X-Forwarded-For" header is defined, then the originating IP address of a
+client is the left-most IP address. But, because this header can be falsified,
+we will define a list of trustworthy proxies. For simplicity, we will use a
+macro. Here is our logger:
+</p>
+
+<div class="box">
+ <verbatim>
+-module(yaws_revproxy_logger).
+
+-behaviour(yaws_logger).
+
+-include_lib("yaws/include/yaws.hrl").
+-include_lib("yaws/include/yaws_api.hrl").
+
+-export([open_log/3, close_log/3, wrap_log/4, write_log/4]).
+
+-define(REVPROXY_WHITELIST, [{192,168,0,1}, {192,168,0,2}]).
+
+%% ===================================================================
+open_log(ServerName, Type, Dir) ->
+ yaws_log:open_log(ServerName, Type, Dir).
+
+
+close_log(ServerName, Type, State) ->
+ yaws_log:close_log(ServerName, Type, State).
+
+
+wrap_log(ServerName, Type, Data, LogWrapSize) ->
+ yaws_log:wrap_log(ServerName, Type, Data, LogWrapSize).
+
+
+write_log(ServerName, auth, State, {Ip, Path, Item}) ->
+ yaws_log:write_log(ServerName, auth, State, {Ip, Path, Item});
+write_log(ServerName, access, State, {Ip, Req, InH, OutH, Time}) ->
+ RealIp = real_client_ip(Ip, ?REVPROXY_WHITELIST, InH),
+ yaws_log:write_log(ServerName, access, State, {RealIp, Req, InH, OutH, Time}).
+
+
+real_client_ip(Ip, ProxyWhitelist, Hdrs) ->
+ case lists:member(Ip, ProxyWhitelist) of
+ true ->
+ FwdFor = Hdrs#headers.x_forwarded_for,
+ case yaws:split_sep(FwdFor, $,) of
+ [FirstIp|_Proxies] ->
+ %% We might check if the last proxy is the remote
+ %% address of the request, i.e hd(_Proxies) =:= Ip.
+ case inet_parse:address(FirstIp) of
+ {error, _} -> unknown;
+ {ok, ClientIp} -> ClientIp
+ end;
+ [] ->
+ Ip
+ end;
+ false ->
+ Ip
+ end.
+
+ </verbatim>
+</div>
+
+<p>
+Now, we just need to compile this module and update yaws.conf accordingly.
+</p>
+
+<p>
+<i>Note: In previous version of Yaws, we could use
+"x_forwarded_for_log_proxy_whitelist" to do the same thing. This parameter is
+now deprecated.</i>
+</p>
+</div>
+<erl>
+out(A) -> {ssi, "END2",[],[]}.
+</erl>
+
+

0 comments on commit f31e4b8

Please sign in to comment.