Feature #7586 » 0001-Add-reverse-proxy-client-ip-wt_config.xml-option.patch
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
|