Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions iocore/net/P_SSLUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ void setClientCertLevel(SSL *ssl, uint8_t certLevel);
void setClientCertCACerts(SSL *ssl, const char *file, const char *dir);
void setTLSValidProtocols(SSL *ssl, unsigned long proto_mask, unsigned long max_mask);

// Helper functions to retrieve sni name or ip address from a SSL object
// Used as part of the lookup key into the origin server session cache
std::string get_sni_addr(SSL *ssl);

namespace ssl
{
namespace detail
Expand Down
35 changes: 12 additions & 23 deletions iocore/net/SSLClientUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -159,32 +159,21 @@ ssl_client_cert_callback(SSL *ssl, void * /*arg*/)
static int
ssl_new_session_callback(SSL *ssl, SSL_SESSION *sess)
{
const char *tlsext_host_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (tlsext_host_name) {
std::string sni(tlsext_host_name);
origin_sess_cache->insert_session(sni, sess);
std::string sni_addr = get_sni_addr(ssl);
if (!sni_addr.empty()) {
SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
std::stringstream ctx_ss;
ctx_ss << static_cast<const void *>(ctx);
std::string lookup_key;
ts::bwprint(lookup_key, "{}:{}", sni_addr.c_str(), ctx_ss.str().c_str());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't do %p with bwprint?

origin_sess_cache->insert_session(lookup_key, sess);
return 1;
} else {
int sock_fd = SSL_get_fd(ssl);
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
std::string addr_s;
if (sock_fd >= 0) {
getpeername(sock_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len);
if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
addr_s.assign(reinterpret_cast<char *>(&addr), addr_len);
origin_sess_cache->insert_session(addr_s, sess);
} else {
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "unknown address family: %d", addr.ss_family);
}
return 0;
}
} else {
return 0;
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "Failed to fetch SNI/IP.");
}
return 0;
}

return 1;
}

SSL_CTX *
Expand Down
8 changes: 4 additions & 4 deletions iocore/net/SSLSessionCache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void
SSLOriginSessionCache::insert_session(std::string lookup_key, SSL_SESSION *sess)
{
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "insert session: %lx = %p", std::hash<std::string>{}(lookup_key), sess);
Debug("ssl.origin_session_cache", "insert session: %s = %p", lookup_key.c_str(), sess);
}

std::unique_lock lock(mutex);
Expand All @@ -330,7 +330,7 @@ void
SSLOriginSessionCache::remove_session(std::string lookup_key)
{
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "remove session: %lx", std::hash<std::string>{}(lookup_key));
Debug("ssl.origin_session_cache", "remove session: %s", lookup_key.c_str());
}

std::unique_lock lock(mutex);
Expand All @@ -345,7 +345,7 @@ SSL_SESSION *
SSLOriginSessionCache::get_session(std::string lookup_key)
{
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "get session: %lx", std::hash<std::string>{}(lookup_key));
Debug("ssl.origin_session_cache", "get session: %s", lookup_key.c_str());
}

std::shared_lock lock(mutex);
Expand All @@ -365,7 +365,7 @@ SSLOriginSessionCache::remove_oldest_session(const std::unique_lock<std::shared_
auto node = origin_sessions.begin();

if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "remove oldest session: %lx = %p", std::hash<std::string>{}(node->first), node->second);
Debug("ssl.origin_session_cache", "remove oldest session: %s = %p", node->first.c_str(), node->second);
}

SSL_SESSION_free(node->second);
Expand Down
61 changes: 41 additions & 20 deletions iocore/net/SSLUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1994,28 +1994,22 @@ SSLConnect(SSL *ssl)
ERR_clear_error();

SSL_SESSION *sess = SSL_get_session(ssl);
std::string lookup_key;
if (!sess && SSLConfigParams::origin_session_cache == 1 && SSLConfigParams::origin_session_cache_size > 0) {
const char *tlsext_host_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (tlsext_host_name) {
lookup_key.assign(tlsext_host_name);
std::string sni_addr = get_sni_addr(ssl);
if (!sni_addr.empty()) {
SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
std::stringstream ctx_ss;
ctx_ss << static_cast<const void *>(ctx);
std::string lookup_key;
ts::bwprint(lookup_key, "{}:{}", sni_addr.c_str(), ctx_ss.str().c_str());

Debug("ssl.origin_session_cache", "origin session cache lookup key = %s", lookup_key.c_str());

sess = origin_sess_cache->get_session(lookup_key);
} else {
int sock_fd = SSL_get_fd(ssl);
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (sock_fd >= 0) {
getpeername(sock_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len);
if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
lookup_key.assign(reinterpret_cast<char *>(&addr), addr_len);
sess = origin_sess_cache->get_session(lookup_key);
}
if (sess) {
SSL_set_session(ssl, sess);
}
}

if (sess) {
SSL_set_session(ssl, sess);
}
}

int ret = SSL_connect(ssl);
Expand All @@ -2024,11 +2018,11 @@ SSLConnect(SSL *ssl)
if (sess && SSL_session_reused(ssl)) {
SSL_INCREMENT_DYN_STAT(ssl_origin_session_reused_count);
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "reused session to origin: %lx = %p", std::hash<std::string>{}(lookup_key), sess);
Debug("ssl.origin_session_cache", "reused session to origin server = %p", sess);
}
} else {
if (is_debug_tag_set("ssl.origin_session_cache")) {
Debug("ssl.origin_session_cache", "new session to origin: %lx = %p", std::hash<std::string>{}(lookup_key), sess);
Debug("ssl.origin_session_cache", "new session to origin server = %p", sess);
}
}
return SSL_ERROR_NONE;
Expand All @@ -2044,6 +2038,33 @@ SSLConnect(SSL *ssl)
return ssl_error;
}

