From c8a5f1e335aa0cda7fc7036ffdc8ef17318c72cd Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 16 Apr 2017 23:47:10 +0900 Subject: [PATCH] nghttpx: SNI based backend server selection --- src/shrpx.cc | 23 +++++++++++++++-------- src/shrpx_client_handler.cc | 31 ++++++++++++++++--------------- src/shrpx_config.cc | 9 +++++++++ src/shrpx_config.h | 3 +++ 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 7601f1f3..13f0238c 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1665,14 +1665,16 @@ Connections: which only lacks trailing '/' (e.g., path "/foo/" matches request path "/foo"). If it does not end with "/", it performs exact match against the request path. - If host is given, it performs exact match against the - request host. If host alone is given, "/" is appended - to it, so that it matches all request paths under the - host (e.g., specifying "nghttp2.org" equals to - "nghttp2.org/"). CONNECT method is treated specially. - It does not have path, and we don't allow empty path. - To workaround this, we assume that CONNECT method has - "/" as path. + If host is given, it performs a match against the + request host. For a request received on the frontend + lister with "sni-fwd" parameter enabled, SNI host is + used instead of a request host. If host alone is given, + "/" is appended to it, so that it matches all request + paths under the host (e.g., specifying "nghttp2.org" + equals to "nghttp2.org/"). CONNECT method is treated + 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 just path. Then, longer patterns take precedence over @@ -1811,6 +1813,11 @@ Connections: Optionally, TLS can be disabled by specifying "no-tls" 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" parameter. This is disabled by default. It is important to limit the access to the API frontend. diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index b0063b33..2353128b 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -979,29 +979,30 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) { auto &balloc = downstream->get_block_allocator(); // Fast path. If we have one group, it must be catch-all group. - // proxy mode falls in this case. if (groups.size() == 1) { 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 { - if (!req.authority.empty()) { - group_idx = match_downstream_addr_group( - routerconf, req.authority, req.path, groups, catch_all, balloc); + StringRef authority; + if (faddr_->sni_fwd) { + authority = sni_; + } else if (!req.authority.empty()) { + authority = req.authority; } else { auto h = req.fs.header(http2::HD_HOST); if (h) { - group_idx = match_downstream_addr_group(routerconf, h->value, req.path, - groups, catch_all, balloc); - } else { - group_idx = match_downstream_addr_group( - routerconf, StringRef{}, req.path, groups, catch_all, balloc); + authority = h->value; } } + + 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)) { diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 34f6cc06..8f1b94e5 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -693,6 +693,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out, struct UpstreamParams { int alt_mode; bool tls; + bool sni_fwd; bool proxyproto; }; @@ -708,6 +709,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) { if (util::strieq_l("tls", param)) { out.tls = true; + } else if (util::strieq_l("sni-fwd", param)) { + out.sni_fwd = true; } else if (util::strieq_l("no-tls", param)) { out.tls = false; } else if (util::strieq_l("api", param)) { @@ -2296,9 +2299,15 @@ int parse_config(Config *config, int optid, const StringRef &opt, return -1; } + if (params.sni_fwd && !params.tls) { + LOG(ERROR) << "frontend: sni_fwd requires tls"; + return -1; + } + UpstreamAddr addr{}; addr.fd = -1; addr.tls = params.tls; + addr.sni_fwd = params.sni_fwd; addr.alt_mode = params.alt_mode; addr.accept_proxy_protocol = params.proxyproto; diff --git a/src/shrpx_config.h b/src/shrpx_config.h index e0c81cd2..9db9c1fe 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -404,6 +404,9 @@ struct UpstreamAddr { bool host_unix; // true if TLS is enabled. 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. bool accept_proxy_protocol; int fd;