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.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-03-23 23:56:09 +09:00
parent 58b06f32a2
commit eec0b04a33
6 changed files with 89 additions and 27 deletions

View File

@ -432,7 +432,8 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr,
}); });
if (found != std::end(iaddrs)) { 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; (*found).used = true;
faddr.fd = (*found).fd; faddr.fd = (*found).fd;
faddr.hostport = "localhost"; faddr.hostport = "localhost";
@ -496,7 +497,8 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr,
return -1; 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.fd = fd;
faddr.hostport = "localhost"; faddr.hostport = "localhost";
@ -652,7 +654,8 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
faddr.fd = fd; faddr.fd = fd;
faddr.hostport = util::make_http_hostport(StringRef{host.data()}, faddr.port); 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; return 0;
} }
@ -1274,13 +1277,17 @@ Connections:
Default: )" << DEFAULT_DOWNSTREAM_HOST << "," Default: )" << DEFAULT_DOWNSTREAM_HOST << ","
<< DEFAULT_DOWNSTREAM_PORT << R"( << DEFAULT_DOWNSTREAM_PORT << R"(
-f, --frontend=(<HOST>,<PORT>|unix:<PATH>) -f, --frontend=(<HOST>,<PORT>|unix:<PATH>)[;no-tls]
Set frontend host and port. If <HOST> is '*', it Set frontend host and port. If <HOST> is '*', it
assumes all addresses including both IPv4 and IPv6. assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock). name with "unix:" (e.g., unix:/var/run/nghttpx.sock).
This option can be used multiple times to listen to This option can be used multiple times to listen to
multiple addresses. multiple addresses.
Optionally, TLS can be disabled by specifying "no-tls"
keyword. TLS is enabled by default.
Default: *,3000 Default: *,3000
--backlog=<N> --backlog=<N>
Set listen backlog size. Set listen backlog size.
@ -1652,8 +1659,6 @@ HTTP/2 and SPDY:
2**<N>-1. For SPDY, the size is 2**<N>. 2**<N>-1. For SPDY, the size is 2**<N>.
Default: )" << get_config()->http2.upstream.connection_window_bits Default: )" << get_config()->http2.upstream.connection_window_bits
<< R"( << R"(
--frontend-no-tls
Disable SSL/TLS on frontend connections.
--backend-http2-window-bits=<N> --backend-http2-window-bits=<N>
Sets the initial window size of HTTP/2 backend Sets the initial window size of HTTP/2 backend
connection to 2**<N>-1. connection to 2**<N>-1.
@ -2040,6 +2045,7 @@ void process_options(
UpstreamAddr addr{}; UpstreamAddr addr{};
addr.host = "*"; addr.host = "*";
addr.port = 3000; addr.port = 3000;
addr.tls = true;
addr.family = AF_INET; addr.family = AF_INET;
listenerconf.addrs.push_back(addr); listenerconf.addrs.push_back(addr);
addr.family = AF_INET6; addr.family = AF_INET6;
@ -2050,14 +2056,14 @@ void process_options(
upstreamconf.worker_connections = std::numeric_limits<size_t>::max(); upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
} }
if (!upstreamconf.no_tls && if (ssl::upstream_tls_enabled() &&
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) { (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
print_usage(std::cerr); print_usage(std::cerr);
LOG(FATAL) << "Too few arguments"; LOG(FATAL) << "Too few arguments";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!upstreamconf.no_tls && !tlsconf.ocsp.disabled) { if (ssl::upstream_tls_enabled() && !tlsconf.ocsp.disabled) {
struct stat buf; struct stat buf;
if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) { if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
tlsconf.ocsp.disabled = true; tlsconf.ocsp.disabled = true;

View File

@ -568,6 +568,40 @@ int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) {
} }
} // namespace } // 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 { struct DownstreamParams {
shrpx_proto proto; shrpx_proto proto;
bool tls; bool tls;
@ -1683,7 +1717,7 @@ int parse_config(const char *opt, const char *optarg,
addr.host = ImmutableString(path, addr_end); addr.host = ImmutableString(path, addr_end);
addr.host_unix = true; addr.host_unix = true;
} else { } 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) { addr_end - std::begin(src)) == -1) {
return -1; return -1;
} }
@ -1707,12 +1741,24 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_FRONTEND: { case SHRPX_OPTID_FRONTEND: {
auto &listenerconf = mod_config()->conn.listener; 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{}; UpstreamAddr addr{};
addr.fd = -1; addr.fd = -1;
addr.tls = params.tls;
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (util::istarts_with_l(src, SHRPX_UNIX_PATH_PREFIX)) {
auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); auto path = std::begin(src) + str_size(SHRPX_UNIX_PATH_PREFIX);
addr.host = ImmutableString(path); addr.host = ImmutableString{path, addr_end};
addr.host_unix = true; addr.host_unix = true;
listenerconf.addrs.push_back(std::move(addr)); listenerconf.addrs.push_back(std::move(addr));
@ -1720,8 +1766,8 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
} }
if (split_host_port(host, sizeof(host), &port, optarg, strlen(optarg)) == if (split_host_port(host, sizeof(host), &port, src.c_str(),
-1) { addr_end - std::begin(src)) == -1) {
return -1; return -1;
} }
@ -1920,8 +1966,8 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
} }
case SHRPX_OPTID_FRONTEND_NO_TLS: 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; return 0;
case SHRPX_OPTID_BACKEND_NO_TLS: case SHRPX_OPTID_BACKEND_NO_TLS:
LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by " LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by "

View File

@ -277,6 +277,8 @@ struct UpstreamAddr {
int family; int family;
// true if |host| contains UNIX domain socket path. // true if |host| contains UNIX domain socket path.
bool host_unix; bool host_unix;
// true if TLS is enabled.
bool tls;
int fd; int fd;
}; };
@ -558,7 +560,6 @@ struct ConnectionConfig {
RateLimitConfig write; RateLimitConfig write;
} ratelimit; } ratelimit;
size_t worker_connections; size_t worker_connections;
bool no_tls;
bool accept_proxy_protocol; bool accept_proxy_protocol;
} upstream; } upstream;

View File

@ -800,8 +800,11 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
} }
} }
SSL *ssl = nullptr; SSL *ssl = nullptr;
auto ssl_ctx = worker->get_sv_ssl_ctx(); if (faddr->tls) {
if (ssl_ctx) { auto ssl_ctx = worker->get_sv_ssl_ctx();
assert(ssl_ctx);
ssl = create_ssl(ssl_ctx); ssl = create_ssl(ssl_ctx);
if (!ssl) { if (!ssl) {
return nullptr; return nullptr;
@ -1245,6 +1248,12 @@ bool in_proto_list(const std::vector<std::string> &protos,
return false; 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<SSL_CTX *> &all_ssl_ctx, SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
CertLookupTree *cert_tree CertLookupTree *cert_tree
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
@ -1252,7 +1261,7 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
neverbleed_t *nb neverbleed_t *nb
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
) { ) {
if (get_config()->conn.upstream.no_tls) { if (!upstream_tls_enabled()) {
return nullptr; return nullptr;
} }
@ -1346,8 +1355,7 @@ void setup_downstream_http1_alpn(SSL *ssl) {
} }
CertLookupTree *create_cert_lookup_tree() { CertLookupTree *create_cert_lookup_tree() {
if (get_config()->conn.upstream.no_tls || if (!upstream_tls_enabled() || get_config()->tls.subcerts.empty()) {
get_config()->tls.subcerts.empty()) {
return nullptr; return nullptr;
} }
return new ssl::CertLookupTree(); return new ssl::CertLookupTree();

View File

@ -213,6 +213,9 @@ CertLookupTree *create_cert_lookup_tree();
SSL *create_ssl(SSL_CTX *ssl_ctx); 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 // Returns true if SSL/TLS is enabled on downstream
bool downstream_tls_enabled(); bool downstream_tls_enabled();

View File

@ -393,10 +393,8 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
conn_handler.add_acceptor(make_unique<AcceptHandler>(&addr, &conn_handler)); conn_handler.add_acceptor(make_unique<AcceptHandler>(&addr, &conn_handler));
} }
auto &upstreamconf = get_config()->conn.upstream;
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
if (!upstreamconf.no_tls || ssl::downstream_tls_enabled()) { if (ssl::upstream_tls_enabled() || ssl::downstream_tls_enabled()) {
std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf; std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf;
auto nb = make_unique<neverbleed_t>(); auto nb = make_unique<neverbleed_t>();
if (neverbleed_init(nb.get(), errbuf.data()) != 0) { if (neverbleed_init(nb.get(), errbuf.data()) != 0) {
@ -423,7 +421,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
MemchunkPool mcpool; MemchunkPool mcpool;
ev_timer renew_ticket_key_timer; ev_timer renew_ticket_key_timer;
if (!upstreamconf.no_tls) { if (ssl::upstream_tls_enabled()) {
auto &ticketconf = get_config()->tls.ticket; auto &ticketconf = get_config()->tls.ticket;
auto &memcachedconf = ticketconf.memcached; auto &memcachedconf = ticketconf.memcached;
@ -522,7 +520,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
ipcev.data = &conn_handler; ipcev.data = &conn_handler;
ev_io_start(loop, &ipcev); 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(); conn_handler.proceed_next_cert_ocsp();
} }