From eec0b04a33f2f008658945b6cb216ce05641dd1b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 23 Mar 2016 23:56:09 +0900 Subject: [PATCH] nghttpx: Enable/disable TLS per frontend address This change allows user to disable TLS per frontend address using no-tls keyword in --frontend option. We removed --frontend-no-tls in favor of this new feature. --- src/shrpx.cc | 22 ++++++++----- src/shrpx_config.cc | 62 ++++++++++++++++++++++++++++++++----- src/shrpx_config.h | 3 +- src/shrpx_ssl.cc | 18 ++++++++--- src/shrpx_ssl.h | 3 ++ src/shrpx_worker_process.cc | 8 ++--- 6 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index c30746d2..c47e0e18 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -432,7 +432,8 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr, }); if (found != std::end(iaddrs)) { - LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host; + LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host + << (faddr.tls ? ", tls" : ""); (*found).used = true; faddr.fd = (*found).fd; faddr.hostport = "localhost"; @@ -496,7 +497,8 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr, return -1; } - LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host; + LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host + << (faddr.tls ? ", tls" : ""); faddr.fd = fd; faddr.hostport = "localhost"; @@ -652,7 +654,8 @@ int create_tcp_server_socket(UpstreamAddr &faddr, faddr.fd = fd; faddr.hostport = util::make_http_hostport(StringRef{host.data()}, faddr.port); - LOG(NOTICE) << "Listening on " << faddr.hostport; + LOG(NOTICE) << "Listening on " << faddr.hostport + << (faddr.tls ? ", tls" : ""); return 0; } @@ -1274,13 +1277,17 @@ Connections: Default: )" << DEFAULT_DOWNSTREAM_HOST << "," << DEFAULT_DOWNSTREAM_PORT << R"( - -f, --frontend=(,|unix:) + -f, --frontend=(,|unix:)[;no-tls] Set frontend host and port. If is '*', it assumes all addresses including both IPv4 and IPv6. UNIX domain socket can be specified by prefixing path name with "unix:" (e.g., unix:/var/run/nghttpx.sock). This option can be used multiple times to listen to multiple addresses. + + Optionally, TLS can be disabled by specifying "no-tls" + keyword. TLS is enabled by default. + Default: *,3000 --backlog= Set listen backlog size. @@ -1652,8 +1659,6 @@ HTTP/2 and SPDY: 2**-1. For SPDY, the size is 2**. Default: )" << get_config()->http2.upstream.connection_window_bits << R"( - --frontend-no-tls - Disable SSL/TLS on frontend connections. --backend-http2-window-bits= Sets the initial window size of HTTP/2 backend connection to 2**-1. @@ -2040,6 +2045,7 @@ void process_options( UpstreamAddr addr{}; addr.host = "*"; addr.port = 3000; + addr.tls = true; addr.family = AF_INET; listenerconf.addrs.push_back(addr); addr.family = AF_INET6; @@ -2050,14 +2056,14 @@ void process_options( upstreamconf.worker_connections = std::numeric_limits::max(); } - if (!upstreamconf.no_tls && + if (ssl::upstream_tls_enabled() && (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) { print_usage(std::cerr); LOG(FATAL) << "Too few arguments"; exit(EXIT_FAILURE); } - if (!upstreamconf.no_tls && !tlsconf.ocsp.disabled) { + if (ssl::upstream_tls_enabled() && !tlsconf.ocsp.disabled) { struct stat buf; if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) { tlsconf.ocsp.disabled = true; diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 51573b23..b5363f2f 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -568,6 +568,40 @@ int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) { } } // namespace +struct UpstreamParams { + bool tls; +}; + +namespace { +// Parses upstream configuration parameter |src_params|, and stores +// parsed results into |out|. This function returns 0 if it succeeds, +// or -1. +int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) { + auto last = std::end(src_params); + for (auto first = std::begin(src_params); first != last;) { + auto end = std::find(first, last, ';'); + auto param = StringRef{first, end}; + + if (util::strieq_l("tls", param)) { + out.tls = true; + } else if (util::strieq_l("no-tls", param)) { + out.tls = false; + } else if (!param.empty()) { + LOG(ERROR) << "frontend: " << param << ": unknown keyword"; + return -1; + } + + if (end == last) { + break; + } + + first = end + 1; + } + + return 0; +} +} // namespace + struct DownstreamParams { shrpx_proto proto; bool tls; @@ -1683,7 +1717,7 @@ int parse_config(const char *opt, const char *optarg, addr.host = ImmutableString(path, addr_end); addr.host_unix = true; } else { - if (split_host_port(host, sizeof(host), &port, &src[0], + if (split_host_port(host, sizeof(host), &port, src.c_str(), addr_end - std::begin(src)) == -1) { return -1; } @@ -1707,12 +1741,24 @@ int parse_config(const char *opt, const char *optarg, case SHRPX_OPTID_FRONTEND: { auto &listenerconf = mod_config()->conn.listener; + auto src = StringRef{optarg}; + auto addr_end = std::find(std::begin(src), std::end(src), ';'); + auto src_params = StringRef{addr_end, std::end(src)}; + + UpstreamParams params{}; + params.tls = true; + + if (parse_upstream_params(params, src_params) != 0) { + return -1; + } + UpstreamAddr addr{}; addr.fd = -1; + addr.tls = params.tls; - if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { - auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); - addr.host = ImmutableString(path); + if (util::istarts_with_l(src, SHRPX_UNIX_PATH_PREFIX)) { + auto path = std::begin(src) + str_size(SHRPX_UNIX_PATH_PREFIX); + addr.host = ImmutableString{path, addr_end}; addr.host_unix = true; listenerconf.addrs.push_back(std::move(addr)); @@ -1720,8 +1766,8 @@ int parse_config(const char *opt, const char *optarg, return 0; } - if (split_host_port(host, sizeof(host), &port, optarg, strlen(optarg)) == - -1) { + if (split_host_port(host, sizeof(host), &port, src.c_str(), + addr_end - std::begin(src)) == -1) { return -1; } @@ -1920,8 +1966,8 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_FRONTEND_NO_TLS: - mod_config()->conn.upstream.no_tls = util::strieq(optarg, "yes"); - + LOG(WARN) << opt << ": deprecated. Use no-tls keyword in " + << SHRPX_OPT_FRONTEND; return 0; case SHRPX_OPTID_BACKEND_NO_TLS: LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by " diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 1f3bcc58..d7984b6d 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -277,6 +277,8 @@ struct UpstreamAddr { int family; // true if |host| contains UNIX domain socket path. bool host_unix; + // true if TLS is enabled. + bool tls; int fd; }; @@ -558,7 +560,6 @@ struct ConnectionConfig { RateLimitConfig write; } ratelimit; size_t worker_connections; - bool no_tls; bool accept_proxy_protocol; } upstream; diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 0e7c4c3f..b6346d64 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -800,8 +800,11 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, } } SSL *ssl = nullptr; - auto ssl_ctx = worker->get_sv_ssl_ctx(); - if (ssl_ctx) { + if (faddr->tls) { + auto ssl_ctx = worker->get_sv_ssl_ctx(); + + assert(ssl_ctx); + ssl = create_ssl(ssl_ctx); if (!ssl) { return nullptr; @@ -1245,6 +1248,12 @@ bool in_proto_list(const std::vector &protos, return false; } +bool upstream_tls_enabled() { + const auto &faddrs = get_config()->conn.listener.addrs; + return std::any_of(std::begin(faddrs), std::end(faddrs), + [](const UpstreamAddr &faddr) { return faddr.tls; }); +} + SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, CertLookupTree *cert_tree #ifdef HAVE_NEVERBLEED @@ -1252,7 +1261,7 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, neverbleed_t *nb #endif // HAVE_NEVERBLEED ) { - if (get_config()->conn.upstream.no_tls) { + if (!upstream_tls_enabled()) { return nullptr; } @@ -1346,8 +1355,7 @@ void setup_downstream_http1_alpn(SSL *ssl) { } CertLookupTree *create_cert_lookup_tree() { - if (get_config()->conn.upstream.no_tls || - get_config()->tls.subcerts.empty()) { + if (!upstream_tls_enabled() || get_config()->tls.subcerts.empty()) { return nullptr; } return new ssl::CertLookupTree(); diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h index 46407324..748c034c 100644 --- a/src/shrpx_ssl.h +++ b/src/shrpx_ssl.h @@ -213,6 +213,9 @@ CertLookupTree *create_cert_lookup_tree(); SSL *create_ssl(SSL_CTX *ssl_ctx); +// Returns true if SSL/TLS is enabled on upstream +bool upstream_tls_enabled(); + // Returns true if SSL/TLS is enabled on downstream bool downstream_tls_enabled(); diff --git a/src/shrpx_worker_process.cc b/src/shrpx_worker_process.cc index 8d09b5d2..83611aea 100644 --- a/src/shrpx_worker_process.cc +++ b/src/shrpx_worker_process.cc @@ -393,10 +393,8 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { conn_handler.add_acceptor(make_unique(&addr, &conn_handler)); } - auto &upstreamconf = get_config()->conn.upstream; - #ifdef HAVE_NEVERBLEED - if (!upstreamconf.no_tls || ssl::downstream_tls_enabled()) { + if (ssl::upstream_tls_enabled() || ssl::downstream_tls_enabled()) { std::array errbuf; auto nb = make_unique(); if (neverbleed_init(nb.get(), errbuf.data()) != 0) { @@ -423,7 +421,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { MemchunkPool mcpool; ev_timer renew_ticket_key_timer; - if (!upstreamconf.no_tls) { + if (ssl::upstream_tls_enabled()) { auto &ticketconf = get_config()->tls.ticket; auto &memcachedconf = ticketconf.memcached; @@ -522,7 +520,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { ipcev.data = &conn_handler; ev_io_start(loop, &ipcev); - if (!upstreamconf.no_tls && !get_config()->tls.ocsp.disabled) { + if (ssl::upstream_tls_enabled() && !get_config()->tls.ocsp.disabled) { conn_handler.proceed_next_cert_ocsp(); }