Skip to content

Commit 12d4576

Browse files
JeanLucGraphalojbertram
authored andcommitted
ARTEMIS-5163 Artemis fails to send mqtt will message using mutual TLS
The client's certificate is cached in order to successfully authenticate when sending LWT using mutual TLS. - move CertificateUtil to artemis-server - cache X509Certificate in NettyServerConnection - try to get certificate from channel if not already cached
1 parent b31da95 commit 12d4576

10 files changed

Lines changed: 79 additions & 11 deletions

File tree

artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/connect/AMQPBrokerConnection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPMirrorBrokerConnectionElement;
5050
import org.apache.activemq.artemis.core.postoffice.Binding;
5151
import org.apache.activemq.artemis.core.postoffice.QueueBinding;
52-
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
5352
import org.apache.activemq.artemis.core.remoting.CloseListener;
5453
import org.apache.activemq.artemis.core.remoting.FailureListener;
5554
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection;
@@ -99,6 +98,7 @@
9998
import org.apache.activemq.artemis.spi.core.remoting.ClientProtocolManager;
10099
import org.apache.activemq.artemis.spi.core.remoting.Connection;
101100
import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
101+
import org.apache.activemq.artemis.utils.CertificateUtil;
102102
import org.apache.activemq.artemis.utils.ConfigurationHelper;
103103
import org.apache.activemq.artemis.utils.UUIDGenerator;
104104
import org.apache.qpid.proton.amqp.Symbol;

artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/ExternalServerSASLFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818

1919
import java.security.Principal;
2020

21-
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
2221
import org.apache.activemq.artemis.core.server.ActiveMQServer;
2322
import org.apache.activemq.artemis.protocol.amqp.broker.AmqpInterceptor;
2423
import org.apache.activemq.artemis.protocol.amqp.proton.AMQPRoutingHandler;
2524
import org.apache.activemq.artemis.spi.core.protocol.ProtocolManager;
2625
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
2726
import org.apache.activemq.artemis.spi.core.remoting.Connection;
27+
import org.apache.activemq.artemis.utils.CertificateUtil;
2828
import org.slf4j.Logger;
2929
import org.slf4j.LoggerFactory;
3030
import java.lang.invoke.MethodHandles;

artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyServerConnection.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
*/
1717
package org.apache.activemq.artemis.core.remoting.impl.netty;
1818

19+
import java.security.cert.X509Certificate;
1920
import java.util.Map;
2021

2122
import io.netty.channel.Channel;
2223
import org.apache.activemq.artemis.spi.core.remoting.Connection;
2324
import org.apache.activemq.artemis.spi.core.remoting.ServerConnectionLifeCycleListener;
25+
import org.apache.activemq.artemis.utils.CertificateUtil;
2426
import org.apache.activemq.artemis.utils.ProxyProtocolUtil;
2527

