Project

General

Profile

Feature #7586 » 0001-Issue-7586-configurable-trusted-proxies-and-forwarde.patch

Roel Standaert, 09/23/2020 09:46 AM

View differences:

examples/widgetgallery/approot/wt_config.xml
</user-agents>
<web-sockets>true</web-sockets>
<behind-reverse-proxy>true</behind-reverse-proxy>
<forwarded-header>X-Forwarded-For</forwarded-header>
<trusted-proxies>127.0.0.1,::1</trusted-proxies>
<bootstrap-method>
<for path="*">progressive</for>
<for path="/forms/line_text-editor">default</for>
src/Wt/Http/Request.C
return std::string();
WServer *server = WServer::instance();
const bool behindReverseProxy = server && server->configuration().behindReverseProxy();
return request_->clientAddress(behindReverseProxy);
return request_->clientAddress(server->configuration());
}
WSslInfo *Request::sslInfo() const
......
return sslInfo_.get();
if (request_) {
auto server = WServer::instance();
bool behindReverseProxy = server && server->configuration().behindReverseProxy();
sslInfo_ = request_->sslInfo(behindReverseProxy);
sslInfo_ = request_->sslInfo(server->configuration());
}
return sslInfo_.get();
}
src/Wt/WEnvironment.C
std::string oldHost = host_;
host_ = str(request.headerValue("Host"));
if(conf.behindReverseProxy()) {
if (conf.isTrustedProxy(request.remoteAddr())) {
std::string forwardedHost = str(request.headerValue("X-Forwarded-Host"));
if (!forwardedHost.empty()) {
......
urlScheme_ = str(request.urlScheme());
Configuration& conf = session_->controller()->configuration();
#ifndef WT_TARGET_JAVA
if (conf.behindReverseProxy() || server()->dedicatedSessionProcess()) {
#else
if (conf.behindReverseProxy()){
#endif
std::string forwardedProto = str(request.headerValue("X-Forwarded-Proto"));
if (!forwardedProto.empty()) {
std::string::size_type i = forwardedProto.rfind(',');
if (i == std::string::npos)
urlScheme_ = forwardedProto;
else
urlScheme_ = forwardedProto.substr(i+1);
}
if (conf.isTrustedProxy(request.remoteAddr())) {
std::string forwardedProto = str(request.headerValue("X-Forwarded-Proto"));
if (!forwardedProto.empty()) {
std::string::size_type i = forwardedProto.rfind(',');
if (i == std::string::npos)
urlScheme_ = forwardedProto;
else
urlScheme_ = forwardedProto.substr(i+1);
}
}
}
......
if(!str(request.headerValue("Redirect-Secret")).empty())
session_->controller()->redirectSecret_ = str(request.headerValue("Redirect-Secret"));
sslInfo_ = request.sslInfo(conf.behindReverseProxy());
sslInfo_ = request.sslInfo(conf);
#endif
setUserAgent(str(request.headerValue("User-Agent")));
......
* If behind a reverse proxy, use external host, schema as communicated using 'X-Forwarded'
* headers.
*/
#ifndef WT_TARGET_JAVA
if (conf.behindReverseProxy() || server()->dedicatedSessionProcess()) {
#else
if (conf.behindReverseProxy()){
#endif
if (conf.isTrustedProxy(request.remoteAddr())) {
std::string forwardedHost = str(request.headerValue("X-Forwarded-Host"));
if (!forwardedHost.empty()) {
......
host_ += ":" + request.serverPort();
}
clientAddress_ = request.clientAddress(conf.behindReverseProxy());
clientAddress_ = request.clientAddress(conf);
const char *cookie = request.headerValue("Cookie");
doesCookies_ = cookie;
src/fcgi/FCGIStream.C
return true;
}
virtual std::unique_ptr<WSslInfo> sslInfo(bool) const override
virtual std::unique_ptr<WSslInfo> sslInfo(const Wt::Configuration &) const override
{
#ifdef WT_WITH_SSL
std::string clientCert = str(envValue("SSL_CLIENT_CERT"));
src/http/HTTPRequest.C
return false;
}
std::unique_ptr<Wt::WSslInfo> HTTPRequest::sslInfo(bool behindReverseProxy) const
std::unique_ptr<Wt::WSslInfo> HTTPRequest::sslInfo(const Wt::Configuration &conf) const
{
auto result = reply_->request().sslInfo();
if (behindReverseProxy) {
if (conf.isTrustedProxy(remoteAddr())) {
#ifdef HTTP_WITH_SSL
if (!result)
result = sslInfoFromJson();
src/http/HTTPRequest.h
virtual const std::string& remoteAddr() const override;
virtual const char *urlScheme() const override;
bool isSynchronous() const;
virtual std::unique_ptr<Wt::WSslInfo> sslInfo(bool behindReverseProxy) const override;
virtual std::unique_ptr<Wt::WSslInfo> sslInfo(const Wt::Configuration & conf) const override;
virtual const std::vector<std::pair<std::string, std::string> > &urlParams() const override;
private:
src/http/ProxyReply.C
std::string forwardedFor;
std::string forwardedProto = request_.urlScheme;
std::string forwardedPort;
std::string forwardedHost;
const Wt::Configuration& wtConfiguration
= connection()->server()->controller()->configuration();
const bool trustedProxy = wtConfiguration.isTrustedProxy(request_.remoteIP);
for (Request::HeaderList::const_iterator it = request_.headers.begin();
it != request_.headers.end(); ++it) {
if (it->name.iequals("Connection") || it->name.iequals("Keep-Alive") ||
......
"requests to a child process. Maybe someone is trying to spoof this "
"header?");
} else if (it->name.istarts_with("X-SSL-Client-")) {
if (wtConfiguration.behindReverseProxy()) {
if (trustedProxy) {
os << it->name << ": " << it->value << "\r\n";
} else {
LOG_SECURE("wthttp is not behind a reverse proxy, dropping " << it->name.str() << " header");
LOG_SECURE("wthttp is not behind a trusted reverse proxy, dropping " << it->name.str() << " header");
}
} else if (it->name.iequals("X-Forwarded-For") ||
it->name.iequals("Client-IP")) {
if (wtConfiguration.behindReverseProxy()) {
} else if (it->name.iequals(wtConfiguration.forwardedHeader().c_str())) {
if (trustedProxy) {
forwardedFor = it->value.str() + ", ";
} else {
LOG_SECURE("wthttp is not behind a trusted reverse proxy, dropping " << it->name.str() << " header");
}
} else if (it->name.iequals("Upgrade")) {
if (it->value.iequals("websocket")) {
establishWebSockets = true;
}
} else if (it->name.iequals("X-Forwarded-Proto")) {
if (wtConfiguration.behindReverseProxy()) {
if (trustedProxy) {
forwardedProto = it->value.str();
} else {
LOG_SECURE("wthttp is not behind a trusted reverse proxy, dropping " << it->name.str() << " header");
}
} else if(it->name.iequals("X-Forwarded-Port")) {
if (wtConfiguration.behindReverseProxy()) {
if (trustedProxy) {
forwardedPort = it->value.str();
} else {
LOG_SECURE("wthttp is not behind a trusted reverse proxy, dropping " << it->name.str() << " header");
}
} else if (it->name.iequals("X-Forwarded-Host")) {
if (trustedProxy) {
forwardedHost = it->value.str();
} else {
LOG_SECURE("wthttp is not behind a trusted reverse proxy, dropping " << it->name.str() << " header");
}
} else if (it->name.length() > 0) {
os << it->name << ": " << it->value << "\r\n";
......
os << "X-Forwarded-Port: " << forwardedPort << "\r\n";
else
os << "X-Forwarded-Port: " << request_.port << "\r\n";
if (!forwardedHost.empty())
os << "X-Forwarded-Host: " << forwardedHost << "\r\n";
// Forward SSL Certificate to session only for first request
if (fwCertificates_) {
auto sslInfo = request_.sslInfo();
src/http/WServer.C
if (impl_->serverConfiguration_->parentPort() != -1) {
configuration().setBehindReverseProxy(true);
configuration().setForwardedHeader("X-Forwarded-For");
configuration().setTrustedProxies({
Configuration::Network::fromString("127.0.0.1"),
Configuration::Network::fromString("::1")
});
dedicatedProcessEnabled_ = true;
}
src/isapi/IsapiRequest.C
return "http";
}
std::unique_ptr<WSslInfo> IsapiRequest::sslInfo(bool) const {
std::unique_ptr<WSslInfo> IsapiRequest::sslInfo(const Configuration &) const {
#ifdef WT_WITH_SSL
CERT_CONTEXT_EX cce;
memset(&cce, 0, sizeof(CERT_CONTEXT_EX));
src/isapi/IsapiRequest.h
virtual const char *urlScheme() const;
virtual std::unique_ptr<WSslInfo> sslInfo(bool behindReverseProxy) const;
virtual std::unique_ptr<WSslInfo> sslInfo(const Configuration &conf) const;
private:
LPEXTENSION_CONTROL_BLOCK ecb_;
src/web/Configuration.C
#include <sys/stat.h>
#include <fcntl.h>
#include <algorithm>
#include <array>
#include <vector>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <regex>
#include <Wt/AsioWrapper/system_error.hpp>
#include "3rdparty/rapidxml/rapidxml.hpp"
#include "3rdparty/rapidxml/rapidxml_print.hpp"
......
return result;
}
template<std::size_t N>
bool prefixMatches(const std::array<unsigned char, N> &networkBytes,
const std::array<unsigned char, N> &addressBytes,
const unsigned char prefixLength)
{
for (std::size_t i = 0; i < N; ++i) {
if ((i + 1) * 8 < prefixLength) {
if (networkBytes[i] != addressBytes[i]) {
return false;
}
} else {
const unsigned char shift = (i + 1) * 8 - prefixLength;
return (networkBytes[i] >> shift) == (addressBytes[i] >> shift);
}
}
return true;
}
static std::string FORWARD_SLASH = "/";
}
......
userAgent_(userAgent)
{ }
Configuration::Network Configuration::Network::fromString(const std::string &s)
{
const auto slashPos = s.find('/');
if (slashPos == std::string::npos) {
AsioWrapper::error_code ec;
const auto address = AsioWrapper::asio::ip::address::from_string(s, ec);
if (ec) {
throw std::invalid_argument("'" + s + "' is not a valid IP address");
}
const unsigned char prefixLength = address.is_v6() ? 128 : 32;
return Network { address, prefixLength };
} else {
AsioWrapper::error_code ec;
const auto address = AsioWrapper::asio::ip::address::from_string(s.substr(0, slashPos), ec);
if (ec) {
throw std::invalid_argument("'" + s + "' is not a valid IP address");
}
const unsigned int prefixLength = Utils::stoi(s.substr(slashPos + 1));
if (prefixLength < 0 ||
(address.is_v4() && prefixLength > 32) ||
(address.is_v6() && prefixLength > 128)) {
throw std::invalid_argument("Invalid prefix length " + s.substr(slashPos + 1) + " for IPv" +
std::string(address.is_v4() ? "4" : "6") + " address");
}
return Network { address, static_cast<unsigned char>(prefixLength) };
}
}
bool Configuration::Network::contains(const AsioWrapper::asio::ip::address &address) const
{
if (this->address.is_v6() && address.is_v6()) {
const auto networkBytes = this->address.to_v6().to_bytes();
const auto addressBytes = address.to_v6().to_bytes();
return prefixMatches(networkBytes, addressBytes, prefixLength);
} else if (this->address.is_v4() && address.is_v4()) {
const auto networkBytes = this->address.to_v4().to_bytes();
const auto addressBytes = address.to_v4().to_bytes();
return prefixMatches(networkBytes, addressBytes, prefixLength);
} else {
return false;
}
}
Configuration::Configuration(const std::string& applicationPath,
const std::string& appRoot,
const std::string& configurationFile,
......
properties_.clear();
xhtmlMimeType_ = false;
behindReverseProxy_ = false;
forwardedHeader_ = "X-Forwarded-For";
trustedProxies_ = {
Network::fromString("10.0.0.0/8"),
Network::fromString("172.16.0.0/12"),
Network::fromString("192.168.0.0/16"),
Network::fromString("fd00::/8"),
Network::fromString("127.0.0.1/32"),
Network::fromString("::1/128")
};
redirectMsg_ = "Load basic HTML";
serializedEvents_ = false;
webSockets_ = false;
......
return behindReverseProxy_;
}
std::string Configuration::forwardedHeader() const {
READ_LOCK;
return forwardedHeader_;
}
std::vector<Configuration::Network> Configuration::trustedProxies() const {
READ_LOCK;
return trustedProxies_;
}
bool Configuration::isTrustedProxy(const std::string &ipAddress) const {
READ_LOCK;
if (behindReverseProxy_) {
return false;
}
AsioWrapper::error_code ec;
const auto address = AsioWrapper::asio::ip::address::from_string(ipAddress, ec);
if (ec) {
return false;
}
return std::any_of(begin(trustedProxies_), end(trustedProxies_), [&address](const Network &network) {
return network.contains(address);
});
}
std::string Configuration::redirectMessage() const
{
READ_LOCK;
......
behindReverseProxy_ = enabled;
}
void Configuration::setForwardedHeader(const std::string &forwardedHeader)
{
forwardedHeader_ = forwardedHeader;
}
void Configuration::setTrustedProxies(const std::vector<Network> &trustedProxies)
{
trustedProxies_ = trustedProxies;
}
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_);
forwardedHeader_ = singleChildElementValue(app, "forwarded-header", forwardedHeader_);
const std::string trustedProxiesStr = singleChildElementValue(app, "trusted-proxies", "");
if (!trustedProxiesStr.empty()) {
trustedProxies_.clear();
std::vector<std::string> splitTrustedProxies;
boost::split(splitTrustedProxies, trustedProxiesStr, boost::is_any_of(","));
for (auto &trustedProxyStr : splitTrustedProxies) {
boost::trim(trustedProxyStr);
try {
trustedProxies_.push_back(Network::fromString(trustedProxyStr));
} catch (const std::invalid_argument &e) {
throw WServer::Exception("Invalid <trusted-proxies>: " + std::string(e.what()));
}
}
}
setBoolean(app, "strict-event-serialization", serializedEvents_);
setBoolean(app, "web-sockets", webSockets_);
src/web/Configuration.h
#include "WebSession.h"
#include "Wt/WRandom.h"
#ifndef WT_TARGET_JAVA
#include "Wt/AsioWrapper/asio.hpp"
#endif // WT_TARGET_JAVA
#ifndef WT_TARGET_JAVA
#include "EntryPoint.h"
#endif // WT_TARGET_JAVA
......
const std::string *property(const std::string& name) const;
#endif
#ifndef WT_TARGET_JAVA
using IpAddress = AsioWrapper::asio::ip::address;
#else
struct IpAddress {};
#endif
struct WT_API Network {
IpAddress address;
unsigned char prefixLength;
static Network fromString(const std::string &s);
bool contains(const IpAddress &address) const;
};
void setAppRoot(const std::string& path);
std::string appRoot() const;
bool behindReverseProxy() const;
std::string forwardedHeader() const;
std::vector<Network> trustedProxies() const;
bool isTrustedProxy(const std::string &ipAddress) const;
std::string redirectMessage() const;
bool serializedEvents() const;
bool webSockets() const;
......
void setUseSlashExceptionForInternalPaths(bool enabled);
void setNeedReadBodyBeforeResponse(bool needed);
void setBehindReverseProxy(bool enabled);
void setForwardedHeader(const std::string &forwardedHeader);
void setTrustedProxies(const std::vector<Network> &trustedProxies);
std::string generateSessionId();
bool registerSessionId(const std::string& oldId, const std::string& newId);
......
PropertyMap properties_;
bool xhtmlMimeType_;
bool behindReverseProxy_;
std::string forwardedHeader_;
std::vector<Network> trustedProxies_;
std::string redirectMsg_;
bool serializedEvents_;
bool webSockets_;
src/web/WebRequest.C
#include "WebRequest.h"
#include "WebUtils.h"
#include "Configuration.h"
#include <cstdlib>
......
return s ? std::string(s) : std::string();
}
bool isPrivateIP(const std::string &s) {
return boost::starts_with(s, "127.") ||
boost::starts_with(s, "10.") ||
boost::starts_with(s, "192.168.") ||
(s.size() >= 7 &&
boost::starts_with(s, "172.") &&
s[6] == '.' &&
((s[4] == '1' &&
s[5] >= '6' &&
s[5] <= '9') ||
(s[4] == '2' &&
s[5] >= '0' &&
s[5] <= '9') ||
(s[4] == '3' &&
s[5] >= '0' &&
s[5] <= '1')));
}
}
namespace Wt {
......
return urlParams_;
}
std::string WebRequest::clientAddress(const bool behindReverseProxy) const
std::string WebRequest::clientAddress(const Configuration &conf) 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(","));
std::string forwardedFor = str(headerValue("X-Forwarded-For"));
std::string remoteAddr = str(envValue("REMOTE_ADDR"));
if (conf.isTrustedProxy(remoteAddr)) {
std::string forwardedFor = str(headerValue(conf.forwardedHeader().c_str()));
boost::trim(forwardedFor);
std::vector<std::string> forwardedIps;
if (!forwardedFor.empty())
boost::split(forwardedIps, forwardedFor, boost::is_any_of(","));
Utils::insert(ips, forwardedIps);
for (unsigned i = 0; i < ips.size(); ++i) {
result = ips[i];
boost::trim(result);
if (!result.empty()
&& !isPrivateIP(result)) {
break;
boost::split(forwardedIps, forwardedFor, boost::is_any_of(","));
for (auto it = forwardedIps.rbegin();
it != forwardedIps.rend(); ++it) {
boost::trim(*it);
if (!it->empty() && !conf.isTrustedProxy(*it)) {
return *it;
}
}
}
if (result.empty())
result = str(envValue("REMOTE_ADDR"));
return result;
return remoteAddr;
}
}
src/web/WebRequest.h
namespace Wt {
class Configuration;
class EntryPoint;
class WSslInfo;
......
* Returns \c nullptr if the request does not have SSL client certificate
* information.
*/
virtual std::unique_ptr<WSslInfo> sslInfo(bool behindReverseProxy) const = 0;
virtual std::unique_ptr<WSslInfo> sslInfo(const Configuration & conf) const = 0;
virtual const std::vector<std::pair<std::string, std::string> >& urlParams() const;
std::string clientAddress(bool behindReverseProxy) const;
std::string clientAddress(const Configuration & conf) const;
protected:
const EntryPoint *entryPoint_;
src/web/WebSession.C
return;
}
std::string ca = handler.request()->clientAddress(
controller_->configuration().behindReverseProxy());
std::string ca = handler.request()->clientAddress(controller_->configuration());
if (ca != env_->clientAddress()) {
bool isInvalid = sessionIdCookie_.empty();
src/web/WebSocketMessage.C
return "http";
}
std::unique_ptr<Wt::WSslInfo> WebSocketMessage::sslInfo(bool behindReverseProxy) const
std::unique_ptr<Wt::WSslInfo> WebSocketMessage::sslInfo(const Configuration & conf) const
{
return webSocket()->sslInfo(behindReverseProxy);
return webSocket()->sslInfo(conf);
}
const char *WebSocketMessage::headerValue(const char *name) const
src/web/WebSocketMessage.h
virtual const char *urlScheme() const override;
virtual std::unique_ptr<Wt::WSslInfo> sslInfo(bool behindReverseProxy) const override;
virtual std::unique_ptr<Wt::WSslInfo> sslInfo(const Configuration & conf) const override;
virtual const char * headerValue(const char *name) const override;
test/http/HttpClientServerTest.C
#include <Wt/Http/ResponseContinuation.h>
#include <Wt/Http/Request.h>
#include <web/Configuration.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
......
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,
......
response.out() << "Hello";
}
void handleClientAddress(const Http::Request& request,
Http::Response &response)
{
response.setStatus(200);
response.out() << request.clientAddress();
}
void handleWithContinuation(const Http::Request& request,
Http::Response& response)
{
......
client.get("http://" + server.address() + "/test");
client.waitDone();
BOOST_REQUIRE(client.err() == Wt::AsioWrapper::asio::error::bad_descriptor);
BOOST_REQUIRE(client.err() == Wt::AsioWrapper::asio::error::bad_descriptor ||
client.err() == Wt::AsioWrapper::asio::error::operation_aborted);
}
server.resource().haveMoreData();
......
}
}
// Reserved example IP ranges:
// - 192.0.2.0/24
// - 198.51.100.0/24
// - 203.0.113.0/24
// - 2001:db8::/32
BOOST_AUTO_TEST_CASE( http_client_address_not_behind_reverse_proxy )
{
Server server;
server.resource().clientAddressTest();
if (server.start()) {
Client client;
std::vector<Http::Message::Header> headers {
{"X-Forwarded-For", "198.51.100.1"},
{"Client-IP", "203.0.113.1"}
};
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// Client-IP and X-Forwarded-For headers are ignored if not behind reverse proxy
BOOST_REQUIRE(client.message().body() == "127.0.0.1");
}
}
BOOST_AUTO_TEST_CASE( http_client_address_forwarded_for )
{
Server server;
server.resource().clientAddressTest();
server.configuration().setBehindReverseProxy(true);
if (server.start()) {
Client client;
std::vector<Http::Message::Header> headers {
{"X-Forwarded-For", "198.51.100.1"},
{"Client-IP", "203.0.113.1"}
};
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// Should get IP address from X-Forwarded-For
BOOST_REQUIRE(client.message().body() == "198.51.100.1");
}
}
BOOST_AUTO_TEST_CASE( http_client_address_client_ip )
{
Server server;
server.resource().clientAddressTest();
server.configuration().setBehindReverseProxy(true);
server.configuration().setForwardedHeader("Client-IP");
if (server.start()) {
Client client;
std::vector<Http::Message::Header> headers {
{"X-Forwarded-For", "198.51.100.1"},
{"Client-IP", "203.0.113.1"}
};
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// Should get IP address from X-Forwarded-For
BOOST_REQUIRE(client.message().body() == "203.0.113.1");
}
}
BOOST_AUTO_TEST_CASE( http_multiple_proxies )
{
Server server;
server.resource().clientAddressTest();
server.configuration().setBehindReverseProxy(true);
server.configuration().setTrustedProxies({
Configuration::Network::fromString("127.0.0.1"),
Configuration::Network::fromString("198.51.100.0/24")
});
if (server.start()) {
Client client;
std::vector<Http::Message::Header> headers {
{"X-Forwarded-For", "192.0.2.1, 203.0.113.1, 198.51.100.1"},
};
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// Should get IP address from X-Forwarded-For
BOOST_REQUIRE(client.message().body() == "203.0.113.1");
}
}
BOOST_AUTO_TEST_CASE( http_multiple_proxies2 )
{
Server server;
server.resource().clientAddressTest();
server.configuration().setBehindReverseProxy(true);
server.configuration().setTrustedProxies({
Configuration::Network::fromString("127.0.0.1"),
Configuration::Network::fromString("198.51.100.0/24"),
Configuration::Network::fromString("203.0.113.0/24")
});
if (server.start()) {
Client client;
std::vector<Http::Message::Header> headers {
{"X-Forwarded-For", "192.0.2.1, 203.0.113.1, 198.51.100.1"},
};
client.get("http://" + server.address() + "/test", headers);
client.waitDone();
BOOST_REQUIRE(!client.err());
BOOST_REQUIRE(client.message().status() == 200);
// Should get IP address from X-Forwarded-For
BOOST_REQUIRE(client.message().body() == "192.0.2.1");
}
}
#endif // WT_THREADED
test/private/EventDecodeTest.C
return std::vector<Wt::Http::Message::Header>{};
}
virtual std::unique_ptr<Wt::WSslInfo> sslInfo(bool) const override
virtual std::unique_ptr<Wt::WSslInfo> sslInfo(const Wt::Configuration &) const override
{
return nullptr;
}
wt_config.xml.in
-->
<behind-reverse-proxy>false</behind-reverse-proxy>
<!-- Which header to use to get the real client IP address when behind a reverse proxy.
This could be X-Forwarded-For, CF-Connecting-IP for Cloudflare, True-Client-IP, Fastly-Client-IP,...
Defaults to X-Forwarded-For
-->
<forwarded-header>X-Forwarded-For</forwarded-header>
<!-- Which proxy servers are trusted when behind-reverse-proxy is true.
You can either enter single IP addresses or IP ranges in CIDR notation,
separated by commas. Whitespace is ignored.
By default, localhost and local IP ranges are trusted.
-->
<!--<trusted-proxies>10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8,127.0.0.1,::1</trusted-proxies>-->
<!-- Whether inline CSS is allowed.
Some Wt widgets will insert CSS rules in the the inline
(2-2/3)