Project

General

Profile

Feature #7586 » 0001-Add-reverse-proxy-client-ip-wt_config.xml-option.patch

Bruce Toll, 05/28/2020 08:16 PM

View differences:

src/Wt/Http/Request.C
WServer *server = WServer::instance();
const bool behindReverseProxy = server && server->configuration().behindReverseProxy();
return request_->clientAddress(behindReverseProxy);
const bool reverseProxyClientIp = server && server->configuration().reverseProxyClientIp();
return request_->clientAddress(behindReverseProxy, reverseProxyClientIp);
}
WSslInfo *Request::sslInfo() const
src/Wt/WEnvironment.C
host_ += ":" + request.serverPort();
}
clientAddress_ = request.clientAddress(conf.behindReverseProxy());
clientAddress_ = request.clientAddress(conf.behindReverseProxy(),
conf.reverseProxyClientIp());
const char *cookie = request.headerValue("Cookie");
doesCookies_ = cookie;
src/http/ProxyReply.C
LOG_SECURE("wthttp is not behind a reverse proxy, dropping " << it->name.str() << " header");
}
} else if (it->name.iequals("X-Forwarded-For") ||
it->name.iequals("Client-IP")) {
(wtConfiguration.reverseProxyClientIp() && it->name.iequals("Client-IP"))) {
if (wtConfiguration.behindReverseProxy()) {
forwardedFor = it->value.str() + ", ";
}
src/web/Configuration.C
properties_.clear();
xhtmlMimeType_ = false;
behindReverseProxy_ = false;
reverseProxyClientIp_ = false;
redirectMsg_ = "Load basic HTML";
serializedEvents_ = false;
webSockets_ = false;
......
return behindReverseProxy_;
}
bool Configuration::reverseProxyClientIp() const
{
READ_LOCK;
return reverseProxyClientIp_;
}
std::string Configuration::redirectMessage() const
{
READ_LOCK;
......
behindReverseProxy_ = enabled;
}
void Configuration::setReverseProxyClientIp(bool enabled)
{
reverseProxyClientIp_ = enabled;
}
void Configuration::readApplicationSettings(xml_node<> *app)
{
xml_node<> *sess = singleChildElement(app, "session-management");
......
redirectMsg_ = singleChildElementValue(app, "redirect-message", redirectMsg_);
setBoolean(app, "behind-reverse-proxy", behindReverseProxy_);
setBoolean(app, "reverse-proxy-client-ip", reverseProxyClientIp_);
setBoolean(app, "strict-event-serialization", serializedEvents_);
setBoolean(app, "web-sockets", webSockets_);
src/web/Configuration.h
void setAppRoot(const std::string& path);
std::string appRoot() const;
bool behindReverseProxy() const;
bool reverseProxyClientIp() const;
std::string redirectMessage() const;
bool serializedEvents() const;
bool webSockets() const;
......
void setUseSlashExceptionForInternalPaths(bool enabled);
void setNeedReadBodyBeforeResponse(bool needed);
void setBehindReverseProxy(bool enabled);
void setReverseProxyClientIp(bool enabled);
std::string generateSessionId();
bool registerSessionId(const std::string& oldId, const std::string& newId);
......
PropertyMap properties_;
bool xhtmlMimeType_;
bool behindReverseProxy_;
bool reverseProxyClientIp_;
std::string redirectMsg_;
bool serializedEvents_;
bool webSockets_;
src/web/WebRequest.C
return urlParams_;
}
std::string WebRequest::clientAddress(const bool behindReverseProxy) const
std::string WebRequest::clientAddress(const bool behindReverseProxy,
const bool reverseProxyClientIp) const
{
std::string result;
......
* Determine client address, taking into account proxies
*/
if (behindReverseProxy) {
std::string clientIp = str(headerValue("Client-IP"));
boost::trim(clientIp);
std::vector<std::string> ips;
if (!clientIp.empty())
boost::split(ips, clientIp, boost::is_any_of(","));
if (reverseProxyClientIp) {
std::string clientIp = str(headerValue("Client-IP"));
boost::trim(clientIp);
if (!clientIp.empty())
boost::split(ips, clientIp, boost::is_any_of(","));
}
std::string forwardedFor = str(headerValue("X-Forwarded-For"));
boost::trim(forwardedFor);
src/web/WebRequest.h
virtual const std::vector<std::pair<std::string, std::string> >& urlParams() const;
std::string clientAddress(bool behindReverseProxy) const;
std::string clientAddress(bool behindReverseProxy, bool reverseProxyClientIp) const;
protected:
const EntryPoint *entryPoint_;
src/web/WebSession.C
}
std::string ca = handler.request()->clientAddress(
controller_->configuration().behindReverseProxy());
controller_->configuration().behindReverseProxy(),
controller_->configuration().reverseProxyClientIp());
if (ca != env_->clientAddress()) {
bool isInvalid = sessionIdCookie_.empty();
test/http/HttpClientServerTest.C
#include <boost/test/unit_test.hpp>
#include <web/Configuration.h>
#include <Wt/WResource.h>
#include <Wt/WServer.h>
#include <Wt/WIOService.h>
#include <Wt/Http/Client.h>
#include <Wt/Http/Message.h>
#include <Wt/Http/Response.h>
#include <Wt/Http/ResponseContinuation.h>
#include <Wt/Http/Request.h>
......
delaySendingBody_(false),
haveEverMoreData_(false),
haveRandomMoreData_(false),
clientAddressTest_(false),
aborted_(0)
{ }
......
haveRandomMoreData_ = true;
}
void clientAddressTest() {
clientAddressTest_ = true;
}
int abortedCount() const {
return aborted_;
}
......
{
if (continuation_)
handleWithContinuation(request, response);
else if (clientAddressTest_)
handleClientAddress(request, response);
else
handleSimple(request, response);
}
......
bool delaySendingBody_;
bool haveEverMoreData_;
bool haveRandomMoreData_;
bool clientAddressTest_;
int aborted_;
void handleSimple(const Http::Request& request,
......
c->waitForMoreData();
}
}
void handleClientAddress(const Http::Request& request,
Http::Response& response)
{
response.setStatus(200);
response.out() << request.clientAddress();
}
};
class Server : public WServer
{
public:
......
}
}
#endif // WT_THREADED
BOOST_AUTO_TEST_CASE( client_address_default_not_behind_reverse_proxy )
{
Server server;
server.resource().clientAddressTest();
if (server.start()) {
Client client;
/*
* NOTE: Need to use public IP address or clientAddress() will ignore
* The addresses are not accessed and I believe are Google DNS
*/
std::vector<Http::Message::Header> headers;
headers.push_back(Http::Message::Header("X-Forwarded-For", "8.8.8.8"));
headers.push_back(Http::Message::Header("Client-IP", "8.8.4.4"));
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// should ignore "Client-IP" and "X-Forwarded-For" headers if not behindReverseProxy
BOOST_REQUIRE(client.message().body() == "127.0.0.1");
}
}
BOOST_AUTO_TEST_CASE( client_address_behind_reverse_proxy_without_reverse_proxy_client_ip )
{
Server server;
server.resource().clientAddressTest();
server.configuration().setBehindReverseProxy(true);
if (server.start()) {
Client client;
/*
* NOTE: Need to use public IP address or clientAddress() will ignore
* The addresses are not accessed and I believe are Google DNS
*/
std::vector<Http::Message::Header> headers;
headers.push_back(Http::Message::Header("X-Forwarded-For", "8.8.8.8"));
headers.push_back(Http::Message::Header("Client-IP", "8.8.4.4"));
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// should use "X-Forwarded-For" header if behindReverseProxy without reverseProxyClientIp
BOOST_REQUIRE(client.message().body() == "8.8.8.8");
}
}
BOOST_AUTO_TEST_CASE( client_address_behind_reverse_proxy_with_reverse_proxy_client_ip )
{
Server server;
server.resource().clientAddressTest();
server.configuration().setBehindReverseProxy(true);
server.configuration().setReverseProxyClientIp(true);
if (server.start()) {
Client client;
/*
* NOTE: Need to use public IP address or clientAddress() will ignore
* The addresses are not accessed and I believe are Google DNS
*/
std::vector<Http::Message::Header> headers;
headers.push_back(Http::Message::Header("X-Forwarded-For", "8.8.8.8"));
headers.push_back(Http::Message::Header("Client-IP", "8.8.4.4"));
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// should use "Client-IP" header if behindReverseProxy with reverseProxyClientIp
BOOST_REQUIRE(client.message().body() == "8.8.4.4");
}
}
#endif // WT_THREADED
wt_config.xml.in
-->
<behind-reverse-proxy>false</behind-reverse-proxy>
<!-- Whether HTTP Client-IP headers should be processed
If behind-reverse-proxy is enabled, this option can be used
to enable processing of addresses in Client-IP headers, as
well as X-Forwarded-For headers.
If your reverse proxy does not use Client-IP headers, you
should leave this at its default setting of false.
-->
<reverse-proxy-client-ip>false</reverse-proxy-client-ip>
<!-- Whether inline CSS is allowed.
Some Wt widgets will insert CSS rules in the the inline
(1-1/3)