nghttpx: Structured TLS related configurations

This commit is contained in:
Tatsuhiro Tsujikawa 2016-01-18 14:21:09 +09:00
parent b12af8c410
commit f3e1dc7a4f
10 changed files with 266 additions and 212 deletions

View File

@ -903,9 +903,6 @@ void fill_default_config() {
mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION;
mod_config()->host = strcopy("*"); mod_config()->host = strcopy("*");
mod_config()->port = 3000; mod_config()->port = 3000;
mod_config()->private_key_file = nullptr;
mod_config()->private_key_passwd = nullptr;
mod_config()->cert_file = nullptr;
// Read timeout for HTTP2 upstream connection // Read timeout for HTTP2 upstream connection
mod_config()->http2_upstream_read_timeout = 3_min; mod_config()->http2_upstream_read_timeout = 3_min;
@ -957,14 +954,11 @@ void fill_default_config() {
mod_config()->syslog_facility = LOG_DAEMON; mod_config()->syslog_facility = LOG_DAEMON;
// Default accept() backlog // Default accept() backlog
mod_config()->backlog = 512; mod_config()->backlog = 512;
mod_config()->ciphers = nullptr;
mod_config()->http2_proxy = false; mod_config()->http2_proxy = false;
mod_config()->http2_bridge = false; mod_config()->http2_bridge = false;
mod_config()->client_proxy = false; mod_config()->client_proxy = false;
mod_config()->client = false; mod_config()->client = false;
mod_config()->client_mode = false; mod_config()->client_mode = false;
mod_config()->insecure = false;
mod_config()->cacert = nullptr;
mod_config()->pid_file = nullptr; mod_config()->pid_file = nullptr;
mod_config()->user = nullptr; mod_config()->user = nullptr;
mod_config()->uid = 0; mod_config()->uid = 0;
@ -981,10 +975,6 @@ void fill_default_config() {
mod_config()->worker_read_burst = 0; mod_config()->worker_read_burst = 0;
mod_config()->worker_write_rate = 0; mod_config()->worker_write_rate = 0;
mod_config()->worker_write_burst = 0; mod_config()->worker_write_burst = 0;
mod_config()->verify_client = false;
mod_config()->verify_client_cacert = nullptr;
mod_config()->client_private_key_file = nullptr;
mod_config()->client_cert_file = nullptr;
mod_config()->http2_upstream_dump_request_header = nullptr; mod_config()->http2_upstream_dump_request_header = nullptr;
mod_config()->http2_upstream_dump_response_header = nullptr; mod_config()->http2_upstream_dump_response_header = nullptr;
mod_config()->http2_no_cookie_crumbling = false; mod_config()->http2_no_cookie_crumbling = false;
@ -1002,7 +992,6 @@ void fill_default_config() {
nghttp2_option_set_peer_max_concurrent_streams( nghttp2_option_set_peer_max_concurrent_streams(
get_config()->http2_client_option, 100); get_config()->http2_client_option, 100);
mod_config()->tls_proto_mask = 0;
mod_config()->no_location_rewrite = false; mod_config()->no_location_rewrite = false;
mod_config()->no_host_rewrite = true; mod_config()->no_host_rewrite = true;
mod_config()->argc = 0; mod_config()->argc = 0;
@ -1015,23 +1004,42 @@ void fill_default_config() {
mod_config()->no_server_push = false; mod_config()->no_server_push = false;
mod_config()->host_unix = false; mod_config()->host_unix = false;
mod_config()->http2_downstream_connections_per_worker = 0; mod_config()->http2_downstream_connections_per_worker = 0;
// ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
mod_config()->ocsp_update_interval = 4_h;
mod_config()->fetch_ocsp_response_file =
strcopy(PKGDATADIR "/fetch-ocsp-response");
mod_config()->no_ocsp = false;
mod_config()->header_field_buffer = 64_k; mod_config()->header_field_buffer = 64_k;
mod_config()->max_header_fields = 100; mod_config()->max_header_fields = 100;
mod_config()->downstream_addr_group_catch_all = 0; mod_config()->downstream_addr_group_catch_all = 0;
mod_config()->tls_ticket_key_cipher = EVP_aes_128_cbc();
mod_config()->tls_ticket_key_cipher_given = false;
mod_config()->tls_session_timeout = std::chrono::hours(12);
mod_config()->tls_ticket_key_memcached_max_retry = 3;
mod_config()->tls_ticket_key_memcached_max_fail = 2;
mod_config()->tls_ticket_key_memcached_interval = 10_min;
mod_config()->fastopen = 0; mod_config()->fastopen = 0;
mod_config()->tls_dyn_rec_warmup_threshold = 1_m;
mod_config()->tls_dyn_rec_idle_timeout = 1.; auto &tlsconf = mod_config()->tls;
{
auto &ticketconf = tlsconf.ticket;
ticketconf.cipher = EVP_aes_128_cbc();
ticketconf.cipher_given = false;
{
auto &memcachedconf = ticketconf.memcached;
memcachedconf.max_retry = 3;
memcachedconf.max_fail = 2;
memcachedconf.interval = 10_min;
}
auto &ocspconf = tlsconf.ocsp;
// ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
ocspconf.update_interval = 4_h;
ocspconf.fetch_ocsp_response_file =
strcopy(PKGDATADIR "/fetch-ocsp-response");
ocspconf.disabled = false;
auto &client_verify = tlsconf.client_verify;
client_verify.enabled = false;
auto &dyn_recconf = tlsconf.dyn_rec;
dyn_recconf.warmup_threshold = 1_m;
dyn_recconf.idle_timeout = 1.;
tlsconf.session_timeout = std::chrono::hours(12);
tlsconf.tls_proto_mask = 0;
tlsconf.insecure = false;
}
} }
} // namespace } // namespace
@ -1386,8 +1394,7 @@ SSL/TLS:
--tls-ticket-key-memcached-interval=<DURATION> --tls-ticket-key-memcached-interval=<DURATION>
Set interval to get TLS ticket keys from memcached. Set interval to get TLS ticket keys from memcached.
Default: )" Default: )"
<< util::duration_str(get_config()->tls_ticket_key_memcached_interval) << util::duration_str(get_config()->tls.ticket.memcached.interval) << R"(
<< R"(
--tls-ticket-key-memcached-max-retry=<N> --tls-ticket-key-memcached-max-retry=<N>
Set maximum number of consecutive retries before Set maximum number of consecutive retries before
abandoning TLS ticket key retrieval. If this number is abandoning TLS ticket key retrieval. If this number is
@ -1395,13 +1402,11 @@ SSL/TLS:
"failure" count is incremented by 1, which contributed "failure" count is incremented by 1, which contributed
to the value controlled to the value controlled
--tls-ticket-key-memcached-max-fail option. --tls-ticket-key-memcached-max-fail option.
Default: )" << get_config()->tls_ticket_key_memcached_max_retry Default: )" << get_config()->tls.ticket.memcached.max_retry << R"(
<< R"(
--tls-ticket-key-memcached-max-fail=<N> --tls-ticket-key-memcached-max-fail=<N>
Set maximum number of consecutive failure before Set maximum number of consecutive failure before
disabling TLS ticket until next scheduled key retrieval. disabling TLS ticket until next scheduled key retrieval.
Default: )" << get_config()->tls_ticket_key_memcached_max_fail Default: )" << get_config()->tls.ticket.memcached.max_fail << R"(
<< R"(
--tls-ticket-key-cipher=<CIPHER> --tls-ticket-key-cipher=<CIPHER>
Specify cipher to encrypt TLS session ticket. Specify Specify cipher to encrypt TLS session ticket. Specify
either aes-128-cbc or aes-256-cbc. By default, either aes-128-cbc or aes-256-cbc. By default,
@ -1409,11 +1414,12 @@ SSL/TLS:
--fetch-ocsp-response-file=<PATH> --fetch-ocsp-response-file=<PATH>
Path to fetch-ocsp-response script file. It should be Path to fetch-ocsp-response script file. It should be
absolute path. absolute path.
Default: )" << get_config()->fetch_ocsp_response_file.get() << R"( Default: )"
<< get_config()->tls.ocsp.fetch_ocsp_response_file.get() << R"(
--ocsp-update-interval=<DURATION> --ocsp-update-interval=<DURATION>
Set interval to update OCSP response cache. Set interval to update OCSP response cache.
Default: )" Default: )"
<< util::duration_str(get_config()->ocsp_update_interval) << R"( << util::duration_str(get_config()->tls.ocsp.update_interval) << R"(
--no-ocsp Disable OCSP stapling. --no-ocsp Disable OCSP stapling.
--tls-session-cache-memcached=<HOST>,<PORT> --tls-session-cache-memcached=<HOST>,<PORT>
Specify address of memcached server to store session Specify address of memcached server to store session
@ -1431,14 +1437,14 @@ SSL/TLS:
period. This behaviour applies to all TLS based period. This behaviour applies to all TLS based
frontends, and TLS HTTP/2 backends. frontends, and TLS HTTP/2 backends.
Default: )" Default: )"
<< util::utos_unit(get_config()->tls_dyn_rec_warmup_threshold) << R"( << util::utos_unit(get_config()->tls.dyn_rec.warmup_threshold) << R"(
--tls-dyn-rec-idle-timeout=<DURATION> --tls-dyn-rec-idle-timeout=<DURATION>
Specify TLS dynamic record size behaviour timeout. See Specify TLS dynamic record size behaviour timeout. See
--tls-dyn-rec-warmup-threshold for more information. --tls-dyn-rec-warmup-threshold for more information.
This behaviour applies to all TLS based frontends, and This behaviour applies to all TLS based frontends, and
TLS HTTP/2 backends. TLS HTTP/2 backends.
Default: )" Default: )"
<< util::duration_str(get_config()->tls_dyn_rec_idle_timeout) << R"( << util::duration_str(get_config()->tls.dyn_rec.idle_timeout) << R"(
HTTP/2 and SPDY: HTTP/2 and SPDY:
-c, --http2-max-concurrent-streams=<N> -c, --http2-max-concurrent-streams=<N>
@ -2410,18 +2416,19 @@ int main(int argc, char **argv) {
} }
} }
if (get_config()->npn_list.empty()) { auto &tlsconf = mod_config()->tls;
mod_config()->npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST);
if (tlsconf.npn_list.empty()) {
tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST);
} }
if (get_config()->tls_proto_list.empty()) { if (tlsconf.tls_proto_list.empty()) {
mod_config()->tls_proto_list = tlsconf.tls_proto_list =
util::parse_config_str_list(DEFAULT_TLS_PROTO_LIST); util::parse_config_str_list(DEFAULT_TLS_PROTO_LIST);
} }
mod_config()->tls_proto_mask = tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
ssl::create_tls_proto_mask(get_config()->tls_proto_list);
mod_config()->alpn_prefs = ssl::set_alpn_prefs(get_config()->npn_list); tlsconf.alpn_prefs = ssl::set_alpn_prefs(tlsconf.npn_list);
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 "
@ -2454,18 +2461,18 @@ int main(int argc, char **argv) {
} }
if (!get_config()->upstream_no_tls && if (!get_config()->upstream_no_tls &&
(!get_config()->private_key_file || !get_config()->cert_file)) { (!tlsconf.private_key_file || !tlsconf.cert_file)) {
print_usage(std::cerr); print_usage(std::cerr);
LOG(FATAL) << "Too few arguments"; LOG(FATAL) << "Too few arguments";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { if (!get_config()->upstream_no_tls && !tlsconf.ocsp.disabled) {
struct stat buf; struct stat buf;
if (stat(get_config()->fetch_ocsp_response_file.get(), &buf) != 0) { if (stat(tlsconf.ocsp.fetch_ocsp_response_file.get(), &buf) != 0) {
mod_config()->no_ocsp = true; tlsconf.ocsp.disabled = true;
LOG(WARN) << "--fetch-ocsp-response-file: " LOG(WARN) << "--fetch-ocsp-response-file: "
<< get_config()->fetch_ocsp_response_file.get() << tlsconf.ocsp.fetch_ocsp_response_file.get()
<< " not found. OCSP stapling has been disabled."; << " not found. OCSP stapling has been disabled.";
} }
} }
@ -2581,23 +2588,25 @@ int main(int argc, char **argv) {
} }
} }
if (get_config()->session_cache_memcached_host) { {
if (resolve_hostname(&mod_config()->session_cache_memcached_addr, auto &memcachedconf = tlsconf.session_cache.memcached;
get_config()->session_cache_memcached_host.get(), if (memcachedconf.host) {
get_config()->session_cache_memcached_port, if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(),
AF_UNSPEC) == -1) { memcachedconf.port, AF_UNSPEC) == -1) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
}
if (get_config()->tls_ticket_key_memcached_host) { {
if (resolve_hostname(&mod_config()->tls_ticket_key_memcached_addr, auto &memcachedconf = tlsconf.ticket.memcached;
get_config()->tls_ticket_key_memcached_host.get(), if (memcachedconf.host) {
get_config()->tls_ticket_key_memcached_port, if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(),
AF_UNSPEC) == -1) { memcachedconf.port, AF_UNSPEC) == -1) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
}
if (get_config()->rlimit_nofile) { if (get_config()->rlimit_nofile) {
struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile), struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile),

View File

@ -375,8 +375,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
get_config()->upstream_read_timeout, get_config()->write_rate, get_config()->upstream_read_timeout, get_config()->write_rate,
get_config()->write_burst, get_config()->read_rate, get_config()->write_burst, get_config()->read_rate,
get_config()->read_burst, writecb, readcb, timeoutcb, this, get_config()->read_burst, writecb, readcb, timeoutcb, this,
get_config()->tls_dyn_rec_warmup_threshold, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls_dyn_rec_idle_timeout), get_config()->tls.dyn_rec.idle_timeout),
pinned_http2sessions_( pinned_http2sessions_(
get_config()->downstream_proto == PROTO_HTTP2 get_config()->downstream_proto == PROTO_HTTP2
? make_unique<std::vector<ssize_t>>( ? make_unique<std::vector<ssize_t>>(
@ -521,7 +521,8 @@ int ClientHandler::validate_next_proto() {
CLOG(INFO, this) << "The negotiated next protocol: " << proto; CLOG(INFO, this) << "The negotiated next protocol: " << proto;
} }
if (!ssl::in_proto_list(get_config()->npn_list, next_proto, next_proto_len)) { if (!ssl::in_proto_list(get_config()->tls.npn_list, next_proto,
next_proto_len)) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "The negotiated protocol is not supported"; CLOG(INFO, this) << "The negotiated protocol is not supported";
} }

View File

@ -1601,7 +1601,7 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD: case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
mod_config()->backend_tls_sni_name = optarg; mod_config()->tls.backend_sni_name = optarg;
return 0; return 0;
case SHRPX_OPTID_PID_FILE: case SHRPX_OPTID_PID_FILE:
@ -1622,7 +1622,7 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
} }
case SHRPX_OPTID_PRIVATE_KEY_FILE: case SHRPX_OPTID_PRIVATE_KEY_FILE:
mod_config()->private_key_file = strcopy(optarg); mod_config()->tls.private_key_file = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: { case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
@ -1631,16 +1631,16 @@ int parse_config(const char *opt, const char *optarg,
LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
return -1; return -1;
} }
mod_config()->private_key_passwd = strcopy(passwd); mod_config()->tls.private_key_passwd = strcopy(passwd);
return 0; return 0;
} }
case SHRPX_OPTID_CERTIFICATE_FILE: case SHRPX_OPTID_CERTIFICATE_FILE:
mod_config()->cert_file = strcopy(optarg); mod_config()->tls.cert_file = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_DH_PARAM_FILE: case SHRPX_OPTID_DH_PARAM_FILE:
mod_config()->dh_param_file = strcopy(optarg); mod_config()->tls.dh_param_file = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_SUBCERT: { case SHRPX_OPTID_SUBCERT: {
@ -1649,7 +1649,7 @@ int parse_config(const char *opt, const char *optarg,
if (sp) { if (sp) {
std::string keyfile(optarg, sp); std::string keyfile(optarg, sp);
// TODO Do we need private key for subcert? // TODO Do we need private key for subcert?
mod_config()->subcerts.emplace_back(keyfile, sp + 1); mod_config()->tls.subcerts.emplace_back(keyfile, sp + 1);
} }
return 0; return 0;
@ -1681,7 +1681,7 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
} }
case SHRPX_OPTID_CIPHERS: case SHRPX_OPTID_CIPHERS:
mod_config()->ciphers = strcopy(optarg); mod_config()->tls.ciphers = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_CLIENT: case SHRPX_OPTID_CLIENT:
@ -1689,11 +1689,11 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
case SHRPX_OPTID_INSECURE: case SHRPX_OPTID_INSECURE:
mod_config()->insecure = util::strieq(optarg, "yes"); mod_config()->tls.insecure = util::strieq(optarg, "yes");
return 0; return 0;
case SHRPX_OPTID_CACERT: case SHRPX_OPTID_CACERT:
mod_config()->cacert = strcopy(optarg); mod_config()->tls.cacert = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_BACKEND_IPV4: case SHRPX_OPTID_BACKEND_IPV4:
@ -1762,27 +1762,27 @@ int parse_config(const char *opt, const char *optarg,
LOG(WARN) << opt << ": not implemented yet"; LOG(WARN) << opt << ": not implemented yet";
return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg); return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg);
case SHRPX_OPTID_NPN_LIST: case SHRPX_OPTID_NPN_LIST:
mod_config()->npn_list = util::parse_config_str_list(optarg); mod_config()->tls.npn_list = util::parse_config_str_list(optarg);
return 0; return 0;
case SHRPX_OPTID_TLS_PROTO_LIST: case SHRPX_OPTID_TLS_PROTO_LIST:
mod_config()->tls_proto_list = util::parse_config_str_list(optarg); mod_config()->tls.tls_proto_list = util::parse_config_str_list(optarg);
return 0; return 0;
case SHRPX_OPTID_VERIFY_CLIENT: case SHRPX_OPTID_VERIFY_CLIENT:
mod_config()->verify_client = util::strieq(optarg, "yes"); mod_config()->tls.client_verify.enabled = util::strieq(optarg, "yes");
return 0; return 0;
case SHRPX_OPTID_VERIFY_CLIENT_CACERT: case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
mod_config()->verify_client_cacert = strcopy(optarg); mod_config()->tls.client_verify.cacert = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE: case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
mod_config()->client_private_key_file = strcopy(optarg); mod_config()->tls.client.private_key_file = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_CLIENT_CERT_FILE: case SHRPX_OPTID_CLIENT_CERT_FILE:
mod_config()->client_cert_file = strcopy(optarg); mod_config()->tls.client.cert_file = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
@ -1899,7 +1899,7 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT:
return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg);
case SHRPX_OPTID_TLS_TICKET_KEY_FILE: case SHRPX_OPTID_TLS_TICKET_KEY_FILE:
mod_config()->tls_ticket_key_files.push_back(optarg); mod_config()->tls.ticket.files.push_back(optarg);
return 0; return 0;
case SHRPX_OPTID_RLIMIT_NOFILE: { case SHRPX_OPTID_RLIMIT_NOFILE: {
int n; int n;
@ -1948,13 +1948,13 @@ int parse_config(const char *opt, const char *optarg,
return parse_uint(&mod_config()->http2_downstream_connections_per_worker, return parse_uint(&mod_config()->http2_downstream_connections_per_worker,
opt, optarg); opt, optarg);
case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
mod_config()->fetch_ocsp_response_file = strcopy(optarg); mod_config()->tls.ocsp.fetch_ocsp_response_file = strcopy(optarg);
return 0; return 0;
case SHRPX_OPTID_OCSP_UPDATE_INTERVAL: case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg); return parse_duration(&mod_config()->tls.ocsp.update_interval, opt, optarg);
case SHRPX_OPTID_NO_OCSP: case SHRPX_OPTID_NO_OCSP:
mod_config()->no_ocsp = util::strieq(optarg, "yes"); mod_config()->tls.ocsp.disabled = util::strieq(optarg, "yes");
return 0; return 0;
case SHRPX_OPTID_HEADER_FIELD_BUFFER: case SHRPX_OPTID_HEADER_FIELD_BUFFER:
@ -1980,15 +1980,15 @@ int parse_config(const char *opt, const char *optarg,
} }
case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER: case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
if (util::strieq(optarg, "aes-128-cbc")) { if (util::strieq(optarg, "aes-128-cbc")) {
mod_config()->tls_ticket_key_cipher = EVP_aes_128_cbc(); mod_config()->tls.ticket.cipher = EVP_aes_128_cbc();
} else if (util::strieq(optarg, "aes-256-cbc")) { } else if (util::strieq(optarg, "aes-256-cbc")) {
mod_config()->tls_ticket_key_cipher = EVP_aes_256_cbc(); mod_config()->tls.ticket.cipher = EVP_aes_256_cbc();
} else { } else {
LOG(ERROR) << opt LOG(ERROR) << opt
<< ": unsupported cipher for ticket encryption: " << optarg; << ": unsupported cipher for ticket encryption: " << optarg;
return -1; return -1;
} }
mod_config()->tls_ticket_key_cipher_given = true; mod_config()->tls.ticket.cipher_given = true;
return 0; return 0;
case SHRPX_OPTID_HOST_REWRITE: case SHRPX_OPTID_HOST_REWRITE:
@ -2001,8 +2001,9 @@ int parse_config(const char *opt, const char *optarg,
return -1; return -1;
} }
mod_config()->session_cache_memcached_host = strcopy(host); auto &memcachedconf = mod_config()->tls.session_cache.memcached;
mod_config()->session_cache_memcached_port = port; memcachedconf.host = strcopy(host);
memcachedconf.port = port;
return 0; return 0;
} }
@ -2012,13 +2013,14 @@ int parse_config(const char *opt, const char *optarg,
return -1; return -1;
} }
mod_config()->tls_ticket_key_memcached_host = strcopy(host); auto &memcachedconf = mod_config()->tls.ticket.memcached;
mod_config()->tls_ticket_key_memcached_port = port; memcachedconf.host = strcopy(host);
memcachedconf.port = port;
return 0; return 0;
} }
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL:
return parse_duration(&mod_config()->tls_ticket_key_memcached_interval, opt, return parse_duration(&mod_config()->tls.ticket.memcached.interval, opt,
optarg); optarg);
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: { case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: {
int n; int n;
@ -2031,11 +2033,11 @@ int parse_config(const char *opt, const char *optarg,
return -1; return -1;
} }
mod_config()->tls_ticket_key_memcached_max_retry = n; mod_config()->tls.ticket.memcached.max_retry = n;
return 0; return 0;
} }
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL:
return parse_uint(&mod_config()->tls_ticket_key_memcached_max_fail, opt, return parse_uint(&mod_config()->tls.ticket.memcached.max_fail, opt,
optarg); optarg);
case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: { case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: {
size_t n; size_t n;
@ -2043,13 +2045,13 @@ int parse_config(const char *opt, const char *optarg,
return -1; return -1;
} }
mod_config()->tls_dyn_rec_warmup_threshold = n; mod_config()->tls.dyn_rec.warmup_threshold = n;
return 0; return 0;
} }
case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT: case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT:
return parse_duration(&mod_config()->tls_dyn_rec_idle_timeout, opt, optarg); return parse_duration(&mod_config()->tls.dyn_rec.idle_timeout, opt, optarg);
case SHRPX_OPTID_MRUBY_FILE: case SHRPX_OPTID_MRUBY_FILE:
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY

