This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch 23182 in repository https://gitbox.apache.org/repos/asf/camel.git
commit a42445fadd622cc1f4f18c3de7d126382653c2fa Author: Andrea Cosentino <[email protected]> AuthorDate: Thu Mar 12 09:44:13 2026 +0100 camel-mongodb: Add TLS integration test with test-infra - Add MongoDBLocalContainerTLSService to test-infra-mongodb that starts a standalone mongod with --tlsMode requireTLS using pre-generated self-signed certificates mounted via classpath resource mapping - Add MongoDbSslConnectionIT integration test that validates end-to-end TLS connectivity using Camel's SSLContextParameters with a JKS truststore containing the test CA certificate - Include test certificate resources: CA cert (ca.pem), combined server cert+key (server.pem), and JKS truststore (ca-truststore.jks) Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Andrea Cosentino <[email protected]> --- .../integration/MongoDbSslConnectionIT.java | 163 +++++++++++++++++++++ .../services/MongoDBLocalContainerTLSService.java | 109 ++++++++++++++ .../infra/mongodb/services/ssl/ca-truststore.jks | Bin 0 -> 1206 bytes .../camel/test/infra/mongodb/services/ssl/ca.pem | 20 +++ .../test/infra/mongodb/services/ssl/server.pem | 48 ++++++ 5 files changed, 340 insertions(+) diff --git a/components/camel-mongodb/src/test/java/org/apache/camel/component/mongodb/integration/MongoDbSslConnectionIT.java b/components/camel-mongodb/src/test/java/org/apache/camel/component/mongodb/integration/MongoDbSslConnectionIT.java new file mode 100644 index 000000000000..f5e92f9a6fbc --- /dev/null +++ b/components/camel-mongodb/src/test/java/org/apache/camel/component/mongodb/integration/MongoDbSslConnectionIT.java @@ -0,0 +1,163 @@ +/* + * 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.camel.component.mongodb.integration; + +import javax.net.ssl.SSLContext; + +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mongodb.MongoDbComponent; +import org.apache.camel.support.jsse.KeyStoreParameters; +import org.apache.camel.support.jsse.SSLContextParameters; +import org.apache.camel.support.jsse.TrustManagersParameters; +import org.apache.camel.test.infra.core.CamelContextExtension; +import org.apache.camel.test.infra.core.DefaultCamelContextExtension; +import org.apache.camel.test.infra.core.annotations.ContextFixture; +import org.apache.camel.test.infra.core.annotations.RouteFixture; +import org.apache.camel.test.infra.core.api.ConfigurableContext; +import org.apache.camel.test.infra.core.api.ConfigurableRoute; +import org.apache.camel.test.infra.mongodb.services.MongoDBLocalContainerTLSService; +import org.bson.Document; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Integration test that validates TLS connectivity to MongoDB using Camel's SSLContextParameters. + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class MongoDbSslConnectionIT implements ConfigurableContext, ConfigurableRoute { + + private static final String DATABASE = "test"; + private static final String COLLECTION = "camelTest"; + + @Order(1) + @RegisterExtension + static MongoDBLocalContainerTLSService service = new MongoDBLocalContainerTLSService(); + + @Order(2) + @RegisterExtension + static CamelContextExtension contextExtension = new DefaultCamelContextExtension(); + + private MongoClient mongo; + private MongoCollection<Document> testCollection; + + @ContextFixture + @Override + public void configureContext(CamelContext context) throws Exception { + SSLContextParameters sslContextParameters = createSslContextParameters(); + context.getRegistry().bind("sslContextParameters", sslContextParameters); + + SSLContext sslContext = sslContextParameters.createSSLContext(context); + MongoClientSettings settings = MongoClientSettings.builder() + .applyConnectionString(new ConnectionString(service.getReplicaSetUrl())) + .applyToSslSettings(builder -> { + builder.enabled(true); + builder.context(sslContext); + builder.invalidHostNameAllowed(true); + }) + .build(); + mongo = MongoClients.create(settings); + + MongoDatabase db = mongo.getDatabase(DATABASE); + testCollection = db.getCollection(COLLECTION, Document.class); + testCollection.drop(); + testCollection = db.getCollection(COLLECTION, Document.class); + + context.getComponent("mongodb", MongoDbComponent.class).setMongoConnection(null); + context.getRegistry().bind("myDb", mongo); + } + + @RouteFixture + @Override + public void createRouteBuilder(CamelContext context) throws Exception { + context.addRoutes(new RouteBuilder() { + public void configure() { + String baseUri = String.format( + "mongodb:myDb?hosts=%s&database=%s&collection=%s" + + "&sslContextParameters=#sslContextParameters" + + "&tlsAllowInvalidHostnames=true", + service.getConnectionAddress(), DATABASE, COLLECTION); + + from("direct:insert").to(baseUri + "&operation=insert"); + from("direct:count").to(baseUri + "&operation=count"); + } + }); + } + + @Test + public void testInsertOverTls() { + ProducerTemplate template = contextExtension.getProducerTemplate(); + + Document doc = new Document("scientist", "Einstein").append("tls", true); + Object result = template.requestBody("direct:insert", doc); + assertNotNull(result, "Insert result should not be null"); + + assertEquals(1L, testCollection.countDocuments(), + "Test collection should contain 1 document after insert"); + } + + @Test + public void testCountOverTls() { + ProducerTemplate template = contextExtension.getProducerTemplate(); + + Object result = template.requestBody("direct:count", "irrelevantBody"); + assertTrue(result instanceof Long, "Count result should be of type Long"); + } + + @AfterEach + void cleanCollection() { + if (testCollection != null) { + testCollection.drop(); + } + } + + @AfterAll + void cleanup() { + if (mongo != null) { + mongo.close(); + } + } + + private SSLContextParameters createSslContextParameters() { + KeyStoreParameters ksp = new KeyStoreParameters(); + ksp.setResource("org/apache/camel/test/infra/mongodb/services/ssl/ca-truststore.jks"); + ksp.setPassword("changeit"); + + TrustManagersParameters tmp = new TrustManagersParameters(); + tmp.setKeyStore(ksp); + + SSLContextParameters sslContextParameters = new SSLContextParameters(); + sslContextParameters.setTrustManagers(tmp); + + return sslContextParameters; + } +} diff --git a/test-infra/camel-test-infra-mongodb/src/main/java/org/apache/camel/test/infra/mongodb/services/MongoDBLocalContainerTLSService.java b/test-infra/camel-test-infra-mongodb/src/main/java/org/apache/camel/test/infra/mongodb/services/MongoDBLocalContainerTLSService.java new file mode 100644 index 000000000000..3c1cfc759e73 --- /dev/null +++ b/test-infra/camel-test-infra-mongodb/src/main/java/org/apache/camel/test/infra/mongodb/services/MongoDBLocalContainerTLSService.java @@ -0,0 +1,109 @@ +/* + * 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.camel.test.infra.mongodb.services; + +import java.time.Duration; + +import org.apache.camel.test.infra.common.LocalPropertyResolver; +import org.apache.camel.test.infra.common.services.ContainerEnvironmentUtil; +import org.apache.camel.test.infra.common.services.ContainerService; +import org.apache.camel.test.infra.mongodb.common.MongoDBProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +/** + * A TLS-enabled MongoDB container service using a standalone mongod with --tlsMode requireTLS. Uses pre-generated + * self-signed certificates mounted from classpath resources. + */ +public class MongoDBLocalContainerTLSService implements MongoDBService, ContainerService<GenericContainer<?>> { + + private static final Logger LOG = LoggerFactory.getLogger(MongoDBLocalContainerTLSService.class); + private static final int DEFAULT_MONGODB_PORT = 27017; + private static final String CERT_RESOURCE_PATH = "org/apache/camel/test/infra/mongodb/services/ssl"; + + private final GenericContainer<?> container; + + public MongoDBLocalContainerTLSService() { + this(LocalPropertyResolver.getProperty( + MongoDBLocalContainerInfraService.class, MongoDBProperties.MONGODB_CONTAINER)); + } + + public MongoDBLocalContainerTLSService(String imageName) { + container = initContainer(imageName); + } + + protected GenericContainer<?> initContainer(String imageName) { + GenericContainer<?> c = new GenericContainer<>(imageName); + + boolean fixedPort = ContainerEnvironmentUtil.isFixedPort(this.getClass()); + ContainerEnvironmentUtil.configurePort(c, fixedPort, DEFAULT_MONGODB_PORT); + + c.withClasspathResourceMapping(CERT_RESOURCE_PATH, "/etc/mongodb/ssl", BindMode.READ_ONLY) + .withCommand( + "mongod", + "--tlsMode", "requireTLS", + "--tlsCertificateKeyFile", "/etc/mongodb/ssl/server.pem", + "--tlsCAFile", "/etc/mongodb/ssl/ca.pem", + "--tlsAllowConnectionsWithoutCertificates", + "--bind_ip_all", + "--port", String.valueOf(DEFAULT_MONGODB_PORT)) + .waitingFor( + Wait.forLogMessage(".*Waiting for connections.*", 1) + .withStartupTimeout(Duration.ofSeconds(60))); + + return c; + } + + @Override + public String getReplicaSetUrl() { + return String.format("mongodb://%s:%s", container.getHost(), + container.getMappedPort(DEFAULT_MONGODB_PORT)); + } + + @Override + public String getConnectionAddress() { + return container.getHost() + ":" + container.getMappedPort(DEFAULT_MONGODB_PORT); + } + + @Override + public void registerProperties() { + System.setProperty(MongoDBProperties.MONGODB_URL, getReplicaSetUrl()); + System.setProperty(MongoDBProperties.MONGODB_CONNECTION_ADDRESS, getConnectionAddress()); + } + + @Override + public void initialize() { + LOG.info("Trying to start the MongoDB TLS service"); + container.start(); + registerProperties(); + LOG.info("MongoDB TLS service running at {}", getReplicaSetUrl()); + } + + @Override + public void shutdown() { + LOG.info("Stopping the MongoDB TLS container"); + container.stop(); + } + + @Override + public GenericContainer<?> getContainer() { + return container; + } +} diff --git a/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/ca-truststore.jks b/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/ca-truststore.jks new file mode 100644 index 000000000000..aac33cbc646d Binary files /dev/null and b/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/ca-truststore.jks differ diff --git a/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/ca.pem b/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/ca.pem new file mode 100644 index 000000000000..daa387fcbd77 --- /dev/null +++ b/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/ca.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUOZ8hm+Y5QKtHsD7RMI43+Tu4j5swDQYJKoZIhvcNAQEL +BQAwNTEPMA0GA1UEAwwGVGVzdENBMRUwEwYDVQQKDAxBcGFjaGUgQ2FtZWwxCzAJ +BgNVBAYTAlVTMB4XDTI2MDMxMjA4MjExOFoXDTM2MDMwOTA4MjExOFowNTEPMA0G +A1UEAwwGVGVzdENBMRUwEwYDVQQKDAxBcGFjaGUgQ2FtZWwxCzAJBgNVBAYTAlVT +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiJ5NrHfXc1CQDrttRl8o +9+/jSL0ZkVMEOWDi4vEoGbQPzVXjkpym7PAAR1KYlXwi9XDIBlsdFIExS0+UONQL +/K8h5P//nWkkf6xha0nnYsrsdVkaF37HnBJmmnRHWK1M9iU4doJDhgZ/LEpS7NPX +twKn00YsWPRVKhY267VgDUp5XUD3VlsnCUcroRDt0OrSIikWs21cOAlOFL/Kul3c +Uf2gUmWxJmY7Ybzra1cFdGijxRlp8b/kcrQeGKJFEwK+c2LIvLiy/0CUICjWsooy +8tY6A/FQ6/RQwU032D7lmk8IjcDW3MKB8Nzk1AuYvf9MHeDWRO2K/o+Yq9v9B/1L +fwIDAQABo1MwUTAdBgNVHQ4EFgQUENNC6Q9yf9UpLXawy8NnfLStEY8wHwYDVR0j +BBgwFoAUENNC6Q9yf9UpLXawy8NnfLStEY8wDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAQEAD9QaBH9ABcSBwMtk+fK/6g37ZWoegcdT4OK0+yWBaptg +1reT9gszCeJ6ghYJPVMeRkTeLSg4iGSF+Iu1g4i3KaAQjbXfaS2ThUSxN47ThZH/ +wAZjlzHVDn+xjIC3Lk1ve+kJHAXm3KxyZbXWMUAmP9RPPse8UeFYUuRVY0mygOEN +PppwYmXLYexsIYiV6WcVFTZ/d1mF3op6HriOA052pYqJtOOWWVjiCSK/V2QhYgWG +1cBX11dHhFr2Zd6I5IizqkkM81g+uxRBMYGGKoeF+AKaoby0ScDuMxMIhogBGSw4 +UVfedFOW0hkTpEOuEvgOhavZMLBGuH7vcsORU5HliQ== +-----END CERTIFICATE----- diff --git a/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/server.pem b/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/server.pem new file mode 100644 index 000000000000..6401f5177263 --- /dev/null +++ b/test-infra/camel-test-infra-mongodb/src/main/resources/org/apache/camel/test/infra/mongodb/services/ssl/server.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIIDWTCCAkGgAwIBAgIUGylDtkbSJkK76KTlkGaIhoiMgcAwDQYJKoZIhvcNAQEL +BQAwNTEPMA0GA1UEAwwGVGVzdENBMRUwEwYDVQQKDAxBcGFjaGUgQ2FtZWwxCzAJ +BgNVBAYTAlVTMB4XDTI2MDMxMjA4MjY0NFoXDTM2MDMwOTA4MjY0NFowODESMBAG +A1UEAwwJbG9jYWxob3N0MRUwEwYDVQQKDAxBcGFjaGUgQ2FtZWwxCzAJBgNVBAYT +AlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkn73xpfZ0XRHQDIO +DsRlfwfYsUH6UUxYpoZjT9Mkm3GiYG86S5P5QU1BL/2h8x9f4NpnsRRCUOrqpHKa +9f17lGAVGVIzJirIlBUZs1b038f6XPLPcP0sQc8RAkRTgG3dZ+FtakoDFYaX8NCr +/r6NDQxh3l0k4yEsP+C3x4eGRepCCwjifCCMtnqQwQ1fw2QK2v9wlQs3sFa6pFvt +SAi4vdWHESOk1/7+788WlhjxaSS0sE3Q9SbVd32b7tazD1Ril0VXl9I4ZKy+NLvI +1aOxpKq3X2DP+N64bKn930JUdoTSAZgcWxj7USMEyfB5ie+J4yZabrJn1ih4umXX +eE9bhwIDAQABo14wXDAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0O +BBYEFAzmsdFHWCMDPrWRlO6OycQfU0W3MB8GA1UdIwQYMBaAFBDTQukPcn/VKS12 +sMvDZ3y0rRGPMA0GCSqGSIb3DQEBCwUAA4IBAQB9ebGCp+0u8E0l4EGUMoOXeNRI +RnfTicAQN1NZE7s2pA2ZC5kiYVmJsq/X8LPcyBWqOVbLWmeWB8CbcM7R3J39WGu7 +1y/PuqwggRIY0MvJH0ZGYOsXpYKUoj0qhFJHV/4XLLnf9CDBZj82Ly/BfVgBDPx4 +JTcD/jT5QlI34raV8VtX5ePdyKsv9IZxJIxco+5Q6EjeU+pObB2IY6OQvNybbJwG +immH+jZWvtTpndlPpy2/Sf8w/jJOYO7u2OSntZPJAVirxIyBztK/PPPyz0QgvV2P +5ntvQh9DDSOPQYL55zH99ob8Ye4Qu9wGxxD0rumnXOj9wagaSZNx+6+COUEe +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCSfvfGl9nRdEdA +Mg4OxGV/B9ixQfpRTFimhmNP0ySbcaJgbzpLk/lBTUEv/aHzH1/g2mexFEJQ6uqk +cpr1/XuUYBUZUjMmKsiUFRmzVvTfx/pc8s9w/SxBzxECRFOAbd1n4W1qSgMVhpfw +0Kv+vo0NDGHeXSTjISw/4LfHh4ZF6kILCOJ8IIy2epDBDV/DZAra/3CVCzewVrqk +W+1ICLi91YcRI6TX/v7vzxaWGPFpJLSwTdD1JtV3fZvu1rMPVGKXRVeX0jhkrL40 +u8jVo7GkqrdfYM/43rhsqf3fQlR2hNIBmBxbGPtRIwTJ8HmJ74njJlpusmfWKHi6 +Zdd4T1uHAgMBAAECggEAEpJ3Lh2tNyEjMU/HOXcLmQWxIpPHPMxNhtsNtx6BCxXm +bIxdWxyI4o79PyzL/csR+CsoLypu19xYX/3JiHsY0jA2LI4fvux4nlFofzR8eexb +4LHFu8DU/gjW0q8/2M3U3mkVWn7EklOMarLBw1t7/VX/CFZNqV/YwMZn9itHyhAe +LQIUWTu67qA/vhZBuwvIh8ELmQZ0jVwtVDg9tbhKV3/LTLaW7E3ko3xT8DhIKOs4 +0UdK/NguaPme+qJEYudSsb4RXjjqH6+ahimTFr1h5bwFa5O/07pcZ4/ejitYy2w+ +bjxFifiqoqQhPoPJspmgFq7AJlsHAzuXVqKwh5tT4QKBgQDHNCz1W4xm0qiBHQv/ +we6LE77bIHSig7KcacOeds2scwmK8B6MYHKBJmwnx+oV5HBskM0ZwthHpkzNHW8n +VyQapcafA+tpYEVl15nWERzajk6Ca15AoFemuqx3vQ0nLkUBPV4xbnGgY0zeBjod +Idd5lyhwRyZnYK4Edi9LJp66JwKBgQC8Q6nE4Jxmhathg0CfNPHyGrwNJEp9lAXo +sk/m+5DrLQs3iLCPJFjeL2fJvMCODSCQBb2fVBoqgX6zmnGqtf6YDVNNwncHMw+p +5ipGcWHE83PxTYNbNoFtLY19EuHBR0r9Qmjpo2Rtuu1fJUraG1zUb3YSDXGdk2S2 +2AQ6MkwPoQKBgEWG5nI0o8p3mCyIUNnRfEq6d5DPwSW/xaVmHMrAOIUKGbiOmnrw +ZsbA/FreIcvGUZ7y40MsiIRpfMDSlysp9QX/+lUh7xZ2bYJgP+dBTcrShIBsrRbt +X+pnmS6po1+bfKY0Hx4tqCcMwZV0ou/sEeL0aT7W9oZ6bgJMpbEbJ6ddAoGBAJ9Y +beMTcY1c6hfY7eNS/s26TxyYcOwlU3MHKZYJqzlCoNHaQgaF7ynv2drohdo1xi/g +jATFPHhproH54OdqrxinfrC8Pd68Gy/kfjetU+FNZf8BaoLTeWydN7p7NtVOsGv3 +v7Cw+RnfM3ZqrBY7PrEXvkm9U0LaNE6GO92+IJ7BAoGBAI0xksuSax7zxiTQn61Z +ckktBadOVGlB6MdtR+NLcRXFW0Vt1uN5QjGBVQ4Hrjxv5AeFxdFt6V35HpnY+VLq +V6a/GvRM2rPedBctrRf53dhV0YLJXNEctolnxa2mFyilH7irA7n/6elx1WeQRJvL +8kAMd2/Xo1uX03M7GzTQWe+D +-----END PRIVATE KEY-----
