nghttpx: Implement TLSv1.3 0-RTT anti-replay with ClientHello cache
This commit is contained in:
parent
b71d9ea58e
commit
8c6612d338
|
@ -168,6 +168,10 @@ OPTIONS = [
|
|||
"no-strip-incoming-x-forwarded-proto",
|
||||
"ocsp-startup",
|
||||
"no-verify-ocsp",
|
||||
"tls-anti-replay-memcached",
|
||||
"tls-anti-replay-memcached-cert-file",
|
||||
"tls-anti-replay-memcached-private-key-file",
|
||||
"tls-anti-replay-memcached-address-family",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
|
74
src/shrpx.cc
74
src/shrpx.cc
|
@ -1438,6 +1438,12 @@ void fill_default_config(Config *config) {
|
|||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
auto &anti_replayconf = tlsconf.anti_replay;
|
||||
{
|
||||
auto &memcachedconf = anti_replayconf.memcached;
|
||||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
ticketconf.cipher = EVP_aes_128_cbc();
|
||||
}
|
||||
|
||||
|
@ -2284,6 +2290,25 @@ SSL/TLS:
|
|||
--tls-session-cache-memcached-private-key-file=<PATH>
|
||||
Path to client private key for memcached connections to
|
||||
store session cache.
|
||||
--tls-anti-replay-memcached=<HOST>,<PORT>[;tls]
|
||||
Specify address of memcached server to store ClientHello
|
||||
to avoid 0-RTT early data replay. This enables shared
|
||||
storage between multiple nghttpx instances. Optionally,
|
||||
memcached connection can be encrypted with TLS by
|
||||
specifying "tls" parameter.
|
||||
--tls-anti-replay-memcached-address-family=(auto|IPv4|IPv6)
|
||||
Specify address family of memcached connections to store
|
||||
ClientHello to avoid 0-RTT early data replay. If "auto"
|
||||
is given, both IPv4 and IPv6 are considered. If "IPv4"
|
||||
is given, only IPv4 address is considered. If "IPv6" is
|
||||
given, only IPv6 address is considered.
|
||||
Default: auto
|
||||
--tls-anti-replay-memcached-cert-file=<PATH>
|
||||
Path to client certificate for memcached connections to
|
||||
store ClientHello to avoid 0-RTT early data replay.
|
||||
--tls-anti-replay-memcached-private-key-file=<PATH>
|
||||
Path to client private key for memcached connections to
|
||||
store ClientHello to avoid 0-RTT early data replay.
|
||||
--tls-dyn-rec-warmup-threshold=<SIZE>
|
||||
Specify the threshold size for TLS dynamic record size
|
||||
behaviour. During a TLS session, after the threshold
|
||||
|
@ -2995,6 +3020,26 @@ int process_options(Config *config,
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto &memcachedconf = tlsconf.anti_replay.memcached;
|
||||
if (!memcachedconf.host.empty()) {
|
||||
auto hostport = util::make_hostport(StringRef{memcachedconf.host},
|
||||
memcachedconf.port);
|
||||
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
|
||||
memcachedconf.port, memcachedconf.family) == -1) {
|
||||
LOG(FATAL) << "Resolving memcached address for TLS anti-replay failed: "
|
||||
<< hostport;
|
||||
return -1;
|
||||
}
|
||||
LOG(NOTICE) << "Memcached address for TLS anti-replay: " << hostport
|
||||
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
||||
if (memcachedconf.tls) {
|
||||
LOG(NOTICE) << "Connection to memcached for TLS anti-replay will be "
|
||||
"encrypted by TLS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config->rlimit_nofile) {
|
||||
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
|
||||
static_cast<rlim_t>(config->rlimit_nofile)};
|
||||
|
@ -3404,6 +3449,14 @@ int main(int argc, char **argv) {
|
|||
{SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
|
||||
&flag, 158},
|
||||
{SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED.c_str(), required_argument, &flag,
|
||||
160},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY.c_str(),
|
||||
required_argument, &flag, 161},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE.c_str(),
|
||||
required_argument, &flag, 162},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
|
||||
required_argument, &flag, 163},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -4165,6 +4218,27 @@ int main(int argc, char **argv) {
|
|||
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 160:
|
||||
// --tls-anti-replay-memcached
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 161:
|
||||
// --tls-anti-replay-memcached-address-family
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 162:
|
||||
// --tls-anti-replay-memcached-cert-file
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 163:
|
||||
// --tls-anti-replay-memcached-private-key-file
|
||||
cmdcfgs.emplace_back(
|
||||
SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2072,6 +2072,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
break;
|
||||
case 25:
|
||||
switch (name[24]) {
|
||||
case 'd':
|
||||
if (util::strieq_l("tls-anti-replay-memcache", name, 24)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
|
||||
|
@ -2255,6 +2260,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
if (util::strieq_l("frontend-http2-optimize-window-siz", name, 34)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("tls-anti-replay-memcached-cert-fil", name, 34)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
|
||||
|
@ -2338,6 +2346,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("tls-anti-replay-memcached-address-famil", name, 39)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 41:
|
||||
|
@ -2364,6 +2377,12 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
break;
|
||||
case 42:
|
||||
switch (name[41]) {
|
||||
case 'e':
|
||||
if (util::strieq_l("tls-anti-replay-memcached-private-key-fil", name,
|
||||
41)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
|
||||
41)) {
|
||||
|
@ -3153,7 +3172,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED:
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
|
||||
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
|
||||
auto src_params = StringRef{addr_end, std::end(optarg)};
|
||||
|
||||
|
@ -3183,6 +3203,13 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
memcachedconf.tls = params.tls;
|
||||
break;
|
||||
}
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
|
||||
memcachedconf.port = port;
|
||||
memcachedconf.tls = params.tls;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return 0;
|
||||
|
@ -3330,6 +3357,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
config->tls.ticket.memcached.private_key_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE:
|
||||
config->tls.anti_replay.memcached.cert_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE:
|
||||
config->tls.anti_replay.memcached.private_key_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.ticket.memcached.family, opt,
|
||||
|
@ -3337,6 +3374,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.session_cache.memcached.family,
|
||||
opt, optarg);
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.anti_replay.memcached.family, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->conn.downstream->family, opt, optarg);
|
||||
case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
|
||||
|
|
|
@ -343,6 +343,14 @@ constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO =
|
|||
StringRef::from_lit("no-strip-incoming-x-forwarded-proto");
|
||||
constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
|
||||
constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED =
|
||||
StringRef::from_lit("tls-anti-replay-memcached");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-cert-file");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-private-key-file");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-address-family");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
|
@ -577,6 +585,23 @@ struct TLSConfig {
|
|||
} memcached;
|
||||
} session_cache;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
Address addr;
|
||||
uint16_t port;
|
||||
// Hostname of memcached server. This is also used as SNI field
|
||||
// if TLS is enabled.
|
||||
StringRef host;
|
||||
// Client private key and certificate for authentication
|
||||
StringRef private_key_file;
|
||||
StringRef cert_file;
|
||||
// Address family of memcached connection. One of either
|
||||
// AF_INET, AF_INET6 or AF_UNSPEC.
|
||||
int family;
|
||||
bool tls;
|
||||
} memcached;
|
||||
} anti_replay;
|
||||
|
||||
// Dynamic record sizing configurations
|
||||
struct {
|
||||
size_t warmup_threshold;
|
||||
|
@ -1097,6 +1122,10 @@ enum {
|
|||
SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR,
|
||||
SHRPX_OPTID_SUBCERT,
|
||||
SHRPX_OPTID_SYSLOG_FACILITY,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
|
||||
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
||||
SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "shrpx_log.h"
|
||||
#include "memchunk.h"
|
||||
#include "util.h"
|
||||
#include "ssl_compat.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
|
@ -93,7 +92,15 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
|||
}
|
||||
}
|
||||
|
||||
Connection::~Connection() { disconnect(); }
|
||||
Connection::~Connection() {
|
||||
disconnect();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.ch_md_ctx) {
|
||||
EVP_MD_CTX_free(tls.ch_md_ctx);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
}
|
||||
|
||||
void Connection::disconnect() {
|
||||
if (tls.ssl) {
|
||||
|
@ -111,10 +118,21 @@ void Connection::disconnect() {
|
|||
tls.cached_session_lookup_req = nullptr;
|
||||
}
|
||||
|
||||
if (tls.anti_replay_req) {
|
||||
tls.anti_replay_req->canceled = true;
|
||||
tls.anti_replay_req = nullptr;
|
||||
}
|
||||
|
||||
SSL_shutdown(tls.ssl);
|
||||
SSL_free(tls.ssl);
|
||||
tls.ssl = nullptr;
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.ch_md_ctx) {
|
||||
EVP_MD_CTX_reset(tls.ch_md_ctx);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
tls.wbuf.reset();
|
||||
tls.rbuf.reset();
|
||||
tls.last_write_idle = 0.;
|
||||
|
@ -126,6 +144,7 @@ void Connection::disconnect() {
|
|||
tls.reneg_started = false;
|
||||
tls.sct_requested = false;
|
||||
tls.early_data_finish = false;
|
||||
tls.early_cb_called = false;
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
|
@ -152,6 +171,14 @@ void Connection::prepare_client_handshake() {
|
|||
void Connection::prepare_server_handshake() {
|
||||
SSL_set_accept_state(tls.ssl);
|
||||
tls.server_handshake = true;
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.ch_md_ctx) {
|
||||
tls.ch_md_ctx = EVP_MD_CTX_new();
|
||||
}
|
||||
|
||||
EVP_DigestInit_ex(tls.ch_md_ctx, EVP_sha256(), nullptr);
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
}
|
||||
|
||||
// BIO implementation is inspired by openldap implementation:
|
||||
|
@ -225,7 +252,19 @@ int shrpx_bio_read(BIO *b, char *buf, int len) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
return rbuf.remove(buf, len);
|
||||
len = rbuf.remove(buf, len);
|
||||
|
||||
if (conn->tls.early_cb_called) {
|
||||
return len;
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (EVP_DigestUpdate(conn->tls.ch_md_ctx, buf, len) == 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
return len;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -355,6 +394,7 @@ int Connection::tls_handshake() {
|
|||
|
||||
switch (tls.handshake_state) {
|
||||
case TLS_CONN_WAIT_FOR_SESSION_CACHE:
|
||||
case TLS_CONN_WAIT_FOR_ANTI_REPLAY:
|
||||
return SHRPX_ERR_INPROGRESS;
|
||||
case TLS_CONN_GOT_SESSION_CACHE: {
|
||||
// Use the same trick invented by @kazuho in h2o project.
|
||||
|
@ -401,15 +441,27 @@ int Connection::tls_handshake() {
|
|||
|
||||
rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
|
||||
if (rv == SSL_READ_EARLY_DATA_ERROR) {
|
||||
if (SSL_get_error(tls.ssl, rv) == SSL_ERROR_WANT_EARLY) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO)
|
||||
<< "tls: early_cb returns negative return value; handshake "
|
||||
"interrupted";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have early data, and server sends ServerHello, assume
|
||||
// that handshake is completed in server side, and start
|
||||
// processing request. If we don't exit handshake code here,
|
||||
// server waits for EndOfEarlyData and Finished message from
|
||||
// client, which voids the purpose of 0-RTT data. The left
|
||||
// over of handshake is done through write_tls or read_tls.
|
||||
rv = (tls.handshake_state == TLS_CONN_WRITE_STARTED ||
|
||||
if ((tls.handshake_state == TLS_CONN_WRITE_STARTED ||
|
||||
tls.wbuf.rleft()) &&
|
||||
tls.earlybuf.rleft();
|
||||
tls.earlybuf.rleft()) {
|
||||
rv = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -454,6 +506,9 @@ int Connection::tls_handshake() {
|
|||
}
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
#if OPENSSL_1_1_1_API
|
||||
case SSL_ERROR_WANT_EARLY:
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -469,7 +524,8 @@ int Connection::tls_handshake() {
|
|||
}
|
||||
}
|
||||
|
||||
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
|
||||
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE ||
|
||||
tls.handshake_state == TLS_CONN_WAIT_FOR_ANTI_REPLAY) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: handshake is still in progress";
|
||||
}
|
||||
|
|
|
@ -32,10 +32,12 @@
|
|||
#include <ev.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "shrpx_rate_limit.h"
|
||||
#include "shrpx_error.h"
|
||||
#include "memchunk.h"
|
||||
#include "ssl_compat.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
|
@ -50,6 +52,7 @@ enum {
|
|||
TLS_CONN_WAIT_FOR_SESSION_CACHE,
|
||||
TLS_CONN_GOT_SESSION_CACHE,
|
||||
TLS_CONN_CANCEL_SESSION_CACHE,
|
||||
TLS_CONN_WAIT_FOR_ANTI_REPLAY,
|
||||
TLS_CONN_WRITE_STARTED,
|
||||
};
|
||||
|
||||
|
@ -62,6 +65,11 @@ struct TLSConnection {
|
|||
SSL_SESSION *cached_session;
|
||||
MemcachedRequest *cached_session_lookup_req;
|
||||
tls::TLSSessionCache *client_session_cache;
|
||||
#if OPENSSL_1_1_1_API
|
||||
// Message digest context to calculate ClientHello for anti-replay.
|
||||
EVP_MD_CTX *ch_md_ctx;
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
MemcachedRequest *anti_replay_req;
|
||||
ev_tstamp last_write_idle;
|
||||
size_t warmup_writelen;
|
||||
// length passed to SSL_write and SSL_read last time. This is
|
||||
|
@ -82,6 +90,8 @@ struct TLSConnection {
|
|||
// This value is also true if this is client side connection for
|
||||
// convenience.
|
||||
bool early_data_finish;
|
||||
// true if early_cb gets called.
|
||||
bool early_cb_called;
|
||||
};
|
||||
|
||||
struct TCPHint {
|
||||
|
|
|
@ -235,9 +235,24 @@ int ConnectionHandler::create_single_worker() {
|
|||
}
|
||||
}
|
||||
|
||||
SSL_CTX *anti_replay_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
anti_replay_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
single_worker_ = make_unique<Worker>(
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, anti_replay_ssl_ctx,
|
||||
cert_tree_.get(), ticket_keys_, this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (single_worker_->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
|
@ -293,12 +308,28 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
|||
}
|
||||
}
|
||||
|
||||
SSL_CTX *anti_replay_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
anti_replay_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
auto loop = ev_loop_new(config->ev_loop_flags);
|
||||
|
||||
auto worker = make_unique<Worker>(
|
||||
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
auto worker =
|
||||
make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
|
||||
anti_replay_ssl_ctx, cert_tree_.get(), ticket_keys_,
|
||||
this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (worker->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
|
|
101
src/shrpx_tls.cc
101
src/shrpx_tls.cc
|
@ -534,6 +534,103 @@ void info_callback(const SSL *ssl, int where, int ret) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
constexpr auto MEMCACHED_ANTI_REPLY_KEY_PREFIX =
|
||||
StringRef::from_lit("nghttpx:anti-reply:");
|
||||
|
||||
namespace {
|
||||
int early_cb(SSL *ssl, int *al, void *arg) {
|
||||
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
|
||||
if (conn->tls.early_cb_called) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
conn->tls.early_cb_called = true;
|
||||
|
||||
const unsigned char *ext;
|
||||
size_t extlen;
|
||||
|
||||
if (!SSL_early_get0_ext(conn->tls.ssl, TLSEXT_TYPE_early_data, &ext,
|
||||
&extlen)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "early_data extension does not exist";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!SSL_early_get0_ext(conn->tls.ssl, TLSEXT_TYPE_psk, &ext, &extlen)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "pre_shared_key extension does not exist";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 32> md;
|
||||
unsigned int mdlen;
|
||||
if (EVP_DigestFinal_ex(conn->tls.ch_md_ctx, md.data(), &mdlen) == 0) {
|
||||
LOG(ERROR) << "EVP_DigestFinal_ex failed";
|
||||
return 0;
|
||||
}
|
||||
assert(md.size() == mdlen);
|
||||
|
||||
auto handler = static_cast<ClientHandler *>(conn->data);
|
||||
auto worker = handler->get_worker();
|
||||
auto dispatcher = worker->get_anti_replay_memcached_dispatcher();
|
||||
auto &balloc = handler->get_block_allocator();
|
||||
|
||||
auto &tlsconf = get_config()->tls;
|
||||
|
||||
auto hex_md =
|
||||
util::format_hex(balloc, StringRef{std::begin(md), std::end(md)});
|
||||
|
||||
if (tlsconf.anti_replay.memcached.host.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto req = make_unique<MemcachedRequest>();
|
||||
req->op = MEMCACHED_OP_ADD;
|
||||
req->key = MEMCACHED_ANTI_REPLY_KEY_PREFIX.str();
|
||||
req->key += hex_md;
|
||||
|
||||
// TODO No value at the moment
|
||||
|
||||
// Set the same timeout value for session with the hope that
|
||||
// OpenSSL library invalidates the outdated ticket.
|
||||
req->expiry = tlsconf.session_timeout.count();
|
||||
req->cb = [conn](MemcachedRequest *req, MemcachedResult res) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Memcached: ClientHello anti-replay registration done. key="
|
||||
<< req->key << ", status_code=" << res.status_code;
|
||||
}
|
||||
|
||||
// We might stop reading, so start it again
|
||||
conn->rlimit.startw();
|
||||
ev_timer_again(conn->loop, &conn->rt);
|
||||
|
||||
conn->wlimit.startw();
|
||||
ev_timer_again(conn->loop, &conn->wt);
|
||||
|
||||
conn->tls.anti_replay_req = nullptr;
|
||||
|
||||
if (res.status_code != 0) {
|
||||
// If we cannot add key/value, just disable 0-RTT early data.
|
||||
// Note that memcached atomically adds key/value.
|
||||
conn->tls.early_data_finish = true;
|
||||
}
|
||||
|
||||
conn->tls.handshake_state = TLS_CONN_NORMAL;
|
||||
};
|
||||
|
||||
conn->tls.handshake_state = TLS_CONN_WAIT_FOR_ANTI_REPLAY;
|
||||
conn->tls.anti_replay_req = req.get();
|
||||
|
||||
dispatcher->add_request(std::move(req));
|
||||
|
||||
return -1;
|
||||
}
|
||||
} // namespace
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
namespace {
|
||||
int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
||||
|
@ -920,6 +1017,10 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
|
|||
#endif // OPENSSL_IS_BORINGSSL
|
||||
SSL_CTX_set_info_callback(ssl_ctx, info_callback);
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
SSL_CTX_set_early_cb(ssl_ctx, early_cb, nullptr);
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
|
||||
#endif // OPENSSL_IS_BORINGSSL
|
||||
|
|
|
@ -118,6 +118,7 @@ bool match_shared_downstream_addr(
|
|||
|
||||
Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
||||
SSL_CTX *tls_session_cache_memcached_ssl_ctx,
|
||||
SSL_CTX *tls_anti_replay_memcached_ssl_ctx,
|
||||
tls::CertLookupTree *cert_tree,
|
||||
const std::shared_ptr<TicketKeys> &ticket_keys,
|
||||
ConnectionHandler *conn_handler,
|
||||
|
@ -153,6 +154,15 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
|||
StringRef{session_cacheconf.memcached.host}, &mcpool_, randgen_);
|
||||
}
|
||||
|
||||
auto &anti_replayconf = get_config()->tls.anti_replay;
|
||||
|
||||
if (!anti_replayconf.memcached.host.empty()) {
|
||||
anti_replay_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
|
||||
&anti_replayconf.memcached.addr, loop,
|
||||
tls_anti_replay_memcached_ssl_ctx, anti_replayconf.memcached.host,
|
||||
&mcpool_, randgen_);
|
||||
}
|
||||
|
||||
replace_downstream_config(std::move(downstreamconf));
|
||||
}
|
||||
|
||||
|
@ -474,6 +484,10 @@ MemcachedDispatcher *Worker::get_session_cache_memcached_dispatcher() {
|
|||
return session_cache_memcached_dispatcher_.get();
|
||||
}
|
||||
|
||||
MemcachedDispatcher *Worker::get_anti_replay_memcached_dispatcher() const {
|
||||
return anti_replay_memcached_dispatcher_.get();
|
||||
}
|
||||
|
||||
std::mt19937 &Worker::get_randgen() { return randgen_; }
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
|
|
|
@ -221,6 +221,7 @@ class Worker {
|
|||
public:
|
||||
Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
||||
SSL_CTX *tls_session_cache_memcached_ssl_ctx,
|
||||
SSL_CTX *tls_anti_replay_memcached_ssl_ctx,
|
||||
tls::CertLookupTree *cert_tree,
|
||||
const std::shared_ptr<TicketKeys> &ticket_keys,
|
||||
ConnectionHandler *conn_handler,
|
||||
|
@ -250,6 +251,7 @@ public:
|
|||
void schedule_clear_mcpool();
|
||||
|
||||
MemcachedDispatcher *get_session_cache_memcached_dispatcher();
|
||||
MemcachedDispatcher *get_anti_replay_memcached_dispatcher() const;
|
||||
|
||||
std::mt19937 &get_randgen();
|
||||
|
||||
|
@ -289,6 +291,7 @@ private:
|
|||
|
||||
std::shared_ptr<DownstreamConfig> downstreamconf_;
|
||||
std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
|
||||
std::unique_ptr<MemcachedDispatcher> anti_replay_memcached_dispatcher_;
|
||||
#ifdef HAVE_MRUBY
|
||||
std::unique_ptr<mruby::MRubyContext> mruby_ctx_;
|
||||
#endif // HAVE_MRUBY
|
||||
|
|
Loading…
Reference in New Issue