From 3a41e4dd1a029ff3254fde29d58d17f90459344a Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 13 Feb 2016 18:17:11 +0900 Subject: [PATCH] nghttpx: Add encryption support for TLS ticket key retrieval --- gennghttpxfun.py | 5 +++- src/shrpx.cc | 48 ++++++++++++++++++++++++++------- src/shrpx_config.cc | 33 +++++++++++++++++++++++ src/shrpx_config.h | 10 +++++++ src/shrpx_connection_handler.cc | 17 ++++++++++++ src/shrpx_connection_handler.h | 1 + src/shrpx_worker_process.cc | 14 ++++++++-- 7 files changed, 115 insertions(+), 13 deletions(-) diff --git a/gennghttpxfun.py b/gennghttpxfun.py index 0df2c3be..b424fe17 100755 --- a/gennghttpxfun.py +++ b/gennghttpxfun.py @@ -117,7 +117,10 @@ OPTIONS = [ "backend-http1-tls", "backend-tls-session-cache-per-worker", "tls-session-cache-memcached-cert-file", - "tls-session-cache-memcached-private-key-file" + "tls-session-cache-memcached-private-key-file", + "tls-ticket-key-memcached-tls", + "tls-ticket-key-memcached-cert-file", + "tls-ticket-key-memcached-private-key-file" ] LOGVARS = [ diff --git a/src/shrpx.cc b/src/shrpx.cc index 7f365ed4..72e91bac 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1520,16 +1520,16 @@ SSL/TLS: ticket key sharing between nghttpx instances is not required. --tls-ticket-key-memcached=, - Specify address of memcached server to store session - cache. This enables shared TLS ticket key between - multiple nghttpx instances. nghttpx does not set TLS - ticket key to memcached. The external ticket key - generator is required. nghttpx just gets TLS ticket - keys from memcached, and use them, possibly replacing - current set of keys. It is up to extern TLS ticket key - generator to rotate keys frequently. See "TLS SESSION - TICKET RESUMPTION" section in manual page to know the - data format in memcached entry. + Specify address of memcached server to get TLS ticket + keys for session resumption. This enables shared TLS + ticket key between multiple nghttpx instances. nghttpx + does not set TLS ticket key to memcached. The external + ticket key generator is required. nghttpx just gets TLS + ticket keys from memcached, and use them, possibly + replacing current set of keys. It is up to extern TLS + ticket key generator to rotate keys frequently. See + "TLS SESSION TICKET RESUMPTION" section in manual page + to know the data format in memcached entry. --tls-ticket-key-memcached-interval= Set interval to get TLS ticket keys from memcached. Default: )" @@ -1550,6 +1550,15 @@ SSL/TLS: Specify cipher to encrypt TLS session ticket. Specify either aes-128-cbc or aes-256-cbc. By default, aes-128-cbc is used. + --tls-ticket-key-memcached-tls + Enable SSL/TLS on memcached connections to get TLS + ticket keys. + --tls-ticket-key-memcached-cert-file= + Path to client certificate for memcached connections to + get TLS ticket keys. + --tls-ticket-key-memcached-private-key-file= + Path to client private key for memcached connections to + get TLS ticket keys. --fetch-ocsp-response-file= Path to fetch-ocsp-response script file. It should be absolute path. @@ -2414,6 +2423,11 @@ int main(int argc, char **argv) { &flag, 109}, {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, required_argument, &flag, 110}, + {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS, no_argument, &flag, 111}, + {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, required_argument, &flag, + 112}, + {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, required_argument, + &flag, 113}, {nullptr, 0, nullptr, 0}}; int option_index = 0; @@ -2886,6 +2900,20 @@ int main(int argc, char **argv) { cmdcfgs.emplace_back( SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, optarg); break; + case 111: + // --tls-ticket-key-memcached-tls + cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS, "yes"); + break; + case 112: + // --tls-ticket-key-memcached-cert-file + cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, + optarg); + break; + case 113: + // --tls-ticket-key-memcached-private-key-file + cmdcfgs.emplace_back( + SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, optarg); + break; default: break; } diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 0b0ff5ff..ad7e4f65 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -764,9 +764,12 @@ enum { SHRPX_OPTID_TLS_TICKET_KEY_CIPHER, SHRPX_OPTID_TLS_TICKET_KEY_FILE, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS, SHRPX_OPTID_USER, SHRPX_OPTID_VERIFY_CLIENT, SHRPX_OPTID_VERIFY_CLIENT_CACERT, @@ -1328,6 +1331,9 @@ int option_lookup_token(const char *name, size_t namelen) { if (util::strieq_l("http2-max-concurrent-stream", name, 27)) { return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS; } + if (util::strieq_l("tls-ticket-key-memcached-tl", name, 27)) { + return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS; + } break; } break; @@ -1363,6 +1369,11 @@ int option_lookup_token(const char *name, size_t namelen) { break; case 34: switch (name[33]) { + case 'e': + if (util::strieq_l("tls-ticket-key-memcached-cert-fil", name, 33)) { + return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE; + } + break; case 'r': if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) { return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER; @@ -1429,6 +1440,16 @@ int option_lookup_token(const char *name, size_t namelen) { break; } break; + case 41: + switch (name[40]) { + case 'e': + if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name, + 40)) { + return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE; + } + break; + } + break; case 44: switch (name[43]) { case 'e': @@ -2267,6 +2288,18 @@ int parse_config(const char *opt, const char *optarg, case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE: mod_config()->tls.session_cache.memcached.private_key_file = optarg; + return 0; + case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS: + mod_config()->tls.ticket.memcached.tls = util::strieq(optarg, "yes"); + + return 0; + case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE: + mod_config()->tls.ticket.memcached.cert_file = optarg; + + return 0; + case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE: + mod_config()->tls.ticket.memcached.private_key_file = optarg; + return 0; case SHRPX_OPTID_CONF: LOG(WARN) << "conf: ignored"; diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 2bf6aef7..19f17cb2 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -215,6 +215,12 @@ constexpr char SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE[] = "tls-session-cache-memcached-cert-file"; constexpr char SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE[] = "tls-session-cache-memcached-private-key-file"; +constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS[] = + "tls-ticket-key-memcached-tls"; +constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE[] = + "tls-ticket-key-memcached-cert-file"; +constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE[] = + "tls-ticket-key-memcached-private-key-file"; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; @@ -341,6 +347,9 @@ struct TLSConfig { Address addr; uint16_t port; std::unique_ptr host; + // Client private key and certificate for authentication + ImmutableString private_key_file; + ImmutableString cert_file; ev_tstamp interval; // Maximum number of retries when getting TLS ticket key from // mamcached, due to network error. @@ -348,6 +357,7 @@ struct TLSConfig { // Maximum number of consecutive error from memcached, when this // limit reached, TLS ticket is disabled. size_t max_fail; + bool tls; } memcached; std::vector files; const EVP_CIPHER *cipher; diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index da023d9c..588e814b 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -759,6 +759,23 @@ void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get( ev_timer_start(loop_, w); } +SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() { + auto &tlsconf = get_config()->tls; + auto &memcachedconf = get_config()->tls.ticket.memcached; + + auto ssl_ctx = ssl::create_ssl_client_context( +#ifdef HAVE_NEVERBLEED + nb_.get(), +#endif // HAVE_NEVERBLEED + StringRef::from_maybe_nullptr(tlsconf.cacert.get()), + StringRef(memcachedconf.cert_file), + StringRef(memcachedconf.private_key_file), StringRef(), nullptr); + + all_ssl_ctx_.push_back(ssl_ctx); + + return ssl_ctx; +} + #ifdef HAVE_NEVERBLEED void ConnectionHandler::set_neverbleed(std::unique_ptr nb) { nb_ = std::move(nb); diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h index 81d872f5..8602b359 100644 --- a/src/shrpx_connection_handler.h +++ b/src/shrpx_connection_handler.h @@ -129,6 +129,7 @@ public: on_tls_ticket_key_get_success(const std::shared_ptr &ticket_keys, ev_timer *w); void schedule_next_tls_ticket_key_memcached_get(ev_timer *w); + SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx(); #ifdef HAVE_NEVERBLEED void set_neverbleed(std::unique_ptr nb); diff --git a/src/shrpx_worker_process.cc b/src/shrpx_worker_process.cc index e7a30311..26863d82 100644 --- a/src/shrpx_worker_process.cc +++ b/src/shrpx_worker_process.cc @@ -420,14 +420,24 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { #endif // HAVE_NEVERBLEED + MemchunkPool mcpool; + ev_timer renew_ticket_key_timer; if (!upstreamconf.no_tls) { auto &ticketconf = get_config()->tls.ticket; + auto &memcachedconf = ticketconf.memcached; if (ticketconf.memcached.host) { + SSL_CTX *ssl_ctx = nullptr; + + if (memcachedconf.tls) { + ssl_ctx = conn_handler.create_tls_ticket_key_memcached_ssl_ctx(); + } + conn_handler.set_tls_ticket_key_memcached_dispatcher( - make_unique(&ticketconf.memcached.addr, loop, - nullptr, "", nullptr)); + make_unique( + &ticketconf.memcached.addr, loop, ssl_ctx, + StringRef(memcachedconf.host.get()), &mcpool)); ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0., 0.);