Currently, I am developing a Spring Boot Microservice and Trying to Deploy it over in Fargate using Cloud Formation. I am using packaging type as war and using DockerFile to Deploy it via CDK.
The Microservice is connecting to Document DB to write some application State, working fine in EC2 self-hosted MongoDB. But when I am going for Document DB It is failing.
Can anyone guide me with the steps required to get this entire thing done?
Currently, AWS ECS and Document Db are in the Same VPC. But when I am trying to connect over TLS Disabled mode , it is getting timeout exception. When I am Enabling TLS and trying to connect I am getting
org.springframework.dao.DataAccessResourceFailureException: Timed out after 30000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=<host>:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketWriteException: Exception sending message}, caused by {javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target}, caused by {sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target}, caused by {sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target}}]; nested exception is com.mongodb.MongoTimeoutException: Timed out after 30000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=<host>:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketWriteException: Exception sending message}, caused by {javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target}, caused by {sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target}, caused by {sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target}}]
How I have connected via Spring boot Application to Document DB?
public class TestApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
log.info("Entering here");
SSLContextHelper.setSslProperties();
SpringApplication.run(TestApplication .class, args);
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(InvestorOnboardingApplication.class);
}
protected static class SSLContextHelper {
private static final String DEFAULT_SSL_CERTIFICATE = "keys/rds-combined-ca-bundle.pem";
private static final String SSL_CERTIFICATE = "sslCertificate";
private static final String KEY_STORE_TYPE = "JKS";
private static final String KEY_STORE_PROVIDER = "SUN";
private static final String KEY_STORE_FILE_PREFIX = "sys-connect-via-ssl-test-cacerts";
private static final String KEY_STORE_FILE_SUFFIX = ".jks";
private static final String DEFAULT_KEY_STORE_PASSWORD = "changeit";
private static final String SSL_TRUST_STORE = "javax.net.ssl.trustStore";
private static final String SSL_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword";
private static final String SSL_TRUST_STORE_TYPE = "javax.net.ssl.trustStoreType";
private static void setSslProperties() {
try {
String sslCertificate = System.getProperty(SSL_CERTIFICATE);
if (StringUtils.isEmpty(sslCertificate)) {
sslCertificate = DEFAULT_SSL_CERTIFICATE;
}
log.info(" ssl certificate path {}", sslCertificate);
System.setProperty(SSL_TRUST_STORE, createKeyStoreFile(sslCertificate));
log.info(" ssl certificate file {}", createKeyStoreFile(sslCertificate));
System.setProperty(SSL_TRUST_STORE_TYPE, KEY_STORE_TYPE);
System.setProperty(SSL_TRUST_STORE_PASSWORD, DEFAULT_KEY_STORE_PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String createKeyStoreFile(String sslCertificate) throws Exception {
return createKeyStoreFile(createCertificate(sslCertificate)).getPath();
}
private static X509Certificate createCertificate(String sslCertificate) throws Exception {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
try (InputStream certInputStream = InvestorOnboardingApplication.class.getClassLoader()
.getResourceAsStream(sslCertificate)) {
return (X509Certificate) certFactory.generateCertificate(certInputStream);
}
}
private static File createKeyStoreFile(X509Certificate rootX509Certificate) throws Exception {
File keyStoreFile = File.createTempFile(KEY_STORE_FILE_PREFIX, KEY_STORE_FILE_SUFFIX);
try (FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath())) {
KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE, KEY_STORE_PROVIDER);
ks.load(null);
ks.setCertificateEntry("rootCaCertificate", rootX509Certificate);
ks.store(fos, DEFAULT_KEY_STORE_PASSWORD.toCharArray());
}
return keyStoreFile;
}
}
and MongoDB Connection as
spring.data.mongodb.uri: uri: mongodb://username:password@host:27017/invest?ssl=true&replicaSet=rs0&readpreference=secondaryPreferred
Please let me know what exactly I am missing over here.
You'll need the Amazon RDS CA certificate in the truststore. Basically, you'll have to build the Docker images with this truststore containing the certificate. See this blog that explains the process in more details.