This closes #2315 ARTEMIS-1919 implement SNI properly
This commit is contained in:
commit
4cbc387782
|
@ -16,6 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.artemis.core.remoting.impl.netty;
|
package org.apache.activemq.artemis.core.remoting.impl.netty;
|
||||||
|
|
||||||
|
import javax.net.ssl.SNIHostName;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLParameters;
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import javax.security.auth.login.LoginContext;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -28,6 +34,7 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,12 +49,6 @@ import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
import javax.net.ssl.SSLParameters;
|
|
||||||
import javax.security.auth.Subject;
|
|
||||||
import javax.security.auth.login.LoginContext;
|
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
@ -383,6 +384,7 @@ public class NettyConnector extends AbstractConnector {
|
||||||
enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS;
|
enabledProtocols = TransportConstants.DEFAULT_ENABLED_PROTOCOLS;
|
||||||
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
|
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
|
||||||
trustAll = TransportConstants.DEFAULT_TRUST_ALL;
|
trustAll = TransportConstants.DEFAULT_TRUST_ALL;
|
||||||
|
sniHost = TransportConstants.DEFAULT_SNIHOST_CONFIG;
|
||||||
useDefaultSslContext = TransportConstants.DEFAULT_USE_DEFAULT_SSL_CONTEXT;
|
useDefaultSslContext = TransportConstants.DEFAULT_USE_DEFAULT_SSL_CONTEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,6 +573,12 @@ public class NettyConnector extends AbstractConnector {
|
||||||
engine.setSSLParameters(sslParameters);
|
engine.setSSLParameters(sslParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sniHost != null) {
|
||||||
|
SSLParameters sslParameters = engine.getSSLParameters();
|
||||||
|
sslParameters.setServerNames(Arrays.asList(new SNIHostName(sniHost)));
|
||||||
|
engine.setSSLParameters(sslParameters);
|
||||||
|
}
|
||||||
|
|
||||||
SslHandler handler = new SslHandler(engine);
|
SslHandler handler = new SslHandler(engine);
|
||||||
|
|
||||||
pipeline.addLast("ssl", handler);
|
pipeline.addLast("ssl", handler);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.artemis.core.remoting.impl.netty;
|
package org.apache.activemq.artemis.core.remoting.impl.netty;
|
||||||
|
|
||||||
|
import javax.net.ssl.SNIHostName;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
|
@ -28,6 +29,7 @@ import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -175,6 +177,8 @@ public class NettyAcceptor extends AbstractAcceptor {
|
||||||
|
|
||||||
private final String kerb5Config;
|
private final String kerb5Config;
|
||||||
|
|
||||||
|
private String sniHost;
|
||||||
|
|
||||||
private final boolean tcpNoDelay;
|
private final boolean tcpNoDelay;
|
||||||
|
|
||||||
private final int backlog;
|
private final int backlog;
|
||||||
|
@ -286,6 +290,8 @@ public class NettyAcceptor extends AbstractAcceptor {
|
||||||
verifyHost = ConfigurationHelper.getBooleanProperty(TransportConstants.VERIFY_HOST_PROP_NAME, TransportConstants.DEFAULT_VERIFY_HOST, configuration);
|
verifyHost = ConfigurationHelper.getBooleanProperty(TransportConstants.VERIFY_HOST_PROP_NAME, TransportConstants.DEFAULT_VERIFY_HOST, configuration);
|
||||||
|
|
||||||
sslProvider = ConfigurationHelper.getStringProperty(TransportConstants.SSL_PROVIDER, TransportConstants.DEFAULT_SSL_PROVIDER, configuration);
|
sslProvider = ConfigurationHelper.getStringProperty(TransportConstants.SSL_PROVIDER, TransportConstants.DEFAULT_SSL_PROVIDER, configuration);
|
||||||
|
|
||||||
|
sniHost = ConfigurationHelper.getStringProperty(TransportConstants.SNIHOST_PROP_NAME, TransportConstants.DEFAULT_SNIHOST_CONFIG, configuration);
|
||||||
} else {
|
} else {
|
||||||
keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
|
keyStoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
|
||||||
keyStorePath = TransportConstants.DEFAULT_KEYSTORE_PATH;
|
keyStorePath = TransportConstants.DEFAULT_KEYSTORE_PATH;
|
||||||
|
@ -300,6 +306,7 @@ public class NettyAcceptor extends AbstractAcceptor {
|
||||||
wantClientAuth = TransportConstants.DEFAULT_WANT_CLIENT_AUTH;
|
wantClientAuth = TransportConstants.DEFAULT_WANT_CLIENT_AUTH;
|
||||||
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
|
verifyHost = TransportConstants.DEFAULT_VERIFY_HOST;
|
||||||
sslProvider = TransportConstants.DEFAULT_SSL_PROVIDER;
|
sslProvider = TransportConstants.DEFAULT_SSL_PROVIDER;
|
||||||
|
sniHost = TransportConstants.DEFAULT_SNIHOST_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration);
|
tcpNoDelay = ConfigurationHelper.getBooleanProperty(TransportConstants.TCP_NODELAY_PROPNAME, TransportConstants.DEFAULT_TCP_NODELAY, configuration);
|
||||||
|
@ -534,6 +541,12 @@ public class NettyAcceptor extends AbstractAcceptor {
|
||||||
engine.setSSLParameters(sslParameters);
|
engine.setSSLParameters(sslParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sniHost != null) {
|
||||||
|
SSLParameters sslParameters = engine.getSSLParameters();
|
||||||
|
sslParameters.setSNIMatchers(Arrays.asList(SNIHostName.createSNIMatcher(sniHost)));
|
||||||
|
engine.setSSLParameters(sslParameters);
|
||||||
|
}
|
||||||
|
|
||||||
return new SslHandler(engine);
|
return new SslHandler(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -460,6 +460,18 @@ additional properties:
|
||||||
https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations for more
|
https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations for more
|
||||||
information's.
|
information's.
|
||||||
|
|
||||||
|
- `sniHost`
|
||||||
|
|
||||||
|
When used on an `acceptor` the `sniHost` is a *regular expression* used to
|
||||||
|
match the [`server_name`](https://tools.ietf.org/html/rfc6066) extension on
|
||||||
|
incoming SSL connections. If the name doesn't match then the connection to
|
||||||
|
the acceptor will be rejected. A WARN message will be logged if this happens.
|
||||||
|
If the incoming connection doesn't include the `server_name` extension then
|
||||||
|
the connection will be accepted.
|
||||||
|
|
||||||
|
When used on a `connector` the `sniHost` value is used for the `server_name`
|
||||||
|
extension on the SSL connection.
|
||||||
|
|
||||||
### Configuring Netty HTTP
|
### Configuring Netty HTTP
|
||||||
|
|
||||||
Netty HTTP tunnels packets over the HTTP protocol. It can be useful in
|
Netty HTTP tunnels packets over the HTTP protocol. It can be useful in
|
||||||
|
|
|
@ -153,6 +153,108 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
||||||
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneWaySSLwithSNI() throws Exception {
|
||||||
|
createCustomSslServer("myhost\\.com");
|
||||||
|
String text = RandomUtil.randomString();
|
||||||
|
|
||||||
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
|
||||||
|
tc.getParams().put(TransportConstants.SNIHOST_PROP_NAME, "myhost.com");
|
||||||
|
|
||||||
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||||
|
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||||
|
ClientSession session = addClientSession(sf.createSession(false, true, true));
|
||||||
|
session.createQueue(CoreClientOverOneWaySSLTest.QUEUE, CoreClientOverOneWaySSLTest.QUEUE, false);
|
||||||
|
ClientProducer producer = addClientProducer(session.createProducer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||||
|
|
||||||
|
ClientMessage message = createTextMessage(session, text);
|
||||||
|
producer.send(message);
|
||||||
|
|
||||||
|
ClientConsumer consumer = addClientConsumer(session.createConsumer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||||
|
session.start();
|
||||||
|
|
||||||
|
ClientMessage m = consumer.receive(1000);
|
||||||
|
Assert.assertNotNull(m);
|
||||||
|
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneWaySSLwithSNINegative() throws Exception {
|
||||||
|
createCustomSslServer("myhost\\.com");
|
||||||
|
|
||||||
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
|
||||||
|
tc.getParams().put(TransportConstants.SNIHOST_PROP_NAME, "badhost.com");
|
||||||
|
|
||||||
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||||
|
try {
|
||||||
|
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||||
|
fail("Should have failed due to unrecognized SNI host name");
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneWaySSLwithSNIOnlyOnTheClient() throws Exception {
|
||||||
|
createCustomSslServer();
|
||||||
|
String text = RandomUtil.randomString();
|
||||||
|
|
||||||
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
|
||||||
|
tc.getParams().put(TransportConstants.SNIHOST_PROP_NAME, "myhost.com");
|
||||||
|
|
||||||
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||||
|
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||||
|
ClientSession session = addClientSession(sf.createSession(false, true, true));
|
||||||
|
session.createQueue(CoreClientOverOneWaySSLTest.QUEUE, CoreClientOverOneWaySSLTest.QUEUE, false);
|
||||||
|
ClientProducer producer = addClientProducer(session.createProducer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||||
|
|
||||||
|
ClientMessage message = createTextMessage(session, text);
|
||||||
|
producer.send(message);
|
||||||
|
|
||||||
|
ClientConsumer consumer = addClientConsumer(session.createConsumer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||||
|
session.start();
|
||||||
|
|
||||||
|
ClientMessage m = consumer.receive(1000);
|
||||||
|
Assert.assertNotNull(m);
|
||||||
|
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneWaySSLwithSNIOnlyOnTheBroker() throws Exception {
|
||||||
|
createCustomSslServer("myhost\\.com");
|
||||||
|
String text = RandomUtil.randomString();
|
||||||
|
|
||||||
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, CLIENT_SIDE_TRUSTSTORE);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD);
|
||||||
|
|
||||||
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||||
|
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||||
|
ClientSession session = addClientSession(sf.createSession(false, true, true));
|
||||||
|
session.createQueue(CoreClientOverOneWaySSLTest.QUEUE, CoreClientOverOneWaySSLTest.QUEUE, false);
|
||||||
|
ClientProducer producer = addClientProducer(session.createProducer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||||
|
|
||||||
|
ClientMessage message = createTextMessage(session, text);
|
||||||
|
producer.send(message);
|
||||||
|
|
||||||
|
ClientConsumer consumer = addClientConsumer(session.createConsumer(CoreClientOverOneWaySSLTest.QUEUE));
|
||||||
|
session.start();
|
||||||
|
|
||||||
|
ClientMessage m = consumer.receive(1000);
|
||||||
|
Assert.assertNotNull(m);
|
||||||
|
Assert.assertEquals(text, m.getBodyBuffer().readString());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOneWaySSLwithURL() throws Exception {
|
public void testOneWaySSLwithURL() throws Exception {
|
||||||
createCustomSslServer();
|
createCustomSslServer();
|
||||||
|
@ -264,7 +366,7 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOneWaySSLVerifyHost() throws Exception {
|
public void testOneWaySSLVerifyHost() throws Exception {
|
||||||
createCustomSslServer(null, null, true);
|
createCustomSslServer(true);
|
||||||
String text = RandomUtil.randomString();
|
String text = RandomUtil.randomString();
|
||||||
|
|
||||||
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
@ -292,7 +394,7 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOneWaySSLVerifyHostNegative() throws Exception {
|
public void testOneWaySSLVerifyHostNegative() throws Exception {
|
||||||
createCustomSslServer(null, null, false);
|
createCustomSslServer();
|
||||||
String text = RandomUtil.randomString();
|
String text = RandomUtil.randomString();
|
||||||
|
|
||||||
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
@ -763,16 +865,29 @@ public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCustomSslServer(String cipherSuites, String protocols) throws Exception {
|
private void createCustomSslServer(String cipherSuites, String protocols) throws Exception {
|
||||||
createCustomSslServer(cipherSuites, protocols, false);
|
createCustomSslServer(cipherSuites, protocols, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCustomSslServer(String sniHost) throws Exception {
|
||||||
|
createCustomSslServer(null, null, false, sniHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCustomSslServer(boolean useVerifiedKeystore) throws Exception {
|
||||||
|
createCustomSslServer(null, null, useVerifiedKeystore, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCustomSslServer(String cipherSuites,
|
private void createCustomSslServer(String cipherSuites,
|
||||||
String protocols,
|
String protocols,
|
||||||
boolean useVerifiedKeystore) throws Exception {
|
boolean useVerifiedKeystore,
|
||||||
|
String sniHost) throws Exception {
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType);
|
params.put(TransportConstants.KEYSTORE_PROVIDER_PROP_NAME, storeType);
|
||||||
|
|
||||||
|
if (sniHost != null) {
|
||||||
|
params.put(TransportConstants.SNIHOST_PROP_NAME, sniHost);
|
||||||
|
}
|
||||||
|
|
||||||
if (useVerifiedKeystore) {
|
if (useVerifiedKeystore) {
|
||||||
params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "verified-" + SERVER_SIDE_KEYSTORE);
|
params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "verified-" + SERVER_SIDE_KEYSTORE);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue