ARTEMIS-3644 add cert info to CONNECTION_CREATED notification
This commit is contained in:
parent
f18dd80dc8
commit
e582ce03a5
|
@ -1,70 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.activemq.artemis.utils;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.security.cert.Certificate;
|
|
||||||
import java.security.cert.CertificateFactory;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.handler.ssl.SslHandler;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
public class CertificateUtil {
|
|
||||||
private static final Logger logger = Logger.getLogger(CertificateUtil.class);
|
|
||||||
|
|
||||||
public static X509Certificate[] getCertsFromChannel(Channel channel) {
|
|
||||||
Certificate[] plainCerts = null;
|
|
||||||
ChannelHandler channelHandler = channel.pipeline().get("ssl");
|
|
||||||
if (channelHandler != null && channelHandler instanceof SslHandler) {
|
|
||||||
SslHandler sslHandler = (SslHandler) channelHandler;
|
|
||||||
try {
|
|
||||||
plainCerts = sslHandler.engine().getSession().getPeerCertificates();
|
|
||||||
} catch (SSLPeerUnverifiedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
X509Certificate[] x509Certs = null;
|
|
||||||
if (plainCerts != null && plainCerts.length > 0) {
|
|
||||||
x509Certs = new X509Certificate[plainCerts.length];
|
|
||||||
for (int i = 0; i < plainCerts.length; i++) {
|
|
||||||
if (plainCerts[i] instanceof X509Certificate) {
|
|
||||||
x509Certs[i] = (X509Certificate) plainCerts[i];
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
x509Certs[i] = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(plainCerts[i].getEncoded()));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Failed to convert SSL cert", ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Cert #" + i + " = " + x509Certs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return x509Certs;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,26 +17,42 @@
|
||||||
|
|
||||||
package org.apache.activemq.artemis.core.remoting;
|
package org.apache.activemq.artemis.core.remoting;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.handler.ssl.SslHandler;
|
import io.netty.handler.ssl.SslHandler;
|
||||||
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection;
|
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection;
|
||||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
||||||
import org.apache.activemq.artemis.spi.core.remoting.Connection;
|
import org.apache.activemq.artemis.spi.core.remoting.Connection;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.security.Principal;
|
|
||||||
|
|
||||||
public class CertificateUtil {
|
public class CertificateUtil {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(CertificateUtil.class);
|
||||||
|
|
||||||
private static final String SSL_HANDLER_NAME = "ssl";
|
private static final String SSL_HANDLER_NAME = "ssl";
|
||||||
|
|
||||||
|
public static String getCertSubjectDN(RemotingConnection connection) {
|
||||||
|
String certSubjectDN = "unavailable";
|
||||||
|
X509Certificate[] certs = getCertsFromConnection(connection);
|
||||||
|
if (certs != null && certs.length > 0 && certs[0] != null) {
|
||||||
|
certSubjectDN = certs[0].getSubjectDN().getName();
|
||||||
|
}
|
||||||
|
return certSubjectDN;
|
||||||
|
}
|
||||||
|
|
||||||
public static X509Certificate[] getCertsFromConnection(RemotingConnection remotingConnection) {
|
public static X509Certificate[] getCertsFromConnection(RemotingConnection remotingConnection) {
|
||||||
X509Certificate[] certificates = null;
|
X509Certificate[] certificates = null;
|
||||||
if (remotingConnection != null) {
|
if (remotingConnection != null) {
|
||||||
Connection transportConnection = remotingConnection.getTransportConnection();
|
Connection transportConnection = remotingConnection.getTransportConnection();
|
||||||
if (transportConnection instanceof NettyConnection) {
|
if (transportConnection instanceof NettyConnection) {
|
||||||
certificates = org.apache.activemq.artemis.utils.CertificateUtil.getCertsFromChannel(((NettyConnection) transportConnection).getChannel());
|
certificates = getCertsFromChannel(((NettyConnection) transportConnection).getChannel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return certificates;
|
return certificates;
|
||||||
|
@ -72,4 +88,47 @@ public class CertificateUtil {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static X509Certificate[] getCertsFromChannel(Channel channel) {
|
||||||
|
Certificate[] plainCerts = null;
|
||||||
|
ChannelHandler channelHandler = channel.pipeline().get("ssl");
|
||||||
|
if (channelHandler != null && channelHandler instanceof SslHandler) {
|
||||||
|
SslHandler sslHandler = (SslHandler) channelHandler;
|
||||||
|
try {
|
||||||
|
plainCerts = sslHandler.engine().getSession().getPeerCertificates();
|
||||||
|
} catch (SSLPeerUnverifiedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the OpenSSL provider on the broker the getPeerCertificates() method does *not* return a
|
||||||
|
* X509Certificate[] so we need to convert the Certificate[] that is returned. This code is inspired by Tomcat's
|
||||||
|
* org.apache.tomcat.util.net.jsse.JSSESupport class.
|
||||||
|
*/
|
||||||
|
X509Certificate[] x509Certs = null;
|
||||||
|
if (plainCerts != null && plainCerts.length > 0) {
|
||||||
|
x509Certs = new X509Certificate[plainCerts.length];
|
||||||
|
for (int i = 0; i < plainCerts.length; i++) {
|
||||||
|
if (plainCerts[i] instanceof X509Certificate) {
|
||||||
|
x509Certs[i] = (X509Certificate) plainCerts[i];
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
x509Certs[i] = (X509Certificate) CertificateFactory
|
||||||
|
.getInstance("X.509").generateCertificate(new ByteArrayInputStream(plainCerts[i].getEncoded()));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Failed to convert SSL cert", ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Cert #" + i + " = " + x509Certs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509Certs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.apache.activemq.artemis.core.security.impl;
|
package org.apache.activemq.artemis.core.security.impl;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -203,15 +202,6 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCertSubjectDN(RemotingConnection connection) {
|
|
||||||
String certSubjectDN = "unavailable";
|
|
||||||
X509Certificate[] certs = CertificateUtil.getCertsFromConnection(connection);
|
|
||||||
if (certs != null && certs.length > 0 && certs[0] != null) {
|
|
||||||
certSubjectDN = certs[0].getSubjectDN().getName();
|
|
||||||
}
|
|
||||||
return certSubjectDN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void check(final SimpleString address,
|
public void check(final SimpleString address,
|
||||||
final CheckType checkType,
|
final CheckType checkType,
|
||||||
|
@ -356,7 +346,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticationFailed(String user, RemotingConnection connection) throws Exception {
|
private void authenticationFailed(String user, RemotingConnection connection) throws Exception {
|
||||||
String certSubjectDN = getCertSubjectDN(connection);
|
String certSubjectDN = CertificateUtil.getCertSubjectDN(connection);
|
||||||
|
|
||||||
if (notificationService != null) {
|
if (notificationService != null) {
|
||||||
TypedProperties props = new TypedProperties();
|
TypedProperties props = new TypedProperties();
|
||||||
|
@ -429,7 +419,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createAuthenticationCacheKey(String username, String password, RemotingConnection connection) {
|
private String createAuthenticationCacheKey(String username, String password, RemotingConnection connection) {
|
||||||
return username + password + getCertSubjectDN(connection);
|
return username + password + CertificateUtil.getCertSubjectDN(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createAuthorizationCacheKey(String user, CheckType checkType) {
|
private String createAuthorizationCacheKey(String user, CheckType checkType) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQException;
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||||
import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
|
import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
|
||||||
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
|
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
|
||||||
|
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
||||||
import org.apache.activemq.artemis.core.server.MessageReference;
|
import org.apache.activemq.artemis.core.server.MessageReference;
|
||||||
import org.apache.activemq.artemis.core.server.ServerConsumer;
|
import org.apache.activemq.artemis.core.server.ServerConsumer;
|
||||||
|
@ -170,8 +171,10 @@ public class NotificationActiveMQServerPlugin implements ActiveMQServerPlugin {
|
||||||
|
|
||||||
if (managementService != null && sendConnectionNotifications) {
|
if (managementService != null && sendConnectionNotifications) {
|
||||||
try {
|
try {
|
||||||
|
String certSubjectDN = CertificateUtil.getCertSubjectDN(connection);
|
||||||
final TypedProperties props = new TypedProperties();
|
final TypedProperties props = new TypedProperties();
|
||||||
props.putSimpleStringProperty(ManagementHelper.HDR_CONNECTION_NAME, SimpleString.toSimpleString(connection.getID().toString()));
|
props.putSimpleStringProperty(ManagementHelper.HDR_CONNECTION_NAME, SimpleString.toSimpleString(connection.getID().toString()));
|
||||||
|
props.putSimpleStringProperty(ManagementHelper.HDR_CERT_SUBJECT_DN, SimpleString.toSimpleString(certSubjectDN));
|
||||||
props.putSimpleStringProperty(ManagementHelper.HDR_REMOTE_ADDRESS, SimpleString.toSimpleString(connection.getRemoteAddress()));
|
props.putSimpleStringProperty(ManagementHelper.HDR_REMOTE_ADDRESS, SimpleString.toSimpleString(connection.getRemoteAddress()));
|
||||||
|
|
||||||
managementService.sendNotification(new Notification(null, type, props));
|
managementService.sendNotification(new Notification(null, type, props));
|
||||||
|
|
|
@ -40,6 +40,8 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
|
||||||
import org.apache.activemq.artemis.core.security.Role;
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServers;
|
import org.apache.activemq.artemis.core.server.ActiveMQServers;
|
||||||
|
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin;
|
||||||
|
import org.apache.activemq.artemis.core.server.plugin.impl.NotificationActiveMQServerPlugin;
|
||||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
|
||||||
import org.apache.activemq.artemis.tests.integration.security.SecurityTest;
|
import org.apache.activemq.artemis.tests.integration.security.SecurityTest;
|
||||||
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
||||||
|
@ -48,6 +50,7 @@ import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.apache.activemq.artemis.api.core.management.CoreNotificationType.CONNECTION_CREATED;
|
||||||
import static org.apache.activemq.artemis.api.core.management.CoreNotificationType.CONSUMER_CREATED;
|
import static org.apache.activemq.artemis.api.core.management.CoreNotificationType.CONSUMER_CREATED;
|
||||||
import static org.apache.activemq.artemis.api.core.management.CoreNotificationType.SECURITY_AUTHENTICATION_VIOLATION;
|
import static org.apache.activemq.artemis.api.core.management.CoreNotificationType.SECURITY_AUTHENTICATION_VIOLATION;
|
||||||
|
|
||||||
|
@ -77,7 +80,6 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSECURITY_AUTHENTICATION_VIOLATION() throws Exception {
|
public void testSECURITY_AUTHENTICATION_VIOLATION() throws Exception {
|
||||||
SSLSecurityNotificationTest.flush(notifConsumer);
|
|
||||||
|
|
||||||
TransportConfiguration tc = new TransportConfiguration(NETTY_CONNECTOR_FACTORY);
|
TransportConfiguration tc = new TransportConfiguration(NETTY_CONNECTOR_FACTORY);
|
||||||
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
@ -89,6 +91,7 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
|
||||||
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||||
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||||
|
|
||||||
|
SSLSecurityNotificationTest.flush(notifConsumer);
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
sf.createSession();
|
sf.createSession();
|
||||||
|
@ -148,6 +151,36 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
|
||||||
guestSession.close();
|
guestSession.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCONNECTION_CREATED() throws Exception {
|
||||||
|
Role role = new Role("notif", true, true, true, true, false, true, true, true, true, true);
|
||||||
|
Set<Role> roles = new HashSet<>();
|
||||||
|
roles.add(role);
|
||||||
|
server.getSecurityRepository().addMatch("#", roles);
|
||||||
|
|
||||||
|
TransportConfiguration tc = new TransportConfiguration(NETTY_CONNECTOR_FACTORY);
|
||||||
|
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "server-ca-truststore.jks");
|
||||||
|
tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "securepass");
|
||||||
|
tc.getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "client-keystore.jks");
|
||||||
|
tc.getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "securepass");
|
||||||
|
|
||||||
|
SSLSecurityNotificationTest.flush(notifConsumer);
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
|
||||||
|
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
|
||||||
|
|
||||||
|
ClientMessage notification = SecurityNotificationTest.consumeMessages(1, notifConsumer)[0];
|
||||||
|
Assert.assertEquals(CONNECTION_CREATED.toString(), notification.getObjectProperty(ManagementHelper.HDR_NOTIFICATION_TYPE).toString());
|
||||||
|
Assert.assertNotNull(notification.getObjectProperty(ManagementHelper.HDR_CERT_SUBJECT_DN));
|
||||||
|
Assert.assertEquals("CN=ActiveMQ Artemis Client, OU=Artemis, O=ActiveMQ, L=AMQ, ST=AMQ, C=AMQ", notification.getObjectProperty(ManagementHelper.HDR_CERT_SUBJECT_DN).toString());
|
||||||
|
Assert.assertTrue(notification.getObjectProperty(ManagementHelper.HDR_REMOTE_ADDRESS).toString().startsWith("/127.0.0.1"));
|
||||||
|
Assert.assertTrue(notification.getTimestamp() >= start);
|
||||||
|
Assert.assertTrue((long) notification.getObjectProperty(ManagementHelper.HDR_NOTIFICATION_TIMESTAMP) >= start);
|
||||||
|
Assert.assertEquals(notification.getTimestamp(), (long) notification.getObjectProperty(ManagementHelper.HDR_NOTIFICATION_TIMESTAMP));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
@ -165,6 +198,12 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
|
||||||
|
|
||||||
server.getConfiguration().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params));
|
server.getConfiguration().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params));
|
||||||
|
|
||||||
|
ActiveMQServerPlugin plugin = new NotificationActiveMQServerPlugin();
|
||||||
|
Map init = new HashMap();
|
||||||
|
init.put(NotificationActiveMQServerPlugin.SEND_CONNECTION_NOTIFICATIONS, "true");
|
||||||
|
plugin.init(init);
|
||||||
|
server.registerBrokerPlugin(plugin);
|
||||||
|
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
notifQueue = RandomUtil.randomSimpleString();
|
notifQueue = RandomUtil.randomSimpleString();
|
||||||
|
|
Loading…
Reference in New Issue