diff --git a/doc/guide.tex b/doc/guide.tex index f77e1ee83..d3db10338 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -824,7 +824,7 @@ The available modules, their purpose and the options allowed by each one are: \titem{\texttt{ejabberd\_http}} Handles incoming HTTP connections.\\ Options: \texttt{captcha}, \texttt{certfile}, \texttt{http\_bind}, \texttt{http\_poll}, - \texttt{request\_handlers}, \texttt{tls}, \texttt{web\_admin}\\ + \texttt{request\_handlers}, \texttt{tls}, \texttt{trusted\_proxies}, \texttt{web\_admin}\\ \end{description} @@ -946,6 +946,10 @@ This is a detailed description of each option allowed by the listening modules: which can be enabled in \ejabberd{} with the option \term{starttls}. If this option is set, you should also set the \option{certfile} option. The option \term{tls} can also be used in \term{ejabberd\_http} to support HTTPS. + \titem{\{trusted\_proxies, all | [IpString]\}} \ind{options!trusted\_proxies} + Specify what proxies are trusted when an HTTP request contains the header \term{X-Forwarded-For} + You can specify \term{all} to allow all proxies, or specify a list of IPs in string format. + The default value is: \term{["127.0.0.1"]} \titem{web\_admin} \ind{options!web\_admin}\ind{web admin}This option enables the Web Admin for \ejabberd{} administration which is available at \verb|http://server:port/admin/|. Login and password are the username and diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index 313e0f264..37f56d085 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -362,13 +362,15 @@ process_request(#state{request_method = Method, LQ -> LQ end, - {ok, IP} = + {ok, IPHere} = case SockMod of gen_tcp -> inet:peername(Socket); _ -> SockMod:peername(Socket) end, + XFF = proplists:get_value('X-Forwarded-For', RequestHeaders, []), + IP = analyze_ip_xff(IPHere, XFF, Host), Request = #request{method = Method, path = LPath, q = LQuery, @@ -409,13 +411,15 @@ process_request(#state{request_method = Method, request_headers = RequestHeaders, request_handlers = RequestHandlers} = State) when (Method=:='POST' orelse Method=:='PUT') andalso is_integer(Len) -> - {ok, IP} = + {ok, IPHere} = case SockMod of gen_tcp -> inet:peername(Socket); _ -> SockMod:peername(Socket) end, + XFF = proplists:get_value('X-Forwarded-For', RequestHeaders, []), + IP = analyze_ip_xff(IPHere, XFF, Host), case SockMod of gen_tcp -> inet:setopts(Socket, [{packet, 0}]); @@ -466,6 +470,31 @@ process_request(State) -> ejabberd_web:make_xhtml([{xmlelement, "h1", [], [{xmlcdata, "400 Bad Request"}]}])). +%% Support for X-Forwarded-From +analyze_ip_xff(IP, [], _Host) -> + IP; +analyze_ip_xff({IPLast, Port}, XFF, Host) -> + [ClientIP | ProxiesIPs] = string:tokens(XFF, ", ") + ++ [inet_parse:ntoa(IPLast)], + TrustedProxies = case ejabberd_config:get_local_option( + {trusted_proxies, Host}) of + undefined -> []; + TPs -> TPs + end, + IPClient = case is_ipchain_trusted(ProxiesIPs, TrustedProxies) of + true -> + {ok, IPFirst} = inet_parse:address(ClientIP), + ?DEBUG("The IP ~w was replaced with ~w due to header " + "X-Forwarded-For: ~s", [IPLast, IPFirst, XFF]), + IPFirst; + false -> + IPLast + end, + {IPClient, Port}. +is_ipchain_trusted(_UserIPs, all) -> + true; +is_ipchain_trusted(UserIPs, TrustedIPs) -> + [] == UserIPs -- ["127.0.0.1" | TrustedIPs]. recv_data(State, Len) -> recv_data(State, Len, []).