nghttpx: SNI based backend server selection

This commit is contained in:
Tatsuhiro Tsujikawa 2017-04-16 23:47:10 +09:00
parent 5e00cf9620
commit c8a5f1e335
4 changed files with 43 additions and 23 deletions

View File

@ -1665,14 +1665,16 @@ Connections:
which only lacks trailing '/' (e.g., path "/foo/" which only lacks trailing '/' (e.g., path "/foo/"
matches request path "/foo"). If it does not end with matches request path "/foo"). If it does not end with
"/", it performs exact match against the request path. "/", it performs exact match against the request path.
If host is given, it performs exact match against the If host is given, it performs a match against the
request host. If host alone is given, "/" is appended request host. For a request received on the frontend
to it, so that it matches all request paths under the lister with "sni-fwd" parameter enabled, SNI host is
host (e.g., specifying "nghttp2.org" equals to used instead of a request host. If host alone is given,
"nghttp2.org/"). CONNECT method is treated specially. "/" is appended to it, so that it matches all request
It does not have path, and we don't allow empty path. paths under the host (e.g., specifying "nghttp2.org"
To workaround this, we assume that CONNECT method has equals to "nghttp2.org/"). CONNECT method is treated
"/" as path. specially. It does not have path, and we don't allow
empty path. To workaround this, we assume that CONNECT
method has "/" as path.
Patterns with host take precedence over patterns with Patterns with host take precedence over patterns with
just path. Then, longer patterns take precedence over just path. Then, longer patterns take precedence over
@ -1811,6 +1813,11 @@ Connections:
Optionally, TLS can be disabled by specifying "no-tls" Optionally, TLS can be disabled by specifying "no-tls"
parameter. TLS is enabled by default. parameter. TLS is enabled by default.
If "sni-fwd" parameter is used, when performing a match
to select a backend server, SNI host name received from
the client is used instead of the request host. See
--backend option about the pattern match.
To make this frontend as API endpoint, specify "api" To make this frontend as API endpoint, specify "api"
parameter. This is disabled by default. It is parameter. This is disabled by default. It is
important to limit the access to the API frontend. important to limit the access to the API frontend.

View File

@ -979,29 +979,30 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
auto &balloc = downstream->get_block_allocator(); auto &balloc = downstream->get_block_allocator();
// Fast path. If we have one group, it must be catch-all group. // Fast path. If we have one group, it must be catch-all group.
// proxy mode falls in this case.
if (groups.size() == 1) { if (groups.size() == 1) {
group_idx = 0; group_idx = 0;
} else if (req.method == HTTP_CONNECT) {
// CONNECT method does not have path. But we requires path in
// host-path mapping. As workaround, we assume that path is "/".
group_idx = match_downstream_addr_group(routerconf, req.authority,
StringRef::from_lit("/"), groups,
catch_all, balloc);
} else { } else {
if (!req.authority.empty()) { StringRef authority;
group_idx = match_downstream_addr_group( if (faddr_->sni_fwd) {
routerconf, req.authority, req.path, groups, catch_all, balloc); authority = sni_;
} else if (!req.authority.empty()) {
authority = req.authority;
} else { } else {
auto h = req.fs.header(http2::HD_HOST); auto h = req.fs.header(http2::HD_HOST);
if (h) { if (h) {
group_idx = match_downstream_addr_group(routerconf, h->value, req.path, authority = h->value;
groups, catch_all, balloc);
} else {
group_idx = match_downstream_addr_group(
routerconf, StringRef{}, req.path, groups, catch_all, balloc);
} }
} }
StringRef path;
// CONNECT method does not have path. But we requires path in
// host-path mapping. As workaround, we assume that path is "/".
if (req.method != HTTP_CONNECT) {
path = req.path;
}
group_idx = match_downstream_addr_group(routerconf, authority, path, groups,
catch_all, balloc);
} }
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {

View File

@ -693,6 +693,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
struct UpstreamParams { struct UpstreamParams {
int alt_mode; int alt_mode;
bool tls; bool tls;
bool sni_fwd;
bool proxyproto; bool proxyproto;
}; };
@ -708,6 +709,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
if (util::strieq_l("tls", param)) { if (util::strieq_l("tls", param)) {
out.tls = true; out.tls = true;
} else if (util::strieq_l("sni-fwd", param)) {
out.sni_fwd = true;
} else if (util::strieq_l("no-tls", param)) { } else if (util::strieq_l("no-tls", param)) {
out.tls = false; out.tls = false;
} else if (util::strieq_l("api", param)) { } else if (util::strieq_l("api", param)) {
@ -2296,9 +2299,15 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
if (params.sni_fwd && !params.tls) {
LOG(ERROR) << "frontend: sni_fwd requires tls";
return -1;
}
UpstreamAddr addr{}; UpstreamAddr addr{};
addr.fd = -1; addr.fd = -1;
addr.tls = params.tls; addr.tls = params.tls;
addr.sni_fwd = params.sni_fwd;
addr.alt_mode = params.alt_mode; addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto; addr.accept_proxy_protocol = params.proxyproto;

View File

@ -404,6 +404,9 @@ struct UpstreamAddr {
bool host_unix; bool host_unix;
// true if TLS is enabled. // true if TLS is enabled.
bool tls; bool tls;
// true if SNI host should be used as a host when selecting backend
// server.
bool sni_fwd;
// true if client is supposed to send PROXY protocol v1 header. // true if client is supposed to send PROXY protocol v1 header.
bool accept_proxy_protocol; bool accept_proxy_protocol;
int fd; int fd;