ARTEMIS-3644 add cert info to CONNECTION_CREATED notification

This commit is contained in:
Justin Bertram 2022-01-13 12:42:08 -06:00
parent f18dd80dc8
commit e582ce03a5
No known key found for this signature in database
GPG Key ID: F41830B875BB8633
5 changed files with 109 additions and 88 deletions

View File

@ -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;
}
}

View File

@ -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;
@ -72,4 +88,47 @@ public class CertificateUtil {
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;
}
}

View File

@ -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;
@ -203,15 +202,6 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
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,
@ -356,7 +346,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
}
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();
@ -429,7 +419,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
}
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) {

View File

@ -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.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;
@ -170,8 +171,10 @@ public class NotificationActiveMQServerPlugin implements ActiveMQServerPlugin {
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));

View File

@ -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.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;
@ -48,6 +50,7 @@ import org.junit.Assert;
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;
@ -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);
@ -89,6 +91,7 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
SSLSecurityNotificationTest.flush(notifConsumer);
long start = System.currentTimeMillis();
try {
sf.createSession();
@ -148,6 +151,36 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
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 {
@ -165,6 +198,12 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
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();