View File

@ -292,25 +292,94 @@ struct HttpProxy {
uint16_t port; uint16_t port;
}; };
struct Config { struct TLSConfig {
// RFC 5077 Session ticket related configurations
struct {
struct {
Address addr;
uint16_t port;
std::unique_ptr<char[]> host;
ev_tstamp interval;
// Maximum number of retries when getting TLS ticket key from
// mamcached, due to network error.
size_t max_retry;
// Maximum number of consecutive error from memcached, when this
// limit reached, TLS ticket is disabled.
size_t max_fail;
} memcached;
std::vector<std::string> files;
const EVP_CIPHER *cipher;
// true if --tls-ticket-key-cipher is used
bool cipher_given;
} ticket;
// Session cache related configurations
struct {
struct {
Address addr;
uint16_t port;
std::unique_ptr<char[]> host;
} memcached;
} session_cache;
// Dynamic record sizing configurations
struct {
size_t warmup_threshold;
ev_tstamp idle_timeout;
} dyn_rec;
// OCSP realted configurations
struct {
ev_tstamp update_interval;
std::unique_ptr<char[]> fetch_ocsp_response_file;
bool disabled;
} ocsp;
// Client verification configurations
struct {
// Path to file containing CA certificate solely used for client
// certificate validation
std::unique_ptr<char[]> cacert;
bool enabled;
} client_verify;
// Client private key and certificate used in backend connections.
struct {
std::unique_ptr<char[]> private_key_file;
std::unique_ptr<char[]> cert_file;
} client;
// 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;
std::vector<AltSvc> altsvcs;
std::vector<std::pair<std::string, std::string>> add_request_headers;
std::vector<std::pair<std::string, std::string>> add_response_headers;
std::vector<unsigned char> alpn_prefs; std::vector<unsigned char> alpn_prefs;
std::vector<LogFragment> accesslog_format;
std::vector<DownstreamAddrGroup> downstream_addr_groups;
std::vector<std::string> tls_ticket_key_files;
// list of supported NPN/ALPN protocol strings in the order of // list of supported NPN/ALPN protocol strings in the order of
// preference. // preference.
std::vector<std::string> npn_list; std::vector<std::string> npn_list;
// list of supported SSL/TLS protocol strings. // list of supported SSL/TLS protocol strings.
std::vector<std::string> tls_proto_list; std::vector<std::string> tls_proto_list;
Address session_cache_memcached_addr; // Bit mask to disable SSL/TLS protocol versions. This will be
Address tls_ticket_key_memcached_addr; // passed to SSL_CTX_set_options().
long int tls_proto_mask;
std::string backend_sni_name;
std::chrono::seconds session_timeout;
std::unique_ptr<char[]> private_key_file;
std::unique_ptr<char[]> private_key_passwd;
std::unique_ptr<char[]> cert_file;
std::unique_ptr<char[]> dh_param_file;
std::unique_ptr<char[]> ciphers;
std::unique_ptr<char[]> cacert;
bool insecure;
};
struct Config {
std::vector<AltSvc> altsvcs;
std::vector<std::pair<std::string, std::string>> add_request_headers;
std::vector<std::pair<std::string, std::string>> add_response_headers;
std::vector<LogFragment> accesslog_format;
std::vector<DownstreamAddrGroup> downstream_addr_groups;
Router router; Router router;
HttpProxy downstream_http_proxy; HttpProxy downstream_http_proxy;
TLSConfig tls;
// obfuscated value used in "by" parameter of Forwarded header // obfuscated value used in "by" parameter of Forwarded header
// field. // field.
std::string forwarded_by_obfuscated; std::string forwarded_by_obfuscated;
@ -318,9 +387,7 @@ struct Config {
// field. This is only used when user defined static obfuscated // field. This is only used when user defined static obfuscated
// string is provided. // string is provided.
std::string forwarded_for_obfuscated; std::string forwarded_for_obfuscated;
std::string backend_tls_sni_name;
StringRef server_name; StringRef server_name;
std::chrono::seconds tls_session_timeout;
ev_tstamp http2_upstream_read_timeout; ev_tstamp http2_upstream_read_timeout;
ev_tstamp upstream_read_timeout; ev_tstamp upstream_read_timeout;
ev_tstamp upstream_write_timeout; ev_tstamp upstream_write_timeout;
@ -330,32 +397,16 @@ struct Config {
ev_tstamp stream_write_timeout; ev_tstamp stream_write_timeout;
ev_tstamp downstream_idle_read_timeout; ev_tstamp downstream_idle_read_timeout;
ev_tstamp listener_disable_timeout; ev_tstamp listener_disable_timeout;
ev_tstamp ocsp_update_interval;
ev_tstamp tls_ticket_key_memcached_interval;
// address of frontend connection. This could be a path to UNIX // address of frontend connection. This could be a path to UNIX
// domain socket. In this case, |host_unix| must be true. // domain socket. In this case, |host_unix| must be true.
std::unique_ptr<char[]> host; std::unique_ptr<char[]> host;
std::unique_ptr<char[]> private_key_file;
std::unique_ptr<char[]> private_key_passwd;
std::unique_ptr<char[]> cert_file;
std::unique_ptr<char[]> dh_param_file;
std::unique_ptr<char[]> pid_file; std::unique_ptr<char[]> pid_file;
std::unique_ptr<char[]> conf_path; std::unique_ptr<char[]> conf_path;
std::unique_ptr<char[]> ciphers;
std::unique_ptr<char[]> cacert;
std::unique_ptr<char[]> http2_upstream_dump_request_header_file; std::unique_ptr<char[]> http2_upstream_dump_request_header_file;
std::unique_ptr<char[]> http2_upstream_dump_response_header_file; std::unique_ptr<char[]> http2_upstream_dump_response_header_file;
// Path to file containing CA certificate solely used for client
// certificate validation
std::unique_ptr<char[]> verify_client_cacert;
std::unique_ptr<char[]> client_private_key_file;
std::unique_ptr<char[]> client_cert_file;
std::unique_ptr<char[]> accesslog_file; std::unique_ptr<char[]> accesslog_file;
std::unique_ptr<char[]> errorlog_file; std::unique_ptr<char[]> errorlog_file;
std::unique_ptr<char[]> fetch_ocsp_response_file;
std::unique_ptr<char[]> user; std::unique_ptr<char[]> user;
std::unique_ptr<char[]> session_cache_memcached_host;
std::unique_ptr<char[]> tls_ticket_key_memcached_host;
std::unique_ptr<char[]> mruby_file; std::unique_ptr<char[]> mruby_file;
FILE *http2_upstream_dump_request_header; FILE *http2_upstream_dump_request_header;
FILE *http2_upstream_dump_response_header; FILE *http2_upstream_dump_response_header;
@ -363,7 +414,6 @@ struct Config {
nghttp2_session_callbacks *http2_downstream_callbacks; nghttp2_session_callbacks *http2_downstream_callbacks;
nghttp2_option *http2_option; nghttp2_option *http2_option;
nghttp2_option *http2_client_option; nghttp2_option *http2_client_option;
const EVP_CIPHER *tls_ticket_key_cipher;
char **original_argv; char **original_argv;
char **argv; char **argv;
char *cwd; char *cwd;
@ -393,15 +443,6 @@ struct Config {
size_t max_header_fields; size_t max_header_fields;
// The index of catch-all group in downstream_addr_groups. // The index of catch-all group in downstream_addr_groups.
size_t downstream_addr_group_catch_all; size_t downstream_addr_group_catch_all;
// Maximum number of retries when getting TLS ticket key from
// mamcached, due to network error.
size_t tls_ticket_key_memcached_max_retry;
// Maximum number of consecutive error from memcached, when this
// limit reached, TLS ticket is disabled.
size_t tls_ticket_key_memcached_max_fail;
// Bit mask to disable SSL/TLS protocol versions. This will be
// passed to SSL_CTX_set_options().
long int tls_proto_mask;
// downstream protocol; this will be determined by given options. // downstream protocol; this will be determined by given options.
shrpx_proto downstream_proto; shrpx_proto downstream_proto;
// bitwise-OR of one or more of shrpx_forwarded_param values. // bitwise-OR of one or more of shrpx_forwarded_param values.
@ -424,11 +465,8 @@ struct Config {
uint16_t port; uint16_t port;
// port in http proxy URI // port in http proxy URI
uint16_t downstream_http_proxy_port; uint16_t downstream_http_proxy_port;
uint16_t session_cache_memcached_port;
uint16_t tls_ticket_key_memcached_port;
bool verbose; bool verbose;
bool daemon; bool daemon;
bool verify_client;
bool http2_proxy; bool http2_proxy;
bool http2_bridge; bool http2_bridge;
bool client_proxy; bool client_proxy;
@ -445,7 +483,6 @@ struct Config {
bool client; bool client;
// true if --client or --client-proxy are enabled. // true if --client or --client-proxy are enabled.
bool client_mode; bool client_mode;
bool insecure;
bool backend_ipv4; bool backend_ipv4;
bool backend_ipv6; bool backend_ipv6;
bool http2_no_cookie_crumbling; bool http2_no_cookie_crumbling;
@ -455,12 +492,7 @@ struct Config {
bool no_server_push; bool no_server_push;
// true if host contains UNIX domain socket path // true if host contains UNIX domain socket path
bool host_unix; bool host_unix;
bool no_ocsp;
// true if --tls-ticket-key-cipher is used
bool tls_ticket_key_cipher_given;
bool accept_proxy_protocol; bool accept_proxy_protocol;
size_t tls_dyn_rec_warmup_threshold;
ev_tstamp tls_dyn_rec_idle_timeout;
}; };
const Config *get_config(); const Config *get_config();

View File

@ -450,7 +450,7 @@ int ConnectionHandler::start_ocsp_update(const char *cert_file) {
assert(!ev_is_active(&ocsp_.chldev)); assert(!ev_is_active(&ocsp_.chldev));
char *const argv[] = { char *const argv[] = {
const_cast<char *>(get_config()->fetch_ocsp_response_file.get()), const_cast<char *>(get_config()->tls.ocsp.fetch_ocsp_response_file.get()),
const_cast<char *>(cert_file), nullptr}; const_cast<char *>(cert_file), nullptr};
char *const envp[] = {nullptr}; char *const envp[] = {nullptr};
@ -634,7 +634,7 @@ void ConnectionHandler::proceed_next_cert_ocsp() {
if (ocsp_.next == all_ssl_ctx_.size()) { if (ocsp_.next == all_ssl_ctx_.size()) {
ocsp_.next = 0; ocsp_.next = 0;
// We have updated all ocsp response, and schedule next update. // We have updated all ocsp response, and schedule next update.
ev_timer_set(&ocsp_timer_, get_config()->ocsp_update_interval, 0.); ev_timer_set(&ocsp_timer_, get_config()->tls.ocsp.update_interval, 0.);
ev_timer_start(loop_, &ocsp_timer_); ev_timer_start(loop_, &ocsp_timer_);
return; return;
} }
@ -673,7 +673,7 @@ ConnectionHandler::get_tls_ticket_key_memcached_dispatcher() const {
void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) { void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) {
if (++tls_ticket_key_memcached_get_retry_count_ >= if (++tls_ticket_key_memcached_get_retry_count_ >=
get_config()->tls_ticket_key_memcached_max_retry) { get_config()->tls.ticket.memcached.max_retry) {
LOG(WARN) << "Memcached: tls ticket get retry all failed " LOG(WARN) << "Memcached: tls ticket get retry all failed "
<< tls_ticket_key_memcached_get_retry_count_ << " times."; << tls_ticket_key_memcached_get_retry_count_ << " times.";
@ -697,7 +697,7 @@ void ConnectionHandler::on_tls_ticket_key_not_found(ev_timer *w) {
tls_ticket_key_memcached_get_retry_count_ = 0; tls_ticket_key_memcached_get_retry_count_ = 0;
if (++tls_ticket_key_memcached_fail_count_ >= if (++tls_ticket_key_memcached_fail_count_ >=
get_config()->tls_ticket_key_memcached_max_fail) { get_config()->tls.ticket.memcached.max_fail) {
LOG(WARN) << "Memcached: could not get tls ticket; disable tls ticket"; LOG(WARN) << "Memcached: could not get tls ticket; disable tls ticket";
tls_ticket_key_memcached_fail_count_ = 0; tls_ticket_key_memcached_fail_count_ = 0;
@ -742,7 +742,7 @@ void ConnectionHandler::on_tls_ticket_key_get_success(
void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get( void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get(
ev_timer *w) { ev_timer *w) {
ev_timer_set(w, get_config()->tls_ticket_key_memcached_interval, 0.); ev_timer_set(w, get_config()->tls.ticket.memcached.interval, 0.);
ev_timer_start(loop_, w); ev_timer_start(loop_, w);
} }

View File

@ -148,8 +148,8 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
: conn_(loop, -1, nullptr, worker->get_mcpool(), : conn_(loop, -1, nullptr, worker->get_mcpool(),
get_config()->downstream_write_timeout, get_config()->downstream_write_timeout,
get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb,
timeoutcb, this, get_config()->tls_dyn_rec_warmup_threshold, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls_dyn_rec_idle_timeout), get_config()->tls.dyn_rec.idle_timeout),
worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx), worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx),
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
addr_idx_(0), group_(group), index_(idx), state_(DISCONNECTED), addr_idx_(0), group_(group), index_(idx), state_(DISCONNECTED),
@ -331,8 +331,8 @@ int Http2Session::initiate_connection() {
conn_.set_ssl(ssl); conn_.set_ssl(ssl);
} }
StringRef sni_name = !get_config()->backend_tls_sni_name.empty() StringRef sni_name = !get_config()->tls.backend_sni_name.empty()
? get_config()->backend_tls_sni_name ? get_config()->tls.backend_sni_name
: downstream_addr.host; : downstream_addr.host;
if (!util::numeric_host(sni_name.c_str())) { if (!util::numeric_host(sni_name.c_str())) {
@ -1718,7 +1718,7 @@ int Http2Session::tls_handshake() {
SSLOG(INFO, this) << "SSL/TLS handshake completed"; SSLOG(INFO, this) << "SSL/TLS handshake completed";
} }
if (!get_config()->downstream_no_tls && !get_config()->insecure && if (!get_config()->downstream_no_tls && !get_config()->tls.insecure &&
check_cert() != 0) { check_cert() != 0) {
return -1; return -1;
} }

View File

@ -115,8 +115,8 @@ HttpDownstreamConnection::HttpDownstreamConnection(
: DownstreamConnection(dconn_pool), : DownstreamConnection(dconn_pool),
conn_(loop, -1, nullptr, nullptr, get_config()->downstream_write_timeout, conn_(loop, -1, nullptr, nullptr, get_config()->downstream_write_timeout,
get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb, get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb,
readcb, timeoutcb, this, get_config()->tls_dyn_rec_warmup_threshold, readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls_dyn_rec_idle_timeout), get_config()->tls.dyn_rec.idle_timeout),
ioctrl_(&conn_.rlimit), response_htp_{0}, group_(group), addr_idx_(0), ioctrl_(&conn_.rlimit), response_htp_{0}, group_(group), addr_idx_(0),
connected_(false) {} connected_(false) {}

View File

@ -70,7 +70,7 @@ namespace ssl {
namespace { namespace {
int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
void *arg) { void *arg) {
auto &prefs = get_config()->alpn_prefs; auto &prefs = get_config()->tls.alpn_prefs;
*data = prefs.data(); *data = prefs.data();
*len = prefs.size(); *len = prefs.size();
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
@ -124,13 +124,13 @@ set_alpn_prefs(const std::vector<std::string> &protos) {
namespace { namespace {
int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) {
auto config = static_cast<Config *>(user_data); auto config = static_cast<Config *>(user_data);
int len = (int)strlen(config->private_key_passwd.get()); int len = (int)strlen(config->tls.private_key_passwd.get());
if (size < len + 1) { if (size < len + 1) {
LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size;
return 0; return 0;
} }
// Copy string including last '\0'. // Copy string including last '\0'.
memcpy(buf, config->private_key_passwd.get(), len + 1); memcpy(buf, config->tls.private_key_passwd.get(), len + 1);
return len; return len;
} }
} // namespace } // namespace
@ -346,7 +346,7 @@ int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
std::copy(std::begin(key.data.name), std::end(key.data.name), key_name); std::copy(std::begin(key.data.name), std::end(key.data.name), key_name);
EVP_EncryptInit_ex(ctx, get_config()->tls_ticket_key_cipher, nullptr, EVP_EncryptInit_ex(ctx, get_config()->tls.ticket.cipher, nullptr,
key.data.enc_key.data(), iv); key.data.enc_key.data(), iv);
HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac, HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac,
nullptr); nullptr);
@ -411,7 +411,7 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
// We assume that get_config()->npn_list contains ALPN protocol // We assume that get_config()->npn_list contains ALPN protocol
// identifier sorted by preference order. So we just break when we // identifier sorted by preference order. So we just break when we
// found the first overlap. // found the first overlap.
for (const auto &target_proto_id : get_config()->npn_list) { for (const auto &target_proto_id : get_config()->tls.npn_list) {
for (auto p = in, end = in + inlen; p < end;) { for (auto p = in, end = in + inlen; p < end;) {
auto proto_id = p + 1; auto proto_id = p + 1;
auto proto_len = *p; auto proto_len = *p;
@ -477,22 +477,24 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE |
SSL_OP_SINGLE_DH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE; SSL_OP_SINGLE_DH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE;
SSL_CTX_set_options(ssl_ctx, ssl_opts | get_config()->tls_proto_mask); auto &tlsconf = get_config()->tls;
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.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);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
if (get_config()->session_cache_memcached_host) { if (tlsconf.session_cache.memcached.host) {
SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb); SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb);
SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb); SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb);
} }
SSL_CTX_set_timeout(ssl_ctx, get_config()->tls_session_timeout.count()); SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count());
const char *ciphers; const char *ciphers;
if (get_config()->ciphers) { if (tlsconf.ciphers) {
ciphers = get_config()->ciphers.get(); ciphers = tlsconf.ciphers.get();
} else { } else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
} }
@ -525,9 +527,9 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
#endif // OPENSSL_NO_EC #endif // OPENSSL_NO_EC
if (get_config()->dh_param_file) { if (tlsconf.dh_param_file) {
// Read DH parameters from file // Read DH parameters from file
auto bio = BIO_new_file(get_config()->dh_param_file.get(), "r"); auto bio = BIO_new_file(tlsconf.dh_param_file.get(), "r");
if (bio == nullptr) { if (bio == nullptr) {
LOG(FATAL) << "BIO_new_file() failed: " LOG(FATAL) << "BIO_new_file() failed: "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
@ -546,7 +548,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (get_config()->private_key_passwd) { if (tlsconf.private_key_passwd) {
SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config()); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config());
} }
@ -576,14 +578,13 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
if (get_config()->verify_client) { if (tlsconf.client_verify.enabled) {
if (get_config()->verify_client_cacert) { if (tlsconf.client_verify.cacert) {
if (SSL_CTX_load_verify_locations( if (SSL_CTX_load_verify_locations(
ssl_ctx, get_config()->verify_client_cacert.get(), nullptr) != ssl_ctx, tlsconf.client_verify.cacert.get(), nullptr) != 1) {
1) {
LOG(FATAL) << "Could not load trusted ca certificates from " LOG(FATAL) << "Could not load trusted ca certificates from "
<< get_config()->verify_client_cacert.get() << ": " << tlsconf.client_verify.cacert.get() << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
@ -591,11 +592,10 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
// error even though it returns success. See // error even though it returns success. See
// http://forum.nginx.org/read.php?29,242540 // http://forum.nginx.org/read.php?29,242540
ERR_clear_error(); ERR_clear_error();
auto list = auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.get());
SSL_load_client_CA_file(get_config()->verify_client_cacert.get());
if (!list) { if (!list) {
LOG(FATAL) << "Could not load ca certificates from " LOG(FATAL) << "Could not load ca certificates from "
<< get_config()->verify_client_cacert.get() << ": " << tlsconf.client_verify.cacert.get() << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
@ -656,11 +656,13 @@ SSL_CTX *create_ssl_client_context(
SSL_OP_NO_COMPRESSION | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
SSL_CTX_set_options(ssl_ctx, ssl_opts | get_config()->tls_proto_mask); auto &tlsconf = get_config()->tls;
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
const char *ciphers; const char *ciphers;
if (get_config()->ciphers) { if (tlsconf.ciphers) {
ciphers = get_config()->ciphers.get(); ciphers = tlsconf.ciphers.get();
} else { } else {
ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST;
} }
@ -678,31 +680,31 @@ SSL_CTX *create_ssl_client_context(
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
} }
if (get_config()->cacert) { if (tlsconf.cacert) {
if (SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert.get(), if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.get(), nullptr) !=
nullptr) != 1) { 1) {
LOG(FATAL) << "Could not load trusted ca certificates from " LOG(FATAL) << "Could not load trusted ca certificates from "
<< get_config()->cacert.get() << ": " << tlsconf.cacert.get() << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
} }
if (get_config()->client_private_key_file) { if (tlsconf.client.private_key_file) {
#ifndef HAVE_NEVERBLEED #ifndef HAVE_NEVERBLEED
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
get_config()->client_private_key_file.get(), tlsconf.client.private_key_file.get(),
SSL_FILETYPE_PEM) != 1) { SSL_FILETYPE_PEM) != 1) {
LOG(FATAL) << "Could not load client private key from " LOG(FATAL) << "Could not load client private key from "
<< get_config()->client_private_key_file.get() << ": " << tlsconf.client.private_key_file.get() << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
#else // HAVE_NEVERBLEED #else // HAVE_NEVERBLEED
std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf; std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf;
if (neverbleed_load_private_key_file( if (neverbleed_load_private_key_file(nb, ssl_ctx,
nb, ssl_ctx, get_config()->client_private_key_file.get(), tlsconf.client.private_key_file.get(),
errbuf.data()) != 1) { errbuf.data()) != 1) {
LOG(FATAL) << "neverbleed_load_private_key_file failed: " LOG(FATAL) << "neverbleed_load_private_key_file failed: "
<< errbuf.data(); << errbuf.data();
@ -710,12 +712,12 @@ SSL_CTX *create_ssl_client_context(
} }
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
} }
if (get_config()->client_cert_file) { if (tlsconf.client.cert_file) {
if (SSL_CTX_use_certificate_chain_file( if (SSL_CTX_use_certificate_chain_file(
ssl_ctx, get_config()->client_cert_file.get()) != 1) { ssl_ctx, tlsconf.client.cert_file.get()) != 1) {
LOG(FATAL) << "Could not load client certificate from " LOG(FATAL) << "Could not load client certificate from "
<< get_config()->client_cert_file.get() << ": " << tlsconf.client.cert_file.get() << ": "
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
DIE(); DIE();
} }
@ -971,8 +973,10 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) {
<< X509_verify_cert_error_string(verify_res); << X509_verify_cert_error_string(verify_res);
return -1; return -1;
} }
auto hostname = !get_config()->backend_tls_sni_name.empty()
? StringRef(get_config()->backend_tls_sni_name) auto &backend_sni_name = get_config()->tls.backend_sni_name;
auto hostname = !backend_sni_name.empty() ? StringRef(backend_sni_name)
: StringRef(addr->host); : StringRef(addr->host);
if (verify_hostname(cert, hostname.c_str(), hostname.size(), &addr->addr) != if (verify_hostname(cert, hostname.c_str(), hostname.size(), &addr->addr) !=
0) { 0) {
@ -1214,8 +1218,10 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
return nullptr; return nullptr;
} }
auto ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file.get(), auto &tlsconf = get_config()->tls;
get_config()->cert_file.get()
auto ssl_ctx = ssl::create_ssl_context(tlsconf.private_key_file.get(),
tlsconf.cert_file.get()
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
, ,
nb nb
@ -1224,7 +1230,7 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
all_ssl_ctx.push_back(ssl_ctx); all_ssl_ctx.push_back(ssl_ctx);
if (get_config()->subcerts.empty()) { if (tlsconf.subcerts.empty()) {
return ssl_ctx; return ssl_ctx;
} }
@ -1234,7 +1240,7 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
return ssl_ctx; return ssl_ctx;
} }
for (auto &keycert : get_config()->subcerts) { for (auto &keycert : tlsconf.subcerts) {
auto ssl_ctx = auto ssl_ctx =
ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str() ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str()
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
@ -1250,8 +1256,8 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
} }
} }
if (ssl::cert_lookup_tree_add_cert_from_file( if (ssl::cert_lookup_tree_add_cert_from_file(cert_tree, ssl_ctx,
cert_tree, ssl_ctx, get_config()->cert_file.get()) == -1) { tlsconf.cert_file.get()) == -1) {
LOG(FATAL) << "Failed to add default certificate."; LOG(FATAL) << "Failed to add default certificate.";
DIE(); DIE();
} }
@ -1284,7 +1290,7 @@ SSL_CTX *setup_client_ssl_context(
} }
CertLookupTree *create_cert_lookup_tree() { CertLookupTree *create_cert_lookup_tree() {
if (get_config()->upstream_no_tls || get_config()->subcerts.empty()) { if (get_config()->upstream_no_tls || get_config()->tls.subcerts.empty()) {
return nullptr; return nullptr;
} }
return new ssl::CertLookupTree(); return new ssl::CertLookupTree();

View File

@ -83,9 +83,11 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.); ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.);
mcpool_clear_timer_.data = this; mcpool_clear_timer_.data = this;
if (get_config()->session_cache_memcached_host) { auto &session_cacheconf = get_config()->tls.session_cache;
if (session_cacheconf.memcached.host) {
session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>( session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
&get_config()->session_cache_memcached_addr, loop); &session_cacheconf.memcached.addr, loop);
} }
if (get_config()->downstream_proto == PROTO_HTTP2) { if (get_config()->downstream_proto == PROTO_HTTP2) {

View File

@ -170,7 +170,7 @@ void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) {
namespace { namespace {
int generate_ticket_key(TicketKey &ticket_key) { int generate_ticket_key(TicketKey &ticket_key) {
ticket_key.cipher = get_config()->tls_ticket_key_cipher; ticket_key.cipher = get_config()->tls.ticket.cipher;
ticket_key.hmac = EVP_sha256(); ticket_key.hmac = EVP_sha256();
ticket_key.hmac_keylen = EVP_MD_size(ticket_key.hmac); ticket_key.hmac_keylen = EVP_MD_size(ticket_key.hmac);
@ -217,7 +217,7 @@ void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto max_tickets = auto max_tickets =
static_cast<size_t>(std::chrono::duration_cast<std::chrono::hours>( static_cast<size_t>(std::chrono::duration_cast<std::chrono::hours>(
get_config()->tls_session_timeout).count()); get_config()->tls.session_timeout).count());
new_keys.resize(std::min(max_tickets, old_keys.size() + 1)); new_keys.resize(std::min(max_tickets, old_keys.size() + 1));
std::copy_n(std::begin(old_keys), new_keys.size() - 1, std::copy_n(std::begin(old_keys), new_keys.size() - 1,
@ -297,14 +297,16 @@ void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
auto end = p + value.size(); auto end = p + value.size();
p += 4; p += 4;
auto &ticketconf = get_config()->tls.ticket;
size_t expectedlen; size_t expectedlen;
size_t enc_keylen; size_t enc_keylen;
size_t hmac_keylen; size_t hmac_keylen;
if (get_config()->tls_ticket_key_cipher == EVP_aes_128_cbc()) { if (ticketconf.cipher == EVP_aes_128_cbc()) {
expectedlen = 48; expectedlen = 48;
enc_keylen = 16; enc_keylen = 16;
hmac_keylen = 16; hmac_keylen = 16;
} else if (get_config()->tls_ticket_key_cipher == EVP_aes_256_cbc()) { } else if (ticketconf.cipher == EVP_aes_256_cbc()) {
expectedlen = 80; expectedlen = 80;
enc_keylen = 32; enc_keylen = 32;
hmac_keylen = 32; hmac_keylen = 32;
@ -335,7 +337,7 @@ void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
return; return;
} }
auto key = TicketKey(); auto key = TicketKey();
key.cipher = get_config()->tls_ticket_key_cipher; key.cipher = ticketconf.cipher;
key.hmac = EVP_sha256(); key.hmac = EVP_sha256();
key.hmac_keylen = hmac_keylen; key.hmac_keylen = hmac_keylen;
@ -423,10 +425,11 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
ev_timer renew_ticket_key_timer; ev_timer renew_ticket_key_timer;
if (!get_config()->upstream_no_tls) { if (!get_config()->upstream_no_tls) {
if (get_config()->tls_ticket_key_memcached_host) { auto &ticketconf = get_config()->tls.ticket;
if (ticketconf.memcached.host) {
conn_handler.set_tls_ticket_key_memcached_dispatcher( conn_handler.set_tls_ticket_key_memcached_dispatcher(
make_unique<MemcachedDispatcher>( make_unique<MemcachedDispatcher>(&ticketconf.memcached.addr, loop));
&get_config()->tls_ticket_key_memcached_addr, loop));
ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0., ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
0.); 0.);
@ -435,8 +438,8 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0); memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
} else { } else {
bool auto_tls_ticket_key = true; bool auto_tls_ticket_key = true;
if (!get_config()->tls_ticket_key_files.empty()) { if (!ticketconf.files.empty()) {
if (!get_config()->tls_ticket_key_cipher_given) { if (!ticketconf.cipher_given) {
LOG(WARN) LOG(WARN)
<< "It is strongly recommended to specify " << "It is strongly recommended to specify "
"--tls-ticket-key-cipher=aes-128-cbc (or " "--tls-ticket-key-cipher=aes-128-cbc (or "
@ -446,8 +449,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
"becomes aes-256-cbc"; "becomes aes-256-cbc";
} }
auto ticket_keys = read_tls_ticket_key_file( auto ticket_keys = read_tls_ticket_key_file(
get_config()->tls_ticket_key_files, ticketconf.files, ticketconf.cipher, EVP_sha256());
get_config()->tls_ticket_key_cipher, EVP_sha256());
if (!ticket_keys) { if (!ticket_keys) {
LOG(WARN) << "Use internal session ticket key generator"; LOG(WARN) << "Use internal session ticket key generator";
} else { } else {
@ -512,7 +514,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
ipcev.data = &conn_handler; ipcev.data = &conn_handler;
ev_io_start(loop, &ipcev); ev_io_start(loop, &ipcev);
if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { if (!get_config()->upstream_no_tls && !get_config()->tls.ocsp.disabled) {
conn_handler.proceed_next_cert_ocsp(); conn_handler.proceed_next_cert_ocsp();
} }