nghttpx: Enable TLS session tickets with session key rotation every 12hrs

This commit is contained in:
Tatsuhiro Tsujikawa 2015-01-08 00:01:09 +09:00
parent a804117c83
commit 52f3572d5b
4 changed files with 137 additions and 1 deletions

View File

@ -47,6 +47,7 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/conf.h> #include <openssl/conf.h>
#include <openssl/rand.h>
#include <ev.h> #include <ev.h>
@ -435,6 +436,47 @@ void refresh_cb(struct ev_loop *loop, ev_timer *w, int revents) {
} }
} // namespace } // namespace
namespace {
void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto old_ticket_keys = std::atomic_load(&get_config()->ticket_keys);
auto ticket_keys = std::make_shared<TicketKeys>();
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "renew ticket key";
}
// We store at most 2 ticket keys
if (old_ticket_keys) {
auto &old_keys = old_ticket_keys->keys;
auto &new_keys = ticket_keys->keys;
assert(!old_keys.empty());
new_keys.resize(2);
new_keys[1] = old_keys[0];
} else {
ticket_keys->keys.resize(1);
}
if (RAND_bytes(reinterpret_cast<unsigned char *>(&ticket_keys->keys[0]),
sizeof(ticket_keys->keys[0])) == 0) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "failed to renew ticket key";
}
std::atomic_store(&mod_config()->ticket_keys,
std::shared_ptr<TicketKeys>());
return;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "ticket keys generation done";
for (auto &key : ticket_keys->keys) {
LOG(INFO) << "name: " << util::format_hex(key.name, sizeof(key.name));
}
}
std::atomic_store(&mod_config()->ticket_keys, ticket_keys);
}
} // namespace
namespace { namespace {
int event_loop() { int event_loop() {
auto loop = EV_DEFAULT; auto loop = EV_DEFAULT;
@ -534,6 +576,14 @@ int event_loop() {
refresh_timer.data = listener_handler.get(); refresh_timer.data = listener_handler.get();
ev_timer_again(loop, &refresh_timer); ev_timer_again(loop, &refresh_timer);
ev_timer renew_ticket_key_timer;
if (sv_ssl_ctx) {
// Renew ticket key every 12hrs
ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 12 * 3600.);
ev_timer_again(loop, &renew_ticket_key_timer);
renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
}
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Entering event loop"; LOG(INFO) << "Entering event loop";
} }

View File

@ -149,6 +149,13 @@ Config *mod_config() { return config; }
void create_config() { config = new Config(); } void create_config() { config = new Config(); }
TicketKeys::~TicketKeys() {
/* Erase keys from memory */
for (auto &key : keys) {
memset(&key, 0, sizeof(key));
}
}
namespace { namespace {
int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr, int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
const char *hostport) { const char *hostport) {

View File

@ -162,6 +162,17 @@ struct DownstreamAddr {
uint16_t port; uint16_t port;
}; };
struct TicketKey {
uint8_t name[16];
uint8_t aes_key[16];
uint8_t hmac_key[16];
};
struct TicketKeys {
~TicketKeys();
std::vector<TicketKey> keys;
};
struct Config { struct Config {
// The list of (private key file, certificate file) pair // The list of (private key file, certificate file) pair
std::vector<std::pair<std::string, std::string>> subcerts; std::vector<std::pair<std::string, std::string>> subcerts;
@ -170,6 +181,7 @@ struct Config {
std::vector<unsigned char> alpn_prefs; std::vector<unsigned char> alpn_prefs;
std::vector<LogFragment> accesslog_format; std::vector<LogFragment> accesslog_format;
std::vector<DownstreamAddr> downstream_addrs; std::vector<DownstreamAddr> downstream_addrs;
std::shared_ptr<TicketKeys> ticket_keys;
// binary form of http proxy host and port // binary form of http proxy host and port
sockaddr_union downstream_http_proxy_addr; sockaddr_union downstream_http_proxy_addr;
ev_tstamp http2_upstream_read_timeout; ev_tstamp http2_upstream_read_timeout;

View File

@ -35,6 +35,7 @@
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
#include <openssl/rand.h>
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
@ -142,6 +143,71 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
} }
} // namespace } // namespace
namespace {
int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) {
auto handler = static_cast<ClientHandler *>(SSL_get_app_data(ssl));
auto ticket_keys = std::atomic_load(&get_config()->ticket_keys);
if (!ticket_keys) {
/* No ticket keys available. Perform full handshake */
return 0;
}
auto &keys = ticket_keys->keys;
assert(!keys.empty());
if (enc) {
if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) == 0) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "session ticket key: RAND_bytes failed";
}
return 0;
}
auto &key = keys[0];
if (LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "encrypt session ticket key: "
<< util::format_hex(key.name, 16);
}
memcpy(key_name, key.name, sizeof(key.name));
EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv);
HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(),
nullptr);
return 1;
}
size_t i;
for (i = 0; i < keys.size(); ++i) {
auto &key = keys[0];
if (memcmp(key.name, key_name, sizeof(key.name)) == 0) {
break;
}
}
if (i == keys.size()) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "session ticket key "
<< util::format_hex(key_name, 16) << " not found";
}
return 0;
}
if (LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "decrypt session ticket key: "
<< util::format_hex(key_name, 16);
}
auto &key = keys[i];
HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), nullptr);
EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv);
return i == 0 ? 1 : 2;
}
} // namespace
namespace { namespace {
void info_callback(const SSL *ssl, int where, int ret) { void info_callback(const SSL *ssl, int where, int ret) {
// To mitigate possible DOS attack using lots of renegotiations, we // To mitigate possible DOS attack using lots of renegotiations, we
@ -232,7 +298,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file,
SSL_OP_NO_COMPRESSION | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE |
SSL_OP_NO_TICKET | get_config()->tls_proto_mask); get_config()->tls_proto_mask);
const unsigned char sid_ctx[] = "shrpx"; const unsigned char sid_ctx[] = "shrpx";
SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
@ -346,6 +412,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file,
verify_callback); verify_callback);
} }
SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback);
SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb);
SSL_CTX_set_info_callback(ssl_ctx, info_callback); SSL_CTX_set_info_callback(ssl_ctx, info_callback);
// NPN advertisement // NPN advertisement