nghttpx: Add --tls-ticket-key-file option

This option specifies files contains 48 random bytes to construct
session ticket key data.  This option can be used repeatedly to
specify multiple keys, but only the first one is used to encrypt
tickets.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-01-08 01:26:30 +09:00
parent 52f3572d5b
commit 08e8cc1915
7 changed files with 132 additions and 2 deletions

View File

@ -112,6 +112,8 @@ int main(int argc, char *argv[]) {
shrpx::test_shrpx_config_parse_header) ||
!CU_add_test(pSuite, "config_parse_log_format",
shrpx::test_shrpx_config_parse_log_format) ||
!CU_add_test(pSuite, "config_read_tls_ticket_key_file",
shrpx::test_shrpx_config_read_tls_ticket_key_file) ||
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
!CU_add_test(pSuite, "util_inp_strlower",

View File

@ -577,7 +577,7 @@ int event_loop() {
ev_timer_again(loop, &refresh_timer);
ev_timer renew_ticket_key_timer;
if (sv_ssl_ctx) {
if (sv_ssl_ctx && get_config()->auto_tls_ticket_key) {
// 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);
@ -746,6 +746,7 @@ void fill_default_config() {
mod_config()->downstream_connections_per_host = 8;
mod_config()->downstream_connections_per_frontend = 0;
mod_config()->listener_disable_timeout = 0.;
mod_config()->auto_tls_ticket_key = true;
}
} // namespace
@ -993,6 +994,25 @@ SSL/TLS:
only and any white spaces are treated as a part
of protocol string.
Default: )" << DEFAULT_TLS_PROTO_LIST << R"(
--tls-ticket-key-file=<FILE>
Path to file that contains 48 bytes random data
to construct TLS session ticket parameters. This
options can be used repeatedly to specify
multiple ticket parameters. If several files are
given, only the first key is used to encrypt TLS
session tickets. Other keys are accepted but
server will issue new session ticket with first
key. This allows session key rotation. Please
note that key rotation does not occur
automatically. User should rearrange files or
change options values and restart nghttpx
gracefully. If opening or reading given file
fails, all loaded keys are discarded and it is
treated as if none of this option is given. If
this option is not given or an error occurred
while opening or reading a file, key is generated
automatically and renewed every 12hrs. At most 2
keys are stored in memory.
HTTP/2 and SPDY:
-c, --http2-max-concurrent-streams=<NUM>
@ -1261,6 +1281,7 @@ int main(int argc, char **argv) {
{"accesslog-format", required_argument, &flag, 66},
{"backend-http1-connections-per-frontend", required_argument, &flag,
67},
{"tls-ticket-key-file", required_argument, &flag, 68},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
@ -1570,6 +1591,10 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
optarg);
break;
case 68:
// --tls-ticket-key-file
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, optarg);
break;
default:
break;
}
@ -1722,6 +1747,17 @@ int main(int argc, char **argv) {
}
}
if (!get_config()->tls_ticket_key_files.empty()) {
auto ticket_keys =
read_tls_ticket_key_file(get_config()->tls_ticket_key_files);
if (!ticket_keys) {
LOG(WARN) << "Use internal session ticket key generator";
} else {
mod_config()->ticket_keys = std::move(ticket_keys);
mod_config()->auto_tls_ticket_key = false;
}
}
if (get_config()->backend_ipv4 && get_config()->backend_ipv6) {
LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the "
<< "same time.";

View File

@ -138,6 +138,7 @@ const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] =
const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] =
"backend-http1-connections-per-frontend";
const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout";
const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file";
namespace {
Config *config = nullptr;
@ -200,6 +201,42 @@ bool is_secure(const char *filename) {
}
} // namespace
std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<std::string> &files) {
auto ticket_keys = util::make_unique<TicketKeys>();
auto &keys = ticket_keys->keys;
keys.resize(files.size());
size_t i = 0;
for (auto &file : files) {
std::ifstream f(file.c_str());
if (!f) {
LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
return nullptr;
}
char buf[48];
f.read(buf, sizeof(buf));
if (f.gcount() != sizeof(buf)) {
LOG(ERROR) << "tls-ticket-key-file: want to read 48 bytes but read "
<< f.gcount() << " bytes from " << file;
return nullptr;
}
auto &key = keys[i++];
auto p = buf;
memcpy(key.name, p, sizeof(key.name));
p += sizeof(key.name);
memcpy(key.aes_key, p, sizeof(key.aes_key));
p += sizeof(key.aes_key);
memcpy(key.hmac_key, p, sizeof(key.hmac_key));
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "session ticket key: " << util::format_hex(key.name,
sizeof(key.name));
}
}
return ticket_keys;
}
FILE *open_file_for_write(const char *filename) {
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
@ -1067,6 +1104,11 @@ int parse_config(const char *opt, const char *optarg) {
return parse_timeval(&mod_config()->listener_disable_timeout, opt, optarg);
}
if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) {
mod_config()->tls_ticket_key_files.push_back(optarg);
return 0;
}
if (util::strieq(opt, "conf")) {
LOG(WARN) << "conf: ignored";

View File

@ -127,6 +127,7 @@ extern const char SHRPX_OPT_NO_LOCATION_REWRITE[];
extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[];
extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[];
extern const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[];
extern const char SHRPX_OPT_TLS_TICKET_KEY_FILE[];
union sockaddr_union {
sockaddr sa;
@ -181,6 +182,7 @@ struct Config {
std::vector<unsigned char> alpn_prefs;
std::vector<LogFragment> accesslog_format;
std::vector<DownstreamAddr> downstream_addrs;
std::vector<std::string> tls_ticket_key_files;
std::shared_ptr<TicketKeys> ticket_keys;
// binary form of http proxy host and port
sockaddr_union downstream_http_proxy_addr;
@ -293,6 +295,7 @@ struct Config {
bool http2_no_cookie_crumbling;
bool upstream_frame_debug;
bool no_location_rewrite;
bool auto_tls_ticket_key;
};
const Config *get_config();
@ -353,6 +356,12 @@ int int_syslog_facility(const char *strfacility);
FILE *open_file_for_write(const char *filename);
// Reads TLS ticket key file in |files| and returns TicketKey which
// stores read key data. This function returns TicketKey if it
// succeeds, or nullptr.
std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<std::string> &files);
} // namespace shrpx
#endif // SHRPX_CONFIG_H

View File

@ -24,6 +24,10 @@
*/
#include "shrpx_config_test.h"
#include <unistd.h>
#include <cstdlib>
#include <CUnit/CUnit.h>
#include "shrpx_config.h"
@ -132,4 +136,38 @@ void test_shrpx_config_parse_log_format(void) {
CU_ASSERT(0 == strcmp("\"", res[13].value.get()));
}
void test_shrpx_config_read_tls_ticket_key_file(void) {
char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
auto fd1 = mkstemp(file1);
assert(fd1 != -1);
assert(48 ==
write(fd1, "0..............12..............34..............5", 48));
char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
auto fd2 = mkstemp(file2);
assert(fd2 != -1);
assert(48 ==
write(fd2, "6..............78..............9a..............b", 48));
close(fd1);
close(fd2);
auto ticket_keys = read_tls_ticket_key_file({file1, file2});
unlink(file1);
unlink(file2);
CU_ASSERT(ticket_keys.get() != nullptr);
CU_ASSERT(2 == ticket_keys->keys.size());
auto key = &ticket_keys->keys[0];
CU_ASSERT(0 == memcmp("0..............1", key->name, sizeof(key->name)));
CU_ASSERT(0 ==
memcmp("2..............3", key->aes_key, sizeof(key->aes_key)));
CU_ASSERT(0 ==
memcmp("4..............5", key->hmac_key, sizeof(key->hmac_key)));
key = &ticket_keys->keys[1];
CU_ASSERT(0 == memcmp("6..............7", key->name, sizeof(key->name)));
CU_ASSERT(0 ==
memcmp("8..............9", key->aes_key, sizeof(key->aes_key)));
CU_ASSERT(0 ==
memcmp("a..............b", key->hmac_key, sizeof(key->hmac_key)));
}
} // namespace shrpx

View File

@ -30,6 +30,7 @@ namespace shrpx {
void test_shrpx_config_parse_config_str_list(void);
void test_shrpx_config_parse_header(void);
void test_shrpx_config_parse_log_format(void);
void test_shrpx_config_read_tls_ticket_key_file(void);
} // namespace shrpx

View File

@ -147,7 +147,9 @@ 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);
auto ticket_keys = get_config()->auto_tls_ticket_key
? std::atomic_load(&get_config()->ticket_keys)
: get_config()->ticket_keys;
if (!ticket_keys) {
/* No ticket keys available. Perform full handshake */
return 0;