nghttpx: Add PROXY-protocol v2 support

This commit is contained in:
Tatsuhiro Tsujikawa 2020-04-17 23:35:05 +09:00
parent 3b17a659f6
commit 49cd8e6e73
3 changed files with 181 additions and 1 deletions

View File

@ -1914,7 +1914,7 @@ Connections:
default. Any requests which come through this address default. Any requests which come through this address
are replied with 200 HTTP status, without no body. are replied with 200 HTTP status, without no body.
To accept PROXY protocol version 1 on frontend To accept PROXY protocol version 1 and 2 on frontend
connection, specify "proxyproto" parameter. This is connection, specify "proxyproto" parameter. This is
disabled by default. disabled by default.

View File

@ -1149,6 +1149,16 @@ int ClientHandler::on_proxy_protocol_finish() {
return 0; return 0;
} }
namespace {
// PROXY-protocol v2 header signature
constexpr uint8_t PROXY_PROTO_V2_SIG[] =
"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
// PROXY-protocol v2 header length
constexpr size_t PROXY_PROTO_V2_HDLEN =
str_size(PROXY_PROTO_V2_SIG) + /* ver_cmd(1) + fam(1) + len(2) = */ 4;
} // namespace
// http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
int ClientHandler::proxy_protocol_read() { int ClientHandler::proxy_protocol_read() {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -1157,6 +1167,14 @@ int ClientHandler::proxy_protocol_read() {
auto first = rb_.pos(); auto first = rb_.pos();
if (rb_.rleft() >= PROXY_PROTO_V2_HDLEN &&
(*(first + str_size(PROXY_PROTO_V2_SIG)) & 0xf0) == 0x20) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol: Detected v2 header signature";
}
return proxy_protocol_v2_read();
}
// NULL character really destroys functions which expects NULL // NULL character really destroys functions which expects NULL
// terminated string. We won't expect it in PROXY protocol line, so // terminated string. We won't expect it in PROXY protocol line, so
// find it here. // find it here.
@ -1338,6 +1356,167 @@ int ClientHandler::proxy_protocol_read() {
return on_proxy_protocol_finish(); return on_proxy_protocol_finish();
} }
int ClientHandler::proxy_protocol_v2_read() {
// Assume that first str_size(PROXY_PROTO_V2_SIG) octets match v2
// protocol signature and followed by the bytes which indicates v2.
assert(rb_.rleft() >= PROXY_PROTO_V2_HDLEN);
auto p = rb_.pos() + str_size(PROXY_PROTO_V2_SIG);
assert(((*p) & 0xf0) == 0x20);
enum { LOCAL, PROXY } cmd;
auto cmd_bits = (*p++) & 0xf;
switch (cmd_bits) {
case 0x0:
cmd = LOCAL;
break;
case 0x01:
cmd = PROXY;
break;
default:
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Unknown command " << log::hex
<< cmd_bits;
}
return -1;
}
auto fam = *p++;
uint16_t len;
memcpy(&len, p, sizeof(len));
len = ntohs(len);
p += sizeof(len);
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Detected family=" << log::hex << fam
<< ", len=" << log::dec << len;
}
if (rb_.last() - p < len) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this)
<< "PROXY-protocol-v2: Prematurely truncated header block; require "
<< len << " bytes, " << rb_.last() - p << " bytes left";
}
return -1;
}
int family;
std::array<char, std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)> src_addr,
dst_addr;
size_t addrlen;
switch (fam) {
case 0x11:
case 0x12:
if (len < 12) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Too short AF_INET addresses";
}
return -1;
}
family = AF_INET;
addrlen = 4;
break;
case 0x21:
case 0x22:
if (len < 36) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Too short AF_INET6 addresses";
}
return -1;
}
family = AF_INET6;
addrlen = 16;
break;
case 0x31:
case 0x32:
if (len < 216) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Too short AF_UNIX addresses";
}
return -1;
}
// fall through
case 0x00: {
// UNSPEC and UNIX are just ignored.
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Ignore combination of address "
"family and protocol "
<< log::hex << fam;
}
rb_.drain(PROXY_PROTO_V2_HDLEN + len);
return on_proxy_protocol_finish();
}
default:
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Unknown combination of address "
"family and protocol "
<< log::hex << fam;
}
return -1;
}
if (cmd != PROXY) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Ignore non-PROXY command";
}
rb_.drain(PROXY_PROTO_V2_HDLEN + len);
return on_proxy_protocol_finish();
}
if (inet_ntop(family, p, src_addr.data(), src_addr.size()) == nullptr) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Unable to parse source address";
}
return -1;
}
p += addrlen;
if (inet_ntop(family, p, dst_addr.data(), dst_addr.size()) == nullptr) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this)
<< "PROXY-protocol-v2: Unable to parse destination address";
}
return -1;
}
p += addrlen;
uint16_t src_port;
memcpy(&src_port, p, sizeof(src_port));
src_port = ntohs(src_port);
// We don't use destination port.
p += 4;
ipaddr_ = make_string_ref(balloc_, StringRef{src_addr.data()});
port_ = util::make_string_ref_uint(balloc_, src_port);
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v2: Finished reading proxy addresses, "
<< p - rb_.pos() << " bytes read, "
<< PROXY_PROTO_V2_HDLEN + len - (p - rb_.pos())
<< " bytes left";
}
auto config = get_config();
auto &fwdconf = config->http.forwarded;
if ((fwdconf.params & FORWARDED_FOR) &&
fwdconf.for_node_type == ForwardedNode::IP) {
init_forwarded_for(family, ipaddr_);
}
rb_.drain(PROXY_PROTO_V2_HDLEN + len);
return on_proxy_protocol_finish();
}
StringRef ClientHandler::get_forwarded_by() const { StringRef ClientHandler::get_forwarded_by() const {
auto &fwdconf = get_config()->http.forwarded; auto &fwdconf = get_config()->http.forwarded;

View File

@ -77,6 +77,7 @@ public:
int upstream_write(); int upstream_write();
int proxy_protocol_read(); int proxy_protocol_read();
int proxy_protocol_v2_read();
int on_proxy_protocol_finish(); int on_proxy_protocol_finish();
// Performs I/O operation. Internally calls on_read()/on_write(). // Performs I/O operation. Internally calls on_read()/on_write().