nghttpx: Implement PROXY protocol version 1
Use --accept-proxy-protocol to enable PROXY protocol handling
This commit is contained in:
parent
39287314d3
commit
ce53bd239e
|
@ -100,6 +100,7 @@ OPTIONS = [
|
|||
"tls-ticket-key-memcached-max-fail",
|
||||
"request-phase-file",
|
||||
"response-phase-file",
|
||||
"accept-proxy-protocol",
|
||||
"conf",
|
||||
]
|
||||
|
||||
|
|
|
@ -1915,6 +1915,7 @@ int main(int argc, char **argv) {
|
|||
90},
|
||||
{SHRPX_OPT_REQUEST_PHASE_FILE, required_argument, &flag, 91},
|
||||
{SHRPX_OPT_RESPONSE_PHASE_FILE, required_argument, &flag, 92},
|
||||
{SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, no_argument, &flag, 93},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -2316,6 +2317,10 @@ int main(int argc, char **argv) {
|
|||
// --response-phase-file
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_PHASE_FILE, optarg);
|
||||
break;
|
||||
case 93:
|
||||
// --accept-proxy-protocol
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, "yes");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -382,6 +382,16 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
|||
conn_.rlimit.startw();
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
|
||||
if (get_config()->accept_proxy_protocol) {
|
||||
read_ = write_ = &ClientHandler::read_clear;
|
||||
on_read_ = &ClientHandler::proxy_protocol_read;
|
||||
on_write_ = &ClientHandler::upstream_noop;
|
||||
} else {
|
||||
setup_upstream_io_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientHandler::setup_upstream_io_callback() {
|
||||
if (conn_.tls.ssl) {
|
||||
conn_.prepare_server_handshake();
|
||||
read_ = write_ = &ClientHandler::tls_handshake;
|
||||
|
@ -829,4 +839,185 @@ ev_io *ClientHandler::get_wev() { return &conn_.wev; }
|
|||
|
||||
Worker *ClientHandler::get_worker() const { return worker_; }
|
||||
|
||||
namespace {
|
||||
ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
|
||||
auto p = first;
|
||||
int32_t port = 0;
|
||||
|
||||
for (; p != last && util::isDigit(*p); ++p) {
|
||||
port *= 10;
|
||||
port += *p - '0';
|
||||
|
||||
if (port > 65535) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return p - first;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int ClientHandler::proxy_protocol_read() {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol: Started";
|
||||
}
|
||||
|
||||
auto first = rb_.pos;
|
||||
|
||||
// NULL character really destroys getaddrinfo function. We won't
|
||||
// expect it in PROXY protocol line, so find it here.
|
||||
auto chrs = std::array<char, 2>{{'\r', '\0'}};
|
||||
auto end = std::find_first_of(rb_.pos, rb_.pos + rb_.rleft(),
|
||||
std::begin(chrs), std::end(chrs));
|
||||
if (end + 2 > rb_.pos + rb_.rleft() || *end == '\0' || end[1] != '\n') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
constexpr const char HEADER[] = "PROXY ";
|
||||
|
||||
if (rb_.rleft() < str_size(HEADER)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!util::streq_l(HEADER, rb_.pos, str_size(HEADER))) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
rb_.drain(str_size(HEADER));
|
||||
|
||||
int family;
|
||||
|
||||
if (rb_.pos[0] == 'T') {
|
||||
if (rb_.rleft() < 5) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (util::streq_l("TCP4 ", rb_.pos, 5)) {
|
||||
family = AF_INET;
|
||||
} else if (util::streq_l("TCP6 ", rb_.pos, 5)) {
|
||||
family = AF_INET6;
|
||||
} else {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
rb_.drain(5);
|
||||
} else {
|
||||
if (rb_.rleft() < 7) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (!util::streq_l("UNKNOWN", rb_.pos, 7)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// source address
|
||||
auto token_end = std::find(rb_.pos, end, ' ');
|
||||
if (token_end == end) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
*token_end = '\0';
|
||||
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto src_addr = rb_.pos;
|
||||
auto src_addrlen = token_end - rb_.pos;
|
||||
|
||||
rb_.drain(token_end - rb_.pos + 1);
|
||||
|
||||
// destination address
|
||||
token_end = std::find(rb_.pos, end, ' ');
|
||||
if (token_end == end) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
*token_end = '\0';
|
||||
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Currently we don't use destination address
|
||||
|
||||
rb_.drain(token_end - rb_.pos + 1);
|
||||
|
||||
// source port
|
||||
auto n = parse_proxy_line_port(rb_.pos, end);
|
||||
if (n <= 0 || *(rb_.pos + n) != ' ') {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
rb_.pos[n] = '\0';
|
||||
auto src_port = rb_.pos;
|
||||
auto src_portlen = n;
|
||||
|
||||
rb_.drain(n + 1);
|
||||
|
||||
// destination port
|
||||
n = parse_proxy_line_port(rb_.pos, end);
|
||||
if (n <= 0 || rb_.pos + n != end) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Currently we don't use destination port
|
||||
|
||||
rb_.drain(end + 2 - rb_.pos);
|
||||
|
||||
ipaddr_.assign(src_addr, src_addr + src_addrlen);
|
||||
port_.assign(src_port, src_port + src_portlen);
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first)
|
||||
<< " bytes read";
|
||||
}
|
||||
|
||||
setup_upstream_io_callback();
|
||||
|
||||
// Run on_read to process data left in buffer since they are not
|
||||
// notified further
|
||||
if (on_read() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
int upstream_http1_connhd_read();
|
||||
int upstream_write();
|
||||
|
||||
int proxy_protocol_read();
|
||||
|
||||
// Performs I/O operation. Internally calls on_read()/on_write().
|
||||
int do_read();
|
||||
int do_write();
|
||||
|
@ -130,6 +132,8 @@ public:
|
|||
void signal_write();
|
||||
ev_io *get_wev();
|
||||
|
||||
void setup_upstream_io_callback();
|
||||
|
||||
private:
|
||||
Connection conn_;
|
||||
ev_timer reneg_shutdown_timer_;
|
||||
|
|
|
@ -625,6 +625,7 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) {
|
|||
|
||||
// generated by gennghttpxfun.py
|
||||
enum {
|
||||
SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL,
|
||||
SHRPX_OPTID_ACCESSLOG_FILE,
|
||||
SHRPX_OPTID_ACCESSLOG_FORMAT,
|
||||
SHRPX_OPTID_ACCESSLOG_SYSLOG,
|
||||
|
@ -1099,6 +1100,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (util::strieq_l("accept-proxy-protoco", name, 20)) {
|
||||
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
|
||||
return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
|
||||
|
@ -1968,6 +1974,10 @@ int parse_config(const char *opt, const char *optarg,
|
|||
LOG(WARN) << opt
|
||||
<< ": ignored because mruby support is disabled at build time.";
|
||||
#endif // !HAVE_MRUBY
|
||||
return 0;
|
||||
case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
|
||||
mod_config()->accept_proxy_protocol = util::strieq(optarg, "yes");
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
|
|
@ -185,6 +185,7 @@ constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL[] =
|
|||
"tls-ticket-key-memcached-max-fail";
|
||||
constexpr char SHRPX_OPT_REQUEST_PHASE_FILE[] = "request-phase-file";
|
||||
constexpr char SHRPX_OPT_RESPONSE_PHASE_FILE[] = "response-phase-file";
|
||||
constexpr char SHRPX_OPT_ACCEPT_PROXY_PROTOCOL[] = "accept-proxy-protocol";
|
||||
|
||||
union sockaddr_union {
|
||||
sockaddr_storage storage;
|
||||
|
@ -409,6 +410,7 @@ struct Config {
|
|||
bool no_ocsp;
|
||||
// true if --tls-ticket-key-cipher is used
|
||||
bool tls_ticket_key_cipher_given;
|
||||
bool accept_proxy_protocol;
|
||||
};
|
||||
|
||||
const Config *get_config();
|
||||
|
|
|
@ -636,9 +636,13 @@ void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
|
|||
}
|
||||
|
||||
bool numeric_host(const char *hostname) {
|
||||
return numeric_host(hostname, AF_UNSPEC);
|
||||
}
|
||||
|
||||
bool numeric_host(const char *hostname, int family) {
|
||||
struct addrinfo *res;
|
||||
struct addrinfo hints {};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_family = family;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
if (getaddrinfo(hostname, nullptr, &hints, &res)) {
|
||||
return false;
|
||||
|
|
|
@ -512,6 +512,8 @@ void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
|
|||
|
||||
bool numeric_host(const char *hostname);
|
||||
|
||||
bool numeric_host(const char *hostname, int family);
|
||||
|
||||
// Returns numeric address string of |addr|. If getnameinfo() is
|
||||
// failed, "unknown" is returned.
|
||||
std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
|
||||
|
|
Loading…
Reference in New Issue