Skip to content

Commit

Permalink
chore: support ssl in mqtt3 integ tests (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcosentino11 authored Nov 8, 2023
1 parent 7cbe567 commit 72d4d5c
Show file tree
Hide file tree
Showing 15 changed files with 81 additions and 36 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
<dependency>
<groupId>io.moquette</groupId>
<artifactId>moquette-broker</artifactId>
<version>0.15</version>
<version>0.17</version>
<scope>test</scope>
</dependency>
<!-- for running hivemq -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.aws.greengrass.mqtt.bridge.BridgeConfig;
import com.aws.greengrass.mqtt.bridge.MQTTBridge;
import com.aws.greengrass.mqtt.bridge.auth.MQTTClientKeyStore;
import com.aws.greengrass.mqtt.bridge.model.MqttVersion;
import com.aws.greengrass.util.Pair;
import lombok.Builder;
import lombok.Setter;
Expand Down Expand Up @@ -75,6 +76,13 @@ void GIVEN_mqtt5_bridge_WHEN_client_cert_changes_THEN_local_client_restarts() th
assertTrue(testContext.getLocalV5Client().getClient().getIsConnected());
}

@BridgeIntegrationTest(
withConfig = "mqtt3_config_ssl.yaml",
withBrokers = Broker.MQTT3, // TODO hivemq doesn't play nicely with v3 paho client for some reason
withLocalClientVersions = MqttVersion.MQTT3)
void GIVEN_bridge_with_ssl_WHEN_bridge_starts_THEN_client_connects_over_ssl() {
}

