nghttpx: Specify TLS protocol by version range
This commit deprecates --tls-proto-list option, and adds 2 new options: --tls-min-proto-version and --tls-max-proto-version to specify minimum and maximum protocol version respectively. Versions between the two are enabled. The deprecated --tls-proto-list has empty default value, and acts like enabling only specific protocol versions in the range for now.
This commit is contained in:
parent
001d45efad
commit
b36e53cccd
|
@ -158,6 +158,8 @@ OPTIONS = [
|
|||
"client-no-http2-cipher-black-list",
|
||||
"client-ciphers",
|
||||
"accesslog-write-early",
|
||||
"tls-min-proto-version",
|
||||
"tls-max-proto-version",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
|
62
src/shrpx.cc
62
src/shrpx.cc
|
@ -1368,7 +1368,8 @@ constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14,"
|
|||
} // namespace
|
||||
|
||||
namespace {
|
||||
constexpr auto DEFAULT_TLS_PROTO_LIST = StringRef::from_lit("TLSv1.2,TLSv1.1");
|
||||
constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.1");
|
||||
constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
@ -1426,6 +1427,8 @@ void fill_default_config(Config *config) {
|
|||
tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
|
||||
tlsconf.client.ciphers =
|
||||
StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
|
||||
tlsconf.min_proto_version = TLS1_1_VERSION;
|
||||
tlsconf.max_proto_version = TLS1_2_VERSION;
|
||||
#if OPENSSL_1_1_API
|
||||
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
|
||||
#else // !OPENSSL_1_1_API
|
||||
|
@ -2054,18 +2057,26 @@ SSL/TLS:
|
|||
--client-cert-file=<PATH>
|
||||
Path to file that contains client certificate used in
|
||||
backend client authentication.
|
||||
--tls-proto-list=<LIST>
|
||||
Comma delimited list of SSL/TLS protocol to be enabled.
|
||||
The following protocols are available: TLSv1.2, TLSv1.1
|
||||
and TLSv1.0. The name matching is done in
|
||||
case-insensitive manner. The parameter must be
|
||||
delimited by a single comma only and any white spaces
|
||||
are treated as a part of protocol string. If the
|
||||
protocol list advertised by client does not overlap this
|
||||
list, you will receive the error message "unknown
|
||||
protocol".
|
||||
--tls-min-proto-version=<VER>
|
||||
Specify minimum SSL/TLS protocol. The following
|
||||
protocols are available: TLSv1.2, TLSv1.1 and TLSv1.0.
|
||||
The name matching is done in case-insensitive manner.
|
||||
The versions between --tls-min-proto-version and
|
||||
--tls-max-proto-version are enabled. If the protocol
|
||||
list advertised by client does not overlap this range,
|
||||
you will receive the error message "unknown protocol".
|
||||
Default: )"
|
||||
<< DEFAULT_TLS_PROTO_LIST << R"(
|
||||
<< DEFAULT_TLS_MIN_PROTO_VERSION << R"(
|
||||
--tls-max-proto-version=<VER>
|
||||
Specify maximum SSL/TLS protocol. The following
|
||||
protocols are available: TLSv1.2, TLSv1.1 and TLSv1.0.
|
||||
The name matching is done in case-insensitive manner.
|
||||
The versions between --tls-min-proto-version and
|
||||
--tls-max-proto-version are enabled. If the protocol
|
||||
list advertised by client does not overlap this range,
|
||||
you will receive the error message "unknown protocol".
|
||||
Default: )"
|
||||
<< DEFAULT_TLS_MAX_PROTO_VERSION << R"(
|
||||
--tls-ticket-key-file=<PATH>
|
||||
Path to file that contains random data to construct TLS
|
||||
session ticket parameters. If aes-128-cbc is given in
|
||||
|
@ -2717,11 +2728,18 @@ int process_options(Config *config,
|
|||
if (tlsconf.npn_list.empty()) {
|
||||
tlsconf.npn_list = util::split_str(DEFAULT_NPN_LIST, ',');
|
||||
}
|
||||
if (tlsconf.tls_proto_list.empty()) {
|
||||
tlsconf.tls_proto_list = util::split_str(DEFAULT_TLS_PROTO_LIST, ',');
|
||||
|
||||
if (!tlsconf.tls_proto_list.empty()) {
|
||||
tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
|
||||
}
|
||||
|
||||
tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
|
||||
// TODO We depends on the ordering of protocol version macro in
|
||||
// OpenSSL.
|
||||
if (tlsconf.min_proto_version > tlsconf.max_proto_version) {
|
||||
LOG(ERROR) << "tls-max-proto-version must be equal to or larger than "
|
||||
"tls-min-proto-version";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssl::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
|
||||
return -1;
|
||||
|
@ -3216,6 +3234,10 @@ int main(int argc, char **argv) {
|
|||
&flag, 149},
|
||||
{SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
|
||||
{SHRPX_OPT_ACCESSLOG_WRITE_EARLY.c_str(), no_argument, &flag, 151},
|
||||
{SHRPX_OPT_TLS_MIN_PROTO_VERSION.c_str(), required_argument, &flag,
|
||||
152},
|
||||
{SHRPX_OPT_TLS_MAX_PROTO_VERSION.c_str(), required_argument, &flag,
|
||||
153},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -3928,6 +3950,16 @@ int main(int argc, char **argv) {
|
|||
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 152:
|
||||
// --tls-min-proto-version
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MIN_PROTO_VERSION,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 153:
|
||||
// --tls-max-proto-version
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -638,6 +638,21 @@ int parse_duration(ev_tstamp *dest, const StringRef &opt,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int parse_tls_proto_version(int &dest, const StringRef &opt,
|
||||
const StringRef &optarg) {
|
||||
auto v = ssl::proto_version_from_string(optarg);
|
||||
if (v == -1) {
|
||||
LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dest = v;
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct MemcachedConnectionParams {
|
||||
bool tls;
|
||||
};
|
||||
|
@ -1786,6 +1801,14 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (util::strieq_l("tls-max-proto-versio", name, 20)) {
|
||||
return SHRPX_OPTID_TLS_MAX_PROTO_VERSION;
|
||||
}
|
||||
if (util::strieq_l("tls-min-proto-versio", name, 20)) {
|
||||
return SHRPX_OPTID_TLS_MIN_PROTO_VERSION;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
|
||||
return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
|
||||
|
@ -2710,6 +2733,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_TLS_PROTO_LIST: {
|
||||
LOG(WARN) << opt << ": deprecated. Use tls-min-proto-version and "
|
||||
"tls-max-proto-version instead.";
|
||||
auto list = util::split_str(optarg, ',');
|
||||
config->tls.tls_proto_list.resize(list.size());
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
|
@ -3327,6 +3352,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
config->logging.access.write_early = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_MIN_PROTO_VERSION:
|
||||
return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg);
|
||||
case SHRPX_OPTID_TLS_MAX_PROTO_VERSION:
|
||||
return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg);
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
|
|
|
@ -327,6 +327,10 @@ constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST =
|
|||
constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers");
|
||||
constexpr auto SHRPX_OPT_ACCESSLOG_WRITE_EARLY =
|
||||
StringRef::from_lit("accesslog-write-early");
|
||||
constexpr auto SHRPX_OPT_TLS_MIN_PROTO_VERSION =
|
||||
StringRef::from_lit("tls-min-proto-version");
|
||||
constexpr auto SHRPX_OPT_TLS_MAX_PROTO_VERSION =
|
||||
StringRef::from_lit("tls-max-proto-version");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
|
@ -594,6 +598,10 @@ struct TLSConfig {
|
|||
StringRef ciphers;
|
||||
StringRef ecdh_curves;
|
||||
StringRef cacert;
|
||||
// The minimum and maximum TLS version. These values are defined in
|
||||
// OpenSSL header file.
|
||||
int min_proto_version;
|
||||
int max_proto_version;
|
||||
bool insecure;
|
||||
bool no_http2_cipher_black_list;
|
||||
};
|
||||
|
@ -1020,6 +1028,8 @@ enum {
|
|||
SHRPX_OPTID_SYSLOG_FACILITY,
|
||||
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
|
||||
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
||||
SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
|
||||
SHRPX_OPTID_TLS_MIN_PROTO_VERSION,
|
||||
SHRPX_OPTID_TLS_PROTO_LIST,
|
||||
SHRPX_OPTID_TLS_SCT_DIR,
|
||||
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
|
||||
|
|
|
@ -639,6 +639,55 @@ long int create_tls_proto_mask(const std::vector<StringRef> &tls_proto_list) {
|
|||
return res;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL_IS_BORINGSSL)
|
||||
namespace {
|
||||
int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) {
|
||||
SSL_CTX_set_min_version(ssl_ctx, min);
|
||||
SSL_CTX_set_max_version(ssl_ctx, max);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
#elif OPENSSL_1_1_API
|
||||
namespace {
|
||||
int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) {
|
||||
if (SSL_CTX_set_min_proto_version(ssl_ctx, min) != 1 ||
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, max) != 1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
#else // !OPENSSL_1_1_API
|
||||
namespace {
|
||||
int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) {
|
||||
long int opts = 0;
|
||||
|
||||
// TODO We depends on the ordering of protocol version macro in
|
||||
// OpenSSL.
|
||||
if (min > TLS1_VERSION) {
|
||||
opts |= SSL_OP_NO_TLSv1;
|
||||
}
|
||||
if (min > TLS1_1_VERSION) {
|
||||
opts |= SSL_OP_NO_TLSv1_1;
|
||||
}
|
||||
if (min > TLS1_2_VERSION) {
|
||||
opts |= SSL_OP_NO_TLSv1_2;
|
||||
}
|
||||
|
||||
if (max < TLS1_2_VERSION) {
|
||||
opts |= SSL_OP_NO_TLSv1_2;
|
||||
}
|
||||
if (max < TLS1_1_VERSION) {
|
||||
opts |= SSL_OP_NO_TLSv1_1;
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ssl_ctx, opts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
#endif // !OPENSSL_1_1_API
|
||||
|
||||
SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
|
||||
const std::vector<uint8_t> &sct_data
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
|
@ -663,6 +712,13 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
|
|||
|
||||
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
|
||||
|
||||
if (ssl_ctx_set_proto_versions(ssl_ctx, tlsconf.min_proto_version,
|
||||
tlsconf.max_proto_version) != 0) {
|
||||
LOG(FATAL) << "Could not set TLS protocol version: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
DIE();
|
||||
}
|
||||
|
||||
const unsigned char sid_ctx[] = "shrpx";
|
||||
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);
|
||||
|
@ -897,6 +953,13 @@ SSL_CTX *create_ssl_client_context(
|
|||
|
||||
SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
|
||||
|
||||
if (ssl_ctx_set_proto_versions(ssl_ctx, tlsconf.min_proto_version,
|
||||
tlsconf.max_proto_version) != 0) {
|
||||
LOG(FATAL) << "Could not set TLS protocol version: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
DIE();
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.client.ciphers.c_str()) == 0) {
|
||||
LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.client.ciphers
|
||||
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
|
||||
|
@ -1678,6 +1741,19 @@ SSL_SESSION *reuse_tls_session(const TLSSessionCache &cache) {
|
|||
return d2i_SSL_SESSION(nullptr, &p, cache.session_data.size());
|
||||
}
|
||||
|
||||
int proto_version_from_string(const StringRef &v) {
|
||||
if (util::strieq_l("TLSv1.2", v)) {
|
||||
return TLS1_2_VERSION;
|
||||
}
|
||||
if (util::strieq_l("TLSv1.1", v)) {
|
||||
return TLS1_1_VERSION;
|
||||
}
|
||||
if (util::strieq_l("TLSv1.0", v)) {
|
||||
return TLS1_VERSION;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace ssl
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -258,6 +258,11 @@ SSL_SESSION *reuse_tls_session(const TLSSessionCache &addr);
|
|||
// the returned object using X509_free().
|
||||
X509 *load_certificate(const char *filename);
|
||||
|
||||
// Returns TLS version from |v|. The returned value is defined in
|
||||
// OpenSSL header file. This function returns -1 if |v| is not valid
|
||||
// TLS version string.
|
||||
int proto_version_from_string(const StringRef &v);
|
||||
|
||||
} // namespace ssl
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
Loading…
Reference in New Issue