SSL/TLS: Add options to configure session cache size and timeout

The default settings for the SSL session cache is unbounded with a timeout of
24 hours. This can lead to memory issues when clients do not resume connections.
This adds a default limit of 1000 sessions in the cache in addition to exposing
settings to control these values.

Closes elastic/elasticsearch#602

Original commit: elastic/x-pack-elasticsearch@9cdc7b613c
This commit is contained in:
jaymode 2015-01-21 08:06:53 -05:00
parent 9fed91b795
commit e8a17d9ccd
2 changed files with 38 additions and 5 deletions

View File

@ -8,8 +8,10 @@ package org.elasticsearch.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.shield.ShieldSettingsException;
@ -27,6 +29,7 @@ public class SSLService extends AbstractComponent {
static final String[] DEFAULT_CIPHERS = new String[]{ "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" };
static final String[] DEFAULT_SUPPORTED_PROTOCOLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"};
static final TimeValue DEFAULT_SESSION_CACHE_TIMEOUT = TimeValue.timeValueHours(24);
private Map<String, SSLContext> sslContexts = ConcurrentCollections.newConcurrentMap();
@ -99,15 +102,17 @@ public class SSLService extends AbstractComponent {
String key = keyStorePath + trustStorePath + sslProtocol;
SSLContext sslContext = sslContexts.get(key);
if (sslContext == null) {
logger.debug("using keyStore[{}], keyAlgorithm[{}], trustStore[{}], truststoreAlgorithm[{}], TLS protocol[{}]",
keyStorePath, keyStoreAlgorithm, trustStorePath, trustStoreAlgorithm, sslProtocol);
int sessionCacheSize = settings.getAsInt("session.cache_size", componentSettings.getAsInt("session.cache_size", 1000));
TimeValue sessionCacheTimeout = settings.getAsTime("session.cache_timeout", componentSettings.getAsTime("session.cache_timeout", DEFAULT_SESSION_CACHE_TIMEOUT));
logger.debug("using keystore[{}], key_algorithm[{}], truststore[{}], truststore_algorithm[{}], tls_protocol[{}], session_cache_size[{}], session_cache_timeout[{}]",
keyStorePath, keyStoreAlgorithm, trustStorePath, trustStoreAlgorithm, sslProtocol, sessionCacheSize, sessionCacheTimeout);
TrustManagerFactory trustFactory = getTrustFactory(trustStorePath, trustStorePassword, trustStoreAlgorithm);
KeyManagerFactory keyManagerFactory = createKeyManagerFactory(keyStorePath, keyStorePassword, keyStoreAlgorithm, keyPassword);
sslContext = createSslContext(keyManagerFactory, trustFactory, sslProtocol);
sslContext = createSslContext(keyManagerFactory, trustFactory, sslProtocol, sessionCacheSize, sessionCacheTimeout);
sslContexts.put(key, sslContext);
} else {
logger.trace("found keystore[{}], trustStore[{}], TLS protocol[{}] in SSL context cache, reusing", keyStorePath, trustStorePath, sslProtocol);
logger.trace("found keystore[{}], truststore[{}], tls_protocol[{}] in SSL context cache, reusing", keyStorePath, trustStorePath, sslProtocol);
}
return sslContext;
@ -144,11 +149,13 @@ public class SSLService extends AbstractComponent {
}
}
private SSLContext createSslContext(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustFactory, String sslProtocol) {
private SSLContext createSslContext(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustFactory, String sslProtocol, int sessionCacheSize, TimeValue sessionCacheTimeout) {
// Initialize sslContext
try {
SSLContext sslContext = SSLContext.getInstance(sslProtocol);
sslContext.init(keyManagerFactory.getKeyManagers(), trustFactory.getTrustManagers(), null);
sslContext.getServerSessionContext().setSessionCacheSize(sessionCacheSize);
sslContext.getServerSessionContext().setSessionTimeout(Ints.checkedCast(sessionCacheTimeout.seconds()));
return sslContext;
} catch (Exception e) {
throw new ElasticsearchSSLException("failed to initialize the SSLContext", e);

View File

@ -6,12 +6,14 @@
package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Before;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@ -100,4 +102,28 @@ public class SSLServiceTests extends ElasticsearchTestCase {
SSLEngine engine = sslService.createSSLEngine();
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
}
@Test
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
SSLService sslService = new SSLService(settingsBuilder()
.put("shield.ssl.keystore.path", testnodeStore)
.put("shield.ssl.keystore.password", "testnode")
.build());
SSLSessionContext context = sslService.getSslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(1000));
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
}
@Test
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
SSLService sslService = new SSLService(settingsBuilder()
.put("shield.ssl.keystore.path", testnodeStore)
.put("shield.ssl.keystore.password", "testnode")
.put("shield.ssl.session.cache_size", "300")
.put("shield.ssl.session.cache_timeout", "600s")
.build());
SSLSessionContext context = sslService.getSslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(300));
assertThat(context.getSessionTimeout(), equalTo(600));
}
}