Skip to content

Commit

Permalink
ARTEMIS-3644 add cert info to CONNECTION_CREATED notification
Browse files Browse the repository at this point in the history
  • Loading branch information
jbertram committed Feb 4, 2022
1 parent f18dd80 commit e582ce0
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 88 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,42 @@

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.handler.ssl.SslHandler;
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.remoting.Connection;

import javax.net.ssl.SSLPeerUnverifiedException;
import java.security.cert.X509Certificate;
import java.security.Principal;
import org.jboss.logging.Logger;

public class CertificateUtil {

private static final Logger logger = Logger.getLogger(CertificateUtil.class);

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) {
X509Certificate[] certificates = null;
if (remotingConnection != null) {
Connection transportConnection = remotingConnection.getTransportConnection();
if (transportConnection instanceof NettyConnection) {
certificates = org.apache.activemq.artemis.utils.CertificateUtil.getCertsFromChannel(((NettyConnection) transportConnection).getChannel());
certificates = getCertsFromChannel(((NettyConnection) transportConnection).getChannel());
}
}
return certificates;
Expand Down Expand Up @@ -72,4 +88,47 @@ public static Principal getLocalPrincipalFromConnection(NettyConnection nettyCon

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.apache.activemq.artemis.core.security.impl;

import javax.security.auth.Subject;
import java.security.cert.X509Certificate;
import java.util.Set;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -203,15 +202,6 @@ public String authenticate(final String user,
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
public void check(final SimpleString address,
final CheckType checkType,
Expand Down Expand Up @@ -356,7 +346,7 @@ public Subject getSessionSubject(SecurityAuth session) {
}

private void authenticationFailed(String user, RemotingConnection connection) throws Exception {
String certSubjectDN = getCertSubjectDN(connection);
String certSubjectDN = CertificateUtil.getCertSubjectDN(connection);

if (notificationService != null) {
TypedProperties props = new TypedProperties();
Expand Down Expand Up @@ -429,7 +419,7 @@ private boolean checkAuthorizationCache(final SimpleString dest, final String us
}

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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.ManagementHelper;
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.MessageReference;
import org.apache.activemq.artemis.core.server.ServerConsumer;
Expand Down Expand Up @@ -170,8 +171,10 @@ private void sendConnectionNotification(final RemotingConnection connection, fin

if (managementService != null && sendConnectionNotifications) {
try {
String certSubjectDN = CertificateUtil.getCertSubjectDN(connection);
final TypedProperties props = new TypedProperties();
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()));

managementService.sendNotification(new Notification(null, type, props));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
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.tests.integration.security.SecurityTest;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
Expand All @@ -48,6 +50,7 @@
import org.junit.Before;
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.SECURITY_AUTHENTICATION_VIOLATION;

Expand Down Expand Up @@ -77,7 +80,6 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {

@Test
public void testSECURITY_AUTHENTICATION_VIOLATION() throws Exception {
SSLSecurityNotificationTest.flush(notifConsumer);

TransportConfiguration tc = new TransportConfiguration(NETTY_CONNECTOR_FACTORY);
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
Expand All @@ -89,6 +91,7 @@ public void testSECURITY_AUTHENTICATION_VIOLATION() throws Exception {
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));

SSLSecurityNotificationTest.flush(notifConsumer);
long start = System.currentTimeMillis();
try {
sf.createSession();
Expand Down Expand Up @@ -148,6 +151,36 @@ public void testCONSUMER_CREATED() throws Exception {
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
@Before
public void setUp() throws Exception {
Expand All @@ -165,6 +198,12 @@ public void setUp() throws Exception {

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();

notifQueue = RandomUtil.randomSimpleString();
Expand Down

0 comments on commit e582ce0

Please sign in to comment.