2628
public class NettyServerConnection extends NettyConnection {
@@ -29,6 +31,8 @@ public class NettyServerConnection extends NettyConnection {
2931

3032
private final String router;
3133

34+
private X509Certificate[] certificates;
35+
3236
public NettyServerConnection(Map<String, Object> configuration,
3337
Channel channel,
3438
ServerConnectionLifeCycleListener listener,
@@ -54,6 +58,13 @@ public String getRouter() {
5458
return router;
5559
}
5660

61+
public X509Certificate[] getPeerCertificates() {
62+
if (certificates == null) {
63+
certificates = CertificateUtil.getCertsFromChannel(channel);
64+
}
65+
return certificates;
66+
}
67+
5768
/**
5869
* {@return a string representation of the remote address of this connection; if this connection was made via the
5970
* proxy protocol then this will be the original address, not the proxy address}

artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
3737
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
3838
import org.apache.activemq.artemis.core.management.impl.ManagementRemotingConnection;
39-
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
4039
import org.apache.activemq.artemis.core.security.CheckType;
4140
import org.apache.activemq.artemis.core.security.Role;
4241
import org.apache.activemq.artemis.core.security.SecurityAuth;
@@ -58,6 +57,7 @@
5857
import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException;
5958
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
6059
import org.apache.activemq.artemis.utils.ByteUtil;
60+
import org.apache.activemq.artemis.utils.CertificateUtil;
6161
import org.apache.activemq.artemis.utils.CompositeAddress;
6262
import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
6363
import org.apache.activemq.artemis.utils.collections.TypedProperties;

artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
import org.apache.activemq.artemis.core.postoffice.QueueBinding;
6868
import org.apache.activemq.artemis.core.postoffice.RoutingStatus;
6969
import org.apache.activemq.artemis.core.protocol.core.CoreRemotingConnection;
70-
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
7170
import org.apache.activemq.artemis.core.remoting.CloseListener;
7271
import org.apache.activemq.artemis.core.remoting.FailureListener;
7372
import org.apache.activemq.artemis.core.security.CheckType;
@@ -104,6 +103,7 @@
104103
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
105104
import org.apache.activemq.artemis.spi.core.protocol.SessionCallback;
106105
import org.apache.activemq.artemis.utils.ByteUtil;
106+
import org.apache.activemq.artemis.utils.CertificateUtil;
107107
import org.apache.activemq.artemis.utils.CompositeAddress;
108108
import org.apache.activemq.artemis.utils.JsonLoader;
109109
import org.apache.activemq.artemis.utils.PrefixUtil;

artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/NotificationActiveMQServerPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.apache.activemq.artemis.api.core.SimpleString;
2424
import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
2525
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
26-
import org.apache.activemq.artemis.core.remoting.CertificateUtil;
2726
import org.apache.activemq.artemis.core.server.ActiveMQServer;
2827
import org.apache.activemq.artemis.core.server.MessageReference;
2928
import org.apache.activemq.artemis.core.server.ServerConsumer;
@@ -32,6 +31,7 @@
3231
import org.apache.activemq.artemis.core.server.management.Notification;
3332
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin;
3433
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
34+
import org.apache.activemq.artemis.utils.CertificateUtil;
3535
import org.apache.activemq.artemis.utils.collections.TypedProperties;
3636
import org.slf4j.Logger;
3737
import org.slf4j.LoggerFactory;

artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
import org.slf4j.Logger;
3636
import org.slf4j.LoggerFactory;
3737

38-
import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
38+
import static org.apache.activemq.artemis.utils.CertificateUtil.getCertsFromConnection;
3939

4040
/**
4141
* This implementation delegates to the JAAS security interfaces.

artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCallbackHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
*/
1717
package org.apache.activemq.artemis.spi.core.security.jaas;
1818

19-
import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
20-
import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getPeerPrincipalFromConnection;
19+
import static org.apache.activemq.artemis.utils.CertificateUtil.getCertsFromConnection;
20+
import static org.apache.activemq.artemis.utils.CertificateUtil.getPeerPrincipalFromConnection;
2121

2222
import java.io.IOException;
2323
import java.security.Principal;

artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/CertificateUtil.java renamed to artemis-server/src/main/java/org/apache/activemq/artemis/utils/CertificateUtil.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.activemq.artemis.core.remoting;
17+
package org.apache.activemq.artemis.utils;
1818

1919
import javax.net.ssl.SSLPeerUnverifiedException;
2020
import java.io.ByteArrayInputStream;
@@ -27,6 +27,7 @@
2727
import io.netty.channel.ChannelHandler;
2828
import io.netty.handler.ssl.SslHandler;
2929
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection;
30+
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyServerConnection;
3031
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
3132
import org.apache.activemq.artemis.spi.core.remoting.Connection;
3233
import org.slf4j.Logger;
@@ -52,7 +53,9 @@ public static X509Certificate[] getCertsFromConnection(RemotingConnection remoti
5253
X509Certificate[] certificates = null;
5354
if (remotingConnection != null) {
5455
Connection transportConnection = remotingConnection.getTransportConnection();
55-
if (transportConnection instanceof NettyConnection nettyConnection) {
56+
if (transportConnection instanceof NettyServerConnection nettyServerConnection) {
57+
certificates = nettyServerConnection.getPeerCertificates();
58+
} else if (transportConnection instanceof NettyConnection nettyConnection) {
5659
certificates = getCertsFromChannel(nettyConnection.getChannel());
5760
}
5861
}
@@ -87,7 +90,7 @@ public static Principal getLocalPrincipalFromConnection(NettyConnection nettyCon
8790
return result;
8891
}
8992

90-
private static X509Certificate[] getCertsFromChannel(Channel channel) {
93+
public static X509Certificate[] getCertsFromChannel(Channel channel) {
9194
Certificate[] plainCerts = null;
9295
ChannelHandler channelHandler = channel.pipeline().get("ssl");
9396
if (channelHandler != null && channelHandler instanceof SslHandler sslHandler) {

tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/ssl/CertificateAuthenticationSslTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
import org.apache.activemq.artemis.utils.RandomUtil;
3636
import org.apache.activemq.artemis.utils.Wait;
3737
import org.eclipse.paho.mqttv5.client.MqttClient;
38+
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
39+
import org.eclipse.paho.mqttv5.common.MqttException;
3840
import org.eclipse.paho.mqttv5.common.MqttMessage;
41+
import org.eclipse.paho.mqttv5.common.packet.MqttProperties;
3942
import org.junit.jupiter.api.TestTemplate;
4043
import org.junit.jupiter.api.Timeout;
4144
import org.junit.jupiter.api.extension.ExtendWith;
@@ -120,4 +123,55 @@ public void messageArrived(String topic, MqttMessage message) {
120123
producer.publish(topic, body, 1, false);
121124
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
122125
}
126+
127+
// Send will message using mutual TLS with certificate-based authentication
128+
@TestTemplate
129+
@Timeout(DEFAULT_TIMEOUT_SEC)
130+
void testSendWillMessage() throws Exception {
131+
final String willSenderId = RandomUtil.randomUUIDString();
132+
final String willTopic = RandomUtil.randomUUIDString();
133+
final byte[] willBody = RandomUtil.randomBytes(32);
134+
135+
CountDownLatch latch = new CountDownLatch(1);
136+
MqttClient willSender = createConnectedWillSender(willSenderId, willTopic, willBody);
137+
MqttClient willConsumer = createConnectedWillConsumer(latch, willTopic, willBody);
138+
139+
if (protocol.equals(WSS)) {
140+
willSender.disconnectForcibly(0, 0, false);
141+
} else {
142+
// for some reason disconnectForcibly doesn't work in this case so we trick the broker into sending the LWT
143+
getSessionStates().get(willSenderId).setFailed(true);
144+
getSessionStates().get(willSenderId).setAttached(false);
145+
}
146+
assertTrue(latch.await(3, TimeUnit.SECONDS));
147+
willConsumer.disconnect();
148+
}
149+
150+
private MqttClient createConnectedWillSender(String clientId, String topic, byte[] body) throws MqttException {
151+
MqttClient willSender = createPahoClient(protocol, clientId);
152+
MqttConnectionOptions options = getSslMqttConnectOptions();
153+
options.setSessionExpiryInterval(5L);
154+
options.setWill(topic, new MqttMessage(body));
155+
MqttProperties willMessageProperties = new MqttProperties();
156+
willMessageProperties.setWillDelayInterval(1L);
157+
options.setWillMessageProperties(willMessageProperties);
158+
willSender.connect(options);
159+
return willSender;
160+
}
161+
162+
private MqttClient createConnectedWillConsumer(CountDownLatch latch,
163+
String topic,
164+
byte[] body) throws MqttException {
165+
MqttClient willConsumer = createPahoClient(protocol, RandomUtil.randomUUIDString());
166+
willConsumer.connect(getSslMqttConnectOptions());
167+
willConsumer.setCallback(new DefaultMqttCallback() {
168+
@Override
169+
public void messageArrived(String topic, MqttMessage message) {
170+
assertEqualsByteArrays(body, message.getPayload());
171+
latch.countDown();
172+
}
173+
});
174+
willConsumer.subscribe(topic, AT_LEAST_ONCE);
175+
return willConsumer;
176+
}
123177
}

0 commit comments

Comments
 (0)