std::string
get_sni_addr(SSL *ssl)
{
std::string sni_addr;

if (ssl != nullptr) {
const char *sni_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (sni_name) {
sni_addr.assign(sni_name);
} else {
int sock_fd = SSL_get_fd(ssl);
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (sock_fd >= 0) {
getpeername(sock_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len);
if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
char ip_addr[INET6_ADDRSTRLEN];
ats_ip_ntop(reinterpret_cast<sockaddr *>(&addr), ip_addr, INET6_ADDRSTRLEN);
sni_addr.assign(ip_addr);
}
}
}
}

return sni_addr;
}

/**
* Process the config to pull out the list of file names, and process the certs to get the list
* of subject and sni names. Thanks to dual cert configurations, there may be mulitple files of each type.
Expand Down
77 changes: 26 additions & 51 deletions tests/gold_tests/tls/tls_origin_session_reuse.test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
ts2 = Test.MakeATSProcess("ts2", select_ports=True, enable_tls=True)
ts3 = Test.MakeATSProcess("ts3", select_ports=True, enable_tls=True)
ts4 = Test.MakeATSProcess("ts4", select_ports=True, enable_tls=True)
ts5 = Test.MakeATSProcess("ts5", select_ports=True, enable_tls=True)
server = Test.MakeOriginServer("server")

# Add info the origin server responses
Expand All @@ -51,24 +50,19 @@
ts3.addSSLfile("ssl/server.key")
ts4.addSSLfile("ssl/server.pem")
ts4.addSSLfile("ssl/server.key")
ts5.addSSLfile("ssl/server.pem")
ts5.addSSLfile("ssl/server.key")

ts1.Disk.remap_config.AddLine(
'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
)
ts2.Disk.remap_config.AddLine(
'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
)
ts3.Disk.remap_config.AddLines([
'map /ts1 https://127.0.0.1:{0}'.format(ts1.Variables.ssl_port),
'map /ts2 https://127.0.0.1:{0}'.format(ts2.Variables.ssl_port)
ts2.Disk.remap_config.AddLines([
'map /reuse_session https://127.0.0.1:{0}'.format(ts1.Variables.ssl_port),
'map /remove_oldest https://127.0.1.1:{0}'.format(ts1.Variables.ssl_port)
])
ts4.Disk.remap_config.AddLine(
ts3.Disk.remap_config.AddLine(
'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
)
ts5.Disk.remap_config.AddLine(
'map / https://127.0.0.1:{0}'.format(ts4.Variables.ssl_port)
ts4.Disk.remap_config.AddLine(
'map / https://127.0.0.1:{0}'.format(ts3.Variables.ssl_port)
)

ts1.Disk.ssl_multicert_config.AddLine(
Expand All @@ -83,9 +77,6 @@
ts4.Disk.ssl_multicert_config.AddLine(
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
)
ts5.Disk.ssl_multicert_config.AddLine(
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
)

ts1.Disk.records_config.update({
'proxy.config.http.cache.http': 0,
Expand All @@ -105,6 +96,8 @@
})
ts2.Disk.records_config.update({
'proxy.config.http.cache.http': 0,
'proxy.config.diags.debug.enabled': 1,
'proxy.config.diags.debug.tags': 'ssl.origin_session_cache',
'proxy.config.ssl.server.cert.path': '{0}'.format(ts2.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts2.Variables.SSLDir),
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
Expand All @@ -121,8 +114,6 @@
})
ts3.Disk.records_config.update({
'proxy.config.http.cache.http': 0,
'proxy.config.diags.debug.enabled': 1,
'proxy.config.diags.debug.tags': 'ssl.origin_session_cache',
'proxy.config.ssl.server.cert.path': '{0}'.format(ts3.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts3.Variables.SSLDir),
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
Expand All @@ -138,27 +129,11 @@
'proxy.config.ssl.origin_session_cache.size': 1
})
ts4.Disk.records_config.update({
'proxy.config.http.cache.http': 0,
'proxy.config.ssl.server.cert.path': '{0}'.format(ts4.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts4.Variables.SSLDir),
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
'proxy.config.exec_thread.autoconfig.scale': 1.0,
'proxy.config.ssl.session_cache': 2,
'proxy.config.ssl.session_cache.size': 4096,
'proxy.config.ssl.session_cache.num_buckets': 256,
'proxy.config.ssl.session_cache.skip_cache_on_bucket_contention': 0,
'proxy.config.ssl.session_cache.timeout': 0,
'proxy.config.ssl.session_cache.auto_clear': 1,
'proxy.config.ssl.server.session_ticket.enable': 1,
'proxy.config.ssl.origin_session_cache': 1,
'proxy.config.ssl.origin_session_cache.size': 1
})
ts5.Disk.records_config.update({
'proxy.config.http.cache.http': 0,
'proxy.config.diags.debug.enabled': 1,
'proxy.config.diags.debug.tags': 'ssl.origin_session_cache',
'proxy.config.ssl.server.cert.path': '{0}'.format(ts5.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts5.Variables.SSLDir),
'proxy.config.ssl.server.cert.path': '{0}'.format(ts4.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts4.Variables.SSLDir),
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
'proxy.config.exec_thread.autoconfig.scale': 1.0,
'proxy.config.ssl.session_cache': 2,
Expand All @@ -173,34 +148,34 @@
})

tr = Test.AddTestRun('new session then reuse')
tr.Processes.Default.Command = 'curl https://127.0.0.1:{0}/ts1 -k && curl https://127.0.0.1:{0}/ts1 -k'.format(
ts3.Variables.ssl_port)
tr.Processes.Default.Command = 'curl https://127.0.0.1:{0}/reuse_session -k && curl https://127.0.0.1:{0}/reuse_session -k'.format(
ts2.Variables.ssl_port)
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.StartBefore(server)
tr.Processes.Default.StartBefore(ts1)
tr.Processes.Default.StartBefore(ts3)
tr.Processes.Default.StartBefore(ts2)
tr.Processes.Default.Streams.All = Testers.ContainsExpression('curl test', 'Making sure the basics still work')
ts3.Streams.All = Testers.ContainsExpression('new session to origin', '')
ts3.Streams.All += Testers.ContainsExpression('reused session to origin', '')
ts2.Streams.All = Testers.ContainsExpression('new session to origin', '')
ts2.Streams.All += Testers.ContainsExpression('reused session to origin', '')
tr.StillRunningAfter = server
tr.StillRunningAfter += ts3
tr.StillRunningAfter += ts1
tr.StillRunningAfter += ts2

tr = Test.AddTestRun('remove oldest session, new session then reuse')
tr.Processes.Default.Command = 'curl https://127.0.0.1:{0}/ts2 -k && curl https://127.0.0.1:{0}/ts2 -k'.format(
ts3.Variables.ssl_port)
tr.Processes.Default.Command = 'curl https://127.0.0.1:{0}/remove_oldest -k && curl https://127.0.0.1:{0}/remove_oldest -k'.format(
ts2.Variables.ssl_port)
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.StartBefore(ts2)
tr.Processes.Default.Streams.All = Testers.ContainsExpression('curl test', 'Making sure the basics still work')
ts3.Streams.All = Testers.ContainsExpression('remove oldest session', '')
ts3.Streams.All += Testers.ContainsExpression('new session to origin', '')
ts3.Streams.All += Testers.ContainsExpression('reused session to origin', '')
ts2.Streams.All = Testers.ContainsExpression('remove oldest session', '')
ts2.Streams.All += Testers.ContainsExpression('new session to origin', '')
ts2.Streams.All += Testers.ContainsExpression('reused session to origin', '')
tr.StillRunningAfter = server

tr = Test.AddTestRun('disable origin session reuse, reuse should fail')
tr.Processes.Default.Command = 'curl https://127.0.0.1:{0} -k && curl https://127.0.0.1:{0} -k'.format(ts5.Variables.ssl_port)
tr.Processes.Default.Command = 'curl https://127.0.0.1:{0} -k && curl https://127.0.0.1:{0} -k'.format(ts4.Variables.ssl_port)
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.StartBefore(ts3)
tr.Processes.Default.StartBefore(ts4)
tr.Processes.Default.StartBefore(ts5)
tr.Processes.Default.Streams.All = Testers.ContainsExpression('curl test', 'Making sure the basics still work')
ts5.Streams.All = Testers.ContainsExpression('new session to origin', '')
ts5.Streams.All += Testers.ExcludesExpression('reused session to origin', '')
ts4.Streams.All = Testers.ContainsExpression('new session to origin', '')
ts4.Streams.All += Testers.ExcludesExpression('reused session to origin', '')