Skip to content

Commit 8c9fed0

Browse files
committed
rethink SSL for older versions
1 parent 7d2aed4 commit 8c9fed0

File tree

4 files changed

+166
-135
lines changed

4 files changed

+166
-135
lines changed

commons/src/main/java/tech/beshu/ror/commons/SSLCertParser.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ public SSLCertParser(BasicSettings settings, LoggerShim logger, SSLContextCreato
4545

4646
private void createContext(BasicSettings settings) {
4747
if (!settings.isSSLEnabled()) {
48-
logger.info("SSL is disabled");
48+
logger.info("ROR SSL: SSL is disabled");
4949
return;
5050
}
51-
logger.info("SSL: attempting with JKS keystore..");
51+
logger.info("ROR SSL: attempting with JKS keystore..");
5252
try {
5353
char[] keyStorePassBa = null;
5454
if (settings.getKeystorePass().isPresent()) {
@@ -80,7 +80,7 @@ private void createContext(BasicSettings settings) {
8080
if (!settings.getKeyAlias().isPresent()) {
8181
if (ks.aliases().hasMoreElements()) {
8282
String inferredAlias = ks.aliases().nextElement();
83-
logger.info("SSL ssl.key_alias not configured, took first alias in keystore: " + inferredAlias);
83+
logger.info("ROR SSL: ssl.key_alias not configured, took first alias in keystore: " + inferredAlias);
8484
sslKeyAlias = inferredAlias;
8585
}
8686
else {
@@ -103,7 +103,7 @@ private void createContext(BasicSettings settings) {
103103
sb.append("\n");
104104
sb.append("---END PRIVATE KEY---");
105105
String privateKey = sb.toString();
106-
logger.info("Discovered key from JKS");
106+
logger.info("ROR SSL: Discovered key from JKS");
107107

108108
// Get CertChain from keystore
109109
Certificate[] cchain = ks.getCertificateChain(sslKeyAlias);
@@ -117,7 +117,7 @@ private void createContext(BasicSettings settings) {
117117
sb.append("-----END CERTIFICATE-----\n");
118118
}
119119
String certChain = sb.toString();
120-
logger.info("Discovered cert chain from JKS");
120+
logger.info("ROR SSL: Discovered cert chain from JKS");
121121

122122

123123
AccessController.doPrivileged(
@@ -127,9 +127,9 @@ private void createContext(BasicSettings settings) {
127127
});
128128

129129
} catch (Throwable t) {
130-
logger.error("Failed to load SSL certs and keys from JKS Keystore!");
130+
logger.error("ROR SSL: Failed to load SSL certs and keys from JKS Keystore!");
131131
if (t instanceof AccessControlException) {
132-
logger.error("Check the JKS Keystore path is correct: " + settings.getKeystoreFile());
132+
logger.error("ROR SSL: Check the JKS Keystore path is correct: " + settings.getKeystoreFile());
133133
}
134134
t.printStackTrace();
135135
}

es51x/src/main/java/tech/beshu/ror/es/SSLTransportNetty4.java

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222
*/
2323

2424
import cz.seznam.euphoria.shaded.guava.com.google.common.base.Joiner;
25+
import io.netty.buffer.ByteBufAllocator;
2526
import io.netty.channel.Channel;
2627
import io.netty.channel.ChannelHandler;
2728
import io.netty.channel.ChannelHandlerContext;
28-
import io.netty.handler.ssl.ApplicationProtocolConfig;
2929
import io.netty.handler.ssl.NotSslRecordException;
3030
import io.netty.handler.ssl.SslContext;
3131
import io.netty.handler.ssl.SslContextBuilder;
32+
import io.netty.handler.ssl.SslHandler;
3233
import org.elasticsearch.common.logging.Loggers;
3334
import org.elasticsearch.common.network.NetworkService;
3435
import org.elasticsearch.common.settings.Settings;
@@ -40,15 +41,17 @@
4041
import tech.beshu.ror.commons.settings.BasicSettings;
4142
import tech.beshu.ror.commons.shims.es.LoggerShim;
4243

44+
import javax.net.ssl.SSLEngine;
45+
import javax.net.ssl.SSLHandshakeException;
4346
import java.io.ByteArrayInputStream;
4447
import java.nio.charset.StandardCharsets;
45-
import java.util.List;
46-
import java.util.Optional;
4748

4849
public class SSLTransportNetty4 extends Netty4HttpServerTransport {
4950

51+
protected final LoggerShim logger;
5052
private final BasicSettings basicSettings;
51-
private final LoggerShim logger;
53+
protected SslContext sslContext;
54+
5255

5356
public SSLTransportNetty4(Settings settings, NetworkService networkService, BigArrays bigArrays,
5457
ThreadPool threadPool) {
@@ -61,15 +64,51 @@ public SSLTransportNetty4(Settings settings, NetworkService networkService, BigA
6164
if (basicSettings.isSSLEnabled()) {
6265
logger.info("creating SSL transport");
6366
}
67+
68+
69+
new SSLCertParser(basicSettings, logger, (certChain, privateKey) -> {
70+
71+
try {
72+
// #TODO expose configuration of sslPrivKeyPem password? Letsencrypt never sets one..
73+
SslContextBuilder sslcb = SslContextBuilder.forServer(
74+
new ByteArrayInputStream(certChain.getBytes(StandardCharsets.UTF_8)),
75+
new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)),
76+
null
77+
);
78+
79+
// Creating one SSL engine just for protocol/cipher validation and logging
80+
sslContext = sslcb.build();
81+
SSLEngine eng = sslContext.newEngine(ByteBufAllocator.DEFAULT);
82+
String[] defaultProtocols = eng.getEnabledProtocols();
83+
84+
logger.info("ROR SSL: Using SSL provider: " + SslContext.defaultServerProvider().name());
85+
86+
logger.info("ROR SSL: Available ciphers: " + Joiner.on(",").join(sslContext.cipherSuites()));
87+
baseSettings.getAllowedSSLProtocols()
88+
.ifPresent(_x -> logger.info("ROR SSL: Restricting to ciphers: " + Joiner.on(",").join(eng.getEnabledProtocols())));
89+
90+
logger.info("ROR SSL: Avaliable SSL protocols: " + Joiner.on(",").join(defaultProtocols));
91+
baseSettings.getAllowedSSLCiphers()
92+
.ifPresent(_x -> logger.info("ROR SSL: Restricting to SSL protocols: " + Joiner.on(",").join(eng.getEnabledProtocols())));
93+
94+
logger.info("ROR SSL: Available ciphers: " + Joiner.on(",").join(sslContext.cipherSuites()));
95+
96+
} catch (Exception e) {
97+
logger.error("Failed to load SSL CertChain & private key from Keystore! "
98+
+ e.getClass().getSimpleName() + ": " + e.getMessage(), e);
99+
}
100+
});
64101
}
65102

103+
66104
protected void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
67105
if (!this.lifecycle.started()) {
68106
return;
69107
}
70-
if (cause.getCause() instanceof NotSslRecordException) {
108+
if (cause.getCause() instanceof NotSslRecordException || cause.getCause() instanceof SSLHandshakeException) {
71109
logger.warn(cause.getMessage());
72110
}
111+
73112
else {
74113
cause.printStackTrace();
75114
super.exceptionCaught(ctx, cause);
@@ -79,52 +118,26 @@ protected void exceptionCaught(final ChannelHandlerContext ctx, final Throwable
79118

80119
public ChannelHandler configureServerChannelHandler() {
81120
SSLHandler handler = new SSLHandler(this);
82-
logger.info("ROR SSL accepted ciphers: " + Joiner.on(",").join(handler.context.get().cipherSuites()));
83121
return handler;
84122
}
85123

86124
private class SSLHandler extends Netty4HttpServerTransport.HttpChannelHandler {
87-
private Optional<SslContext> context = Optional.empty();
88125

89126
SSLHandler(final Netty4HttpServerTransport transport) {
90127
super(transport, SSLTransportNetty4.this.detailedErrorsEnabled, SSLTransportNetty4.this.threadPool.getThreadContext());
91-
92-
new SSLCertParser(basicSettings, logger, (certChain, privateKey) -> {
93-
94-
try {
95-
// #TODO expose configuration of sslPrivKeyPem password? Letsencrypt never sets one..
96-
SslContextBuilder sslcb = SslContextBuilder.forServer(
97-
new ByteArrayInputStream(certChain.getBytes(StandardCharsets.UTF_8)),
98-
new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)),
99-
null
100-
);
101-
102-
basicSettings.getAllowedSSLCiphers().ifPresent(sslcb::ciphers);
103-
104-
basicSettings.getAllowedSSLProtocols().ifPresent(allowedProtos -> {
105-
sslcb.applicationProtocolConfig(new ApplicationProtocolConfig(
106-
ApplicationProtocolConfig.Protocol.NONE,
107-
ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
108-
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
109-
allowedProtos
110-
));
111-
logger.info("ROR SSL accepted protocols: " + Joiner.on(",").join(allowedProtos));
112-
});
113-
114-
context = Optional.of(sslcb.build());
115-
} catch (Exception e) {
116-
context = Optional.empty();
117-
logger.error("Failed to load SSL CertChain & private key from Keystore! "
118-
+ e.getClass().getSimpleName() + ": " + e.getMessage(), e);
119-
}
120-
});
121128
}
122129

123130
protected void initChannel(final Channel ch) throws Exception {
124131
super.initChannel(ch);
125-
context.ifPresent(sslCtx -> {
126-
ch.pipeline().addFirst("ssl_netty4_handler", sslCtx.newHandler(ch.alloc()));
127-
});
132+
SSLEngine eng = sslContext.newEngine(ch.alloc());
133+
134+
basicSettings.getAllowedSSLProtocols()
135+
.ifPresent(p -> eng.setEnabledProtocols(p.toArray(new String[p.size()])));
136+
basicSettings.getAllowedSSLCiphers()
137+
.ifPresent(c -> eng.setEnabledCipherSuites(c.toArray(new String[c.size()])));
138+
139+
ch.pipeline().addFirst("ssl_netty4_handler", new SslHandler(eng));
140+
128141
}
129142
}
130143
}

es52x/src/main/java/tech/beshu/ror/es/SSLTransportNetty4.java

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222
*/
2323

2424
import cz.seznam.euphoria.shaded.guava.com.google.common.base.Joiner;
25+
import io.netty.buffer.ByteBufAllocator;
2526
import io.netty.channel.Channel;
2627
import io.netty.channel.ChannelHandler;
2728
import io.netty.channel.ChannelHandlerContext;
28-
import io.netty.handler.ssl.ApplicationProtocolConfig;
2929
import io.netty.handler.ssl.NotSslRecordException;
3030
import io.netty.handler.ssl.SslContext;
3131
import io.netty.handler.ssl.SslContextBuilder;
32+
import io.netty.handler.ssl.SslHandler;
3233
import org.elasticsearch.common.logging.Loggers;
3334
import org.elasticsearch.common.network.NetworkService;
3435
import org.elasticsearch.common.settings.Settings;
@@ -41,15 +42,17 @@
4142
import tech.beshu.ror.commons.settings.BasicSettings;
4243
import tech.beshu.ror.commons.shims.es.LoggerShim;
4344

45+
import javax.net.ssl.SSLEngine;
46+
import javax.net.ssl.SSLHandshakeException;
4447
import java.io.ByteArrayInputStream;
4548
import java.nio.charset.StandardCharsets;
46-
import java.util.List;
47-
import java.util.Optional;
4849

4950
public class SSLTransportNetty4 extends Netty4HttpServerTransport {
5051

52+
protected final LoggerShim logger;
5153
private final BasicSettings basicSettings;
52-
private final LoggerShim logger;
54+
protected SslContext sslContext;
55+
5356

5457
public SSLTransportNetty4(Settings settings, NetworkService networkService, BigArrays bigArrays,
5558
ThreadPool threadPool, NamedXContentRegistry xContentRegistry) {
@@ -62,15 +65,51 @@ public SSLTransportNetty4(Settings settings, NetworkService networkService, BigA
6265
if (basicSettings.isSSLEnabled()) {
6366
logger.info("creating SSL transport");
6467
}
68+
69+
70+
new SSLCertParser(basicSettings, logger, (certChain, privateKey) -> {
71+
72+
try {
73+
// #TODO expose configuration of sslPrivKeyPem password? Letsencrypt never sets one..
74+
SslContextBuilder sslcb = SslContextBuilder.forServer(
75+
new ByteArrayInputStream(certChain.getBytes(StandardCharsets.UTF_8)),
76+
new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)),
77+
null
78+
);
79+
80+
// Creating one SSL engine just for protocol/cipher validation and logging
81+
sslContext = sslcb.build();
82+
SSLEngine eng = sslContext.newEngine(ByteBufAllocator.DEFAULT);
83+
String[] defaultProtocols = eng.getEnabledProtocols();
84+
85+
logger.info("ROR SSL: Using SSL provider: " + SslContext.defaultServerProvider().name());
86+
87+
logger.info("ROR SSL: Available ciphers: " + Joiner.on(",").join(sslContext.cipherSuites()));
88+
baseSettings.getAllowedSSLProtocols()
89+
.ifPresent(_x -> logger.info("ROR SSL: Restricting to ciphers: " + Joiner.on(",").join(eng.getEnabledProtocols())));
90+
91+
logger.info("ROR SSL: Avaliable SSL protocols: " + Joiner.on(",").join(defaultProtocols));
92+
baseSettings.getAllowedSSLCiphers()
93+
.ifPresent(_x -> logger.info("ROR SSL: Restricting to SSL protocols: " + Joiner.on(",").join(eng.getEnabledProtocols())));
94+
95+
logger.info("ROR SSL: Available ciphers: " + Joiner.on(",").join(sslContext.cipherSuites()));
96+
97+
} catch (Exception e) {
98+
logger.error("Failed to load SSL CertChain & private key from Keystore! "
99+
+ e.getClass().getSimpleName() + ": " + e.getMessage(), e);
100+
}
101+
});
65102
}
66103

104+
67105
protected void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
68106
if (!this.lifecycle.started()) {
69107
return;
70108
}
71-
if (cause.getCause() instanceof NotSslRecordException) {
109+
if (cause.getCause() instanceof NotSslRecordException || cause.getCause() instanceof SSLHandshakeException) {
72110
logger.warn(cause.getMessage());
73111
}
112+
74113
else {
75114
cause.printStackTrace();
76115
super.exceptionCaught(ctx, cause);
@@ -80,56 +119,26 @@ protected void exceptionCaught(final ChannelHandlerContext ctx, final Throwable
80119

81120
public ChannelHandler configureServerChannelHandler() {
82121
SSLHandler handler = new SSLHandler(this);
83-
logger.info("ROR SSL accepted ciphers: " + Joiner.on(",").join(handler.context.get().cipherSuites()));
84122
return handler;
85123
}
86124

87125
private class SSLHandler extends Netty4HttpServerTransport.HttpChannelHandler {
88-
private Optional<SslContext> context = Optional.empty();
89126

90127
SSLHandler(final Netty4HttpServerTransport transport) {
91128
super(transport, SSLTransportNetty4.this.detailedErrorsEnabled, SSLTransportNetty4.this.threadPool.getThreadContext());
92-
93-
new SSLCertParser(basicSettings, logger, (certChain, privateKey) -> {
94-
try {
95-
// #TODO expose configuration of sslPrivKeyPem password? Letsencrypt never sets one..
96-
SslContextBuilder sslcb = SslContextBuilder.forServer(
97-
new ByteArrayInputStream(certChain.getBytes(StandardCharsets.UTF_8)),
98-
new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)),
99-
null
100-
);
101-
102-
if (basicSettings.getAllowedSSLCiphers().isPresent()) {
103-
sslcb.ciphers(basicSettings.getAllowedSSLCiphers().get());
104-
}
105-
106-
if (basicSettings.getAllowedSSLProtocols().isPresent()) {
107-
List<String> protocols = basicSettings.getAllowedSSLProtocols().get();
108-
sslcb.applicationProtocolConfig(new ApplicationProtocolConfig(
109-
ApplicationProtocolConfig.Protocol.NONE,
110-
ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
111-
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
112-
protocols
113-
));
114-
115-
logger.info("ROR SSL accepted protocols: " + Joiner.on(",").join(protocols));
116-
}
117-
118-
context = Optional.of(sslcb.build());
119-
120-
} catch (Exception e) {
121-
context = Optional.empty();
122-
logger.error("Failed to load SSL CertChain & private key from Keystore! "
123-
+ e.getClass().getSimpleName() + ": " + e.getMessage(), e);
124-
}
125-
});
126129
}
127130

128131
protected void initChannel(final Channel ch) throws Exception {
129132
super.initChannel(ch);
130-
context.ifPresent(sslCtx -> {
131-
ch.pipeline().addFirst("ssl_netty4_handler", sslCtx.newHandler(ch.alloc()));
132-
});
133+
SSLEngine eng = sslContext.newEngine(ch.alloc());
134+
135+
basicSettings.getAllowedSSLProtocols()
136+
.ifPresent(p -> eng.setEnabledProtocols(p.toArray(new String[p.size()])));
137+
basicSettings.getAllowedSSLCiphers()
138+
.ifPresent(c -> eng.setEnabledCipherSuites(c.toArray(new String[c.size()])));
139+
140+
ch.pipeline().addFirst("ssl_netty4_handler", new SslHandler(eng));
141+
133142
}
134143
}
135144
}

0 commit comments

Comments
 (0)