@BridgeIntegrationTest(
withConfig = "mqtt5_config_ssl.yaml",
withBrokers = Broker.MQTT5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.aws.greengrass.testcommons.testutilities.GGExtension;
import com.aws.greengrass.testcommons.testutilities.UniqueRootPathExtension;
import com.aws.greengrass.util.Pair;
import io.moquette.BrokerConstants;
import io.moquette.broker.Server;
import io.moquette.broker.config.IConfig;
import io.moquette.broker.config.MemoryConfig;
Expand Down Expand Up @@ -127,7 +126,8 @@ private static void launchKernel(Kernel kernel, ExtensionContext extensionContex

private static Server startMoquette() throws IOException {
IConfig brokerConf = new MemoryConfig(new Properties());
brokerConf.setProperty(BrokerConstants.PORT_PROPERTY_NAME, String.valueOf(8883));
brokerConf.setProperty(IConfig.PORT_PROPERTY_NAME, String.valueOf(8883));
brokerConf.setProperty(IConfig.ENABLE_TELEMETRY_NAME, "false");
Server moquette = new Server();
moquette.startServer(brokerConf);
return moquette;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public IoTCoreClient getIotCoreClient() {
public MQTTClient getLocalV3Client() {
MessageClient<MqttMessage> client = getFromContext(MQTTBridge.class).getLocalMqttClient();
if (!(client instanceof MQTTClient)) {
throw new RuntimeException("Excepted " + MQTTClient.class.getSimpleName()
throw new RuntimeException("Expected " + MQTTClient.class.getSimpleName()
+ " but got " + client.getClass().getSimpleName());
}
return (MQTTClient) client;
Expand All @@ -60,7 +60,7 @@ public MQTTClient getLocalV3Client() {
public LocalMqtt5Client getLocalV5Client() {
MessageClient<MqttMessage> client = getFromContext(MQTTBridge.class).getLocalMqttClient();
if (!(client instanceof LocalMqtt5Client)) {
throw new RuntimeException("Excepted " + MQTTClient.class.getSimpleName()
throw new RuntimeException("Expected " + LocalMqtt5Client.class.getSimpleName()
+ " but got " + client.getClass().getSimpleName());
}
return (LocalMqtt5Client) client;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
import com.aws.greengrass.mqtt.bridge.BridgeConfig;
import com.aws.greengrass.mqtt.bridge.MQTTBridge;
import com.aws.greengrass.mqtt.bridge.auth.MQTTClientKeyStore;
import com.aws.greengrass.mqtt.bridge.clients.LocalMqtt5Client;
import com.aws.greengrass.mqtt.bridge.clients.MQTTClient;
import com.aws.greengrass.mqtt.bridge.clients.MockMqttClient;
import com.aws.greengrass.mqtt.bridge.model.MqttVersion;
import com.aws.greengrass.mqttclient.MqttClient;
import io.moquette.BrokerConstants;
import io.moquette.broker.Server;
import io.moquette.broker.config.IConfig;
import io.moquette.broker.config.MemoryConfig;
Expand Down Expand Up @@ -73,10 +74,14 @@
import static com.aws.greengrass.componentmanager.KernelConfigResolver.CONFIGURATION_CONFIG_KEY;
import static com.aws.greengrass.testcommons.testutilities.ExceptionLogProtector.ignoreExceptionOfType;
import static com.aws.greengrass.testcommons.testutilities.ExceptionLogProtector.ignoreExceptionUltimateCauseOfType;
import static com.github.grantwest.eventually.EventuallyLambdaMatcher.eventuallyEval;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;

@SuppressWarnings("PMD.CouplingBetweenObjects")
public class BridgeIntegrationTestExtension implements AfterTestExecutionCallback, InvocationInterceptor, TestTemplateInvocationContextProvider {

private static final Logger logger = LogManager.getLogger(BridgeIntegrationTestExtension.class);
Expand All @@ -89,7 +94,7 @@ public class BridgeIntegrationTestExtension implements AfterTestExecutionCallbac

Server v3Broker;

MQTTClientKeyStore clientKeyStore = new InitOnceMqttClientKeyStore();
MQTTClientKeyStore clientKeyStore;

@Override
public boolean supportsTestTemplate(ExtensionContext context) {
Expand Down Expand Up @@ -118,6 +123,9 @@ public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContex
} else {
for (Broker brokerParam : brokerParams) {
for (MqttVersion versionParam : versionParams) {
if (versionParam == MqttVersion.MQTT5 && brokerParam == Broker.MQTT3) {
continue;
}
contexts.add(new BridgeIntegrationTestInvocationContext(brokerParam, versionParam, configFile));
}
}
Expand Down Expand Up @@ -150,12 +158,10 @@ public String getDisplayName(int invocationIndex) {
// initialize the context for parameterized test, regardless if
// BridgeIntegrationTestContext is a param of the test itself.
// there didn't appear to be a better place to put this...
if (context == null) {
context = new BridgeIntegrationTestContext();
context.broker = broker;
context.clientVersion = clientVersion;
context.configFile = configFile;
}
context = new BridgeIntegrationTestContext();
context.broker = broker;
context.clientVersion = clientVersion;
context.configFile = configFile;
return new BridgeIntegrationTestDisplayNameFormatter(broker, clientVersion, configFile).format();
}

Expand Down Expand Up @@ -190,6 +196,7 @@ public void interceptTestTemplateMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
initializeContext(extensionContext);
configureCertificates();

if (context.broker == Broker.MQTT3) {
configureContextForMqtt3Broker();
Expand Down Expand Up @@ -246,6 +253,16 @@ public void interceptTestTemplateMethod(Invocation<Void> invocation,
invocation.proceed();
}

private Certs configureCertificates() throws KeyStoreException {
Path serverKeystorePath = context.getRootDir().resolve("hivemq.jks");
Path serverTruststorePath = context.getRootDir().resolve("truststore.jks");
clientKeyStore = new InitOnceMqttClientKeyStore();
Certs certs = new Certs(clientKeyStore, serverKeystorePath, serverTruststorePath);
certs.initialize();
context.certs = certs;
return certs;
}

@Override
public void afterTestExecution(ExtensionContext extensionContext) {
if (kernel != null) {
Expand Down Expand Up @@ -293,14 +310,22 @@ private void customizeKernelContext(Kernel kernel) {
}

private void configureContextForMqtt3Broker() {
int brokerPort = 8883;
IConfig brokerConf = new MemoryConfig(new Properties());
brokerConf.setProperty(BrokerConstants.PORT_PROPERTY_NAME, String.valueOf(brokerPort));
int sslPort = 8883;
int tcpPort = 1883;
v3Broker = new Server();
context.setBrokerHost("localhost");
context.setBrokerTCPPort(brokerPort);
context.setBrokerSSLPort(sslPort);
context.setBrokerTCPPort(tcpPort);
context.startBroker = () -> {
try {
IConfig brokerConf = new MemoryConfig(new Properties());
brokerConf.setProperty(IConfig.SSL_PORT_PROPERTY_NAME, String.valueOf(sslPort));
brokerConf.setProperty(IConfig.PORT_PROPERTY_NAME, String.valueOf(tcpPort));
brokerConf.setProperty(IConfig.JKS_PATH_PROPERTY_NAME, context.certs.getKeystorePath().toAbsolutePath().toString());
brokerConf.setProperty(IConfig.KEY_STORE_PASSWORD_PROPERTY_NAME, context.certs.getServerKeystorePassword());
brokerConf.setProperty(IConfig.KEY_MANAGER_PASSWORD_PROPERTY_NAME, context.certs.getServerKeystorePassword());
brokerConf.setProperty(IConfig.ENABLE_TELEMETRY_NAME, "false");
brokerConf.setProperty(IConfig.PERSISTENCE_ENABLED_PROPERTY_NAME, "false");
v3Broker.startServer(brokerConf);
} catch (IOException e) {
fail(e);
Expand All @@ -309,20 +334,13 @@ private void configureContextForMqtt3Broker() {
context.stopBroker = v3Broker::stopServer;
}

private void configureContextForMqtt5Broker() throws KeyStoreException {
Path serverKeystorePath = context.getRootDir().resolve("hivemq.jks");
Path serverTruststorePath = context.getRootDir().resolve("truststore.jks");

Certs certs = new Certs(clientKeyStore, serverKeystorePath, serverTruststorePath);
certs.initialize();
context.certs = certs;

private void configureContextForMqtt5Broker() {
v5Broker = new HiveMQContainer(
DockerImageName.parse("hivemq/hivemq-ce").withTag("2023.2"))
.withFileSystemBind(serverKeystorePath.toAbsolutePath().toString(), "/opt/hivemq/hivemq.jks", BindMode.READ_ONLY)
.withFileSystemBind(serverTruststorePath.toAbsolutePath().toString(), "/opt/hivemq/truststore.jks", BindMode.READ_ONLY)
.withFileSystemBind(context.certs.getKeystorePath().toAbsolutePath().toString(), "/opt/hivemq/hivemq.jks", BindMode.READ_ONLY)
.withFileSystemBind(context.certs.getTrustorePath().toAbsolutePath().toString(), "/opt/hivemq/truststore.jks", BindMode.READ_ONLY)
.withHiveMQConfig(MountableFile.forClasspathResource("hivemq/config.xml"))
.withEnv("SERVER_JKS_PASSWORD", certs.getServerKeystorePassword())
.withEnv("SERVER_JKS_PASSWORD", context.certs.getServerKeystorePassword())
.withExposedPorts(8883, 1883)
.withLogLevel(Level.DEBUG);
context.startBroker = () -> {
Expand Down Expand Up @@ -376,6 +394,12 @@ private void overrideLocalClientVersion() throws ServiceLoadException {
.getConfig()
.lookup(CONFIGURATION_CONFIG_KEY, BridgeConfig.KEY_MQTT, BridgeConfig.KEY_VERSION)
.withValue(context.clientVersion.name());
Class<?> expectedMessageClient = context.clientVersion == MqttVersion.MQTT5
? LocalMqtt5Client.class : MQTTClient.class;
assertThat("local client version switched",
() -> context.getFromContext(MQTTBridge.class).getLocalMqttClient().getClass()
.isAssignableFrom(expectedMessageClient),
eventuallyEval(is(true)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public class Certs {
private KeyStore serverKeyStore;
private KeyStore serverTrustStore;
private final MQTTClientKeyStore clientKeyStore;
@Getter
private final Path keystorePath;
@Getter
private final Path trustorePath;

public Certs(MQTTClientKeyStore clientKeyStore,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
level: "DEBUG"
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
main:
dependencies:
- aws.greengrass.clientdevices.mqtt.Bridge
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
mqtt5RouteOptions:
mapping1:
noLocal: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
main:
dependencies:
- aws.greengrass.clientdevices.mqtt.Bridge
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
services:
aws.greengrass.Nucleus:
configuration:
logging:
level: "DEBUG"
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'ssl://localhost:8883'
main:
dependencies:
- aws.greengrass.clientdevices.mqtt.Bridge
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
mqttTopicMapping:
toIotCore:
topic: topic/toIotCore
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
mqttTopicMapping:
toLocal:
topic: topic/toLocal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
mqttTopicMapping:
toIotCore:
topic: topic/toIotCore
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
mqtt5RouteOptions:
toIotCore:
noLocal: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
aws.greengrass.clientdevices.mqtt.Bridge:
configuration:
brokerUri: 'tcp://localhost:8883'
brokerUri: 'tcp://localhost:1883'
mqtt5RouteOptions:
toIotCore:
retainAsPublished: true
Expand Down

0 comments on commit 72d4d5c

Please sign in to comment.