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:
parent
52f3572d5b
commit
08e8cc1915
|
@ -112,6 +112,8 @@ int main(int argc, char *argv[]) {
|
||||||
shrpx::test_shrpx_config_parse_header) ||
|
shrpx::test_shrpx_config_parse_header) ||
|
||||||
!CU_add_test(pSuite, "config_parse_log_format",
|
!CU_add_test(pSuite, "config_parse_log_format",
|
||||||
shrpx::test_shrpx_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_streq", shrpx::test_util_streq) ||
|
||||||
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
||||||
!CU_add_test(pSuite, "util_inp_strlower",
|
!CU_add_test(pSuite, "util_inp_strlower",
|
||||||
|
|
38
src/shrpx.cc
38
src/shrpx.cc
|
@ -577,7 +577,7 @@ int event_loop() {
|
||||||
ev_timer_again(loop, &refresh_timer);
|
ev_timer_again(loop, &refresh_timer);
|
||||||
|
|
||||||
ev_timer renew_ticket_key_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
|
// Renew ticket key every 12hrs
|
||||||
ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 12 * 3600.);
|
ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 12 * 3600.);
|
||||||
ev_timer_again(loop, &renew_ticket_key_timer);
|
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_host = 8;
|
||||||
mod_config()->downstream_connections_per_frontend = 0;
|
mod_config()->downstream_connections_per_frontend = 0;
|
||||||
mod_config()->listener_disable_timeout = 0.;
|
mod_config()->listener_disable_timeout = 0.;
|
||||||
|
mod_config()->auto_tls_ticket_key = true;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -993,6 +994,25 @@ SSL/TLS:
|
||||||
only and any white spaces are treated as a part
|
only and any white spaces are treated as a part
|
||||||
of protocol string.
|
of protocol string.
|
||||||
Default: )" << DEFAULT_TLS_PROTO_LIST << R"(
|
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:
|
HTTP/2 and SPDY:
|
||||||
-c, --http2-max-concurrent-streams=<NUM>
|
-c, --http2-max-concurrent-streams=<NUM>
|
||||||
|
@ -1261,6 +1281,7 @@ int main(int argc, char **argv) {
|
||||||
{"accesslog-format", required_argument, &flag, 66},
|
{"accesslog-format", required_argument, &flag, 66},
|
||||||
{"backend-http1-connections-per-frontend", required_argument, &flag,
|
{"backend-http1-connections-per-frontend", required_argument, &flag,
|
||||||
67},
|
67},
|
||||||
|
{"tls-ticket-key-file", required_argument, &flag, 68},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 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,
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
|
||||||
optarg);
|
optarg);
|
||||||
break;
|
break;
|
||||||
|
case 68:
|
||||||
|
// --tls-ticket-key-file
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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) {
|
if (get_config()->backend_ipv4 && get_config()->backend_ipv6) {
|
||||||
LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the "
|
LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the "
|
||||||
<< "same time.";
|
<< "same time.";
|
||||||
|
|
|
@ -138,6 +138,7 @@ const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] =
|
||||||
const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] =
|
const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] =
|
||||||
"backend-http1-connections-per-frontend";
|
"backend-http1-connections-per-frontend";
|
||||||
const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout";
|
const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout";
|
||||||
|
const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Config *config = nullptr;
|
Config *config = nullptr;
|
||||||
|
@ -200,6 +201,42 @@ bool is_secure(const char *filename) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // 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) {
|
FILE *open_file_for_write(const char *filename) {
|
||||||
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
|
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
|
||||||
S_IRUSR | S_IWUSR);
|
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);
|
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")) {
|
if (util::strieq(opt, "conf")) {
|
||||||
LOG(WARN) << "conf: ignored";
|
LOG(WARN) << "conf: ignored";
|
||||||
|
|
||||||
|
|
|
@ -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_HOST[];
|
||||||
extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[];
|
extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[];
|
||||||
extern const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[];
|
extern const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[];
|
||||||
|
extern const char SHRPX_OPT_TLS_TICKET_KEY_FILE[];
|
||||||
|
|
||||||
union sockaddr_union {
|
union sockaddr_union {
|
||||||
sockaddr sa;
|
sockaddr sa;
|
||||||
|
@ -181,6 +182,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::vector<std::string> tls_ticket_key_files;
|
||||||
std::shared_ptr<TicketKeys> ticket_keys;
|
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;
|
||||||
|
@ -293,6 +295,7 @@ struct Config {
|
||||||
bool http2_no_cookie_crumbling;
|
bool http2_no_cookie_crumbling;
|
||||||
bool upstream_frame_debug;
|
bool upstream_frame_debug;
|
||||||
bool no_location_rewrite;
|
bool no_location_rewrite;
|
||||||
|
bool auto_tls_ticket_key;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Config *get_config();
|
const Config *get_config();
|
||||||
|
@ -353,6 +356,12 @@ int int_syslog_facility(const char *strfacility);
|
||||||
|
|
||||||
FILE *open_file_for_write(const char *filename);
|
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
|
} // namespace shrpx
|
||||||
|
|
||||||
#endif // SHRPX_CONFIG_H
|
#endif // SHRPX_CONFIG_H
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
*/
|
*/
|
||||||
#include "shrpx_config_test.h"
|
#include "shrpx_config_test.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <CUnit/CUnit.h>
|
#include <CUnit/CUnit.h>
|
||||||
|
|
||||||
#include "shrpx_config.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()));
|
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
|
} // namespace shrpx
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace shrpx {
|
||||||
void test_shrpx_config_parse_config_str_list(void);
|
void test_shrpx_config_parse_config_str_list(void);
|
||||||
void test_shrpx_config_parse_header(void);
|
void test_shrpx_config_parse_header(void);
|
||||||
void test_shrpx_config_parse_log_format(void);
|
void test_shrpx_config_parse_log_format(void);
|
||||||
|
void test_shrpx_config_read_tls_ticket_key_file(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,9 @@ namespace {
|
||||||
int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
|
int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
|
||||||
EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) {
|
EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) {
|
||||||
auto handler = static_cast<ClientHandler *>(SSL_get_app_data(ssl));
|
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) {
|
if (!ticket_keys) {
|
||||||
/* No ticket keys available. Perform full handshake */
|
/* No ticket keys available. Perform full handshake */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue