자율 프로젝트에서의 제일 큰 도전 과제는 MSA 도입이었는데,
더불어 DB도 EC2에 도커 이미지로 띄우는게 아니라 AWS DocumentDB랑 RDS를 써보기로했다.
이번 문제는 EC2에서 실행되고있는 Spring에서 DocumentDB에 있는 mongoDB로 데이터를 전송하면 TLS 인증 문제가 발생하는 것이었다.
🖥️ 개발 환경
Ubuntu 22.04
Spring 2.7
Java 11
👩🏻💻 전체 에러 로그
Timed out after 30000 ms while waiting for a server that matches com.mongodb.client.internal.MongoClientDelegate$1@33790563. Client view of cluster state is {type=REPLICA_SET, servers=[{address:27017=zootopia-mongodb.cluster-c2f2jpwzzxst.ap-northeast-2.docdb.amazonaws.com, 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 for a server that matches com.mongodb.client.internal.MongoClientDelegate$1@33790563. Client view of cluster state is {type=REPLICA_SET, servers=[{, 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}}]
👩🏻💻 핵심 에러 로그
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
👩🏻💻 원인 (확실하진 않으나 추정)
Java에서 https를 이용한 SSL/TLS 통신을 할 때, 통신하려는 서버의 인증서가 통신을 시도하는 자바 버전에서 신뢰할 수 있는 인증 기관으로 인식되지 못해 발생하는 것
👩🏻💻 해결 방법
- 사실 팀원이랑 동시에 에러 해결에 붙어서 해결 방법은 이것입니다!!! 할수 없음
- 그래서 우리가 행한 모든 행동을 기록하려 함
1️⃣ AWS 공식 문서 참고
- Ubuntu에서 script.sh 작성
- truststorePassword → 별도 설정
- 실제로 /tmp/ 경로는 존재했으나 /certs/는 없어서 mkdir certs를 통해 폴더 생성 해줌
- 스크립트 파일 실행 ./script.sh
- 스크립트 실행 시, 권한 때문에 deny되면 chmod +x script.sh
- 그 외 권한 문제 검색 참고
mydir=/tmp/certs
truststore=${mydir}/rds-truststore.jks
storepassword=
curl -sS "<https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem>" > ${mydir}/global-bundle.pem
awk 'split_after == 1 {n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1}{print > "rds-ca-" n ".pem"}' < ${mydir}/global-bundle.pem
for CERT in rds-ca-*; do
alias=$(openssl x509 -noout -text -in $CERT | perl -ne 'next unless /Subject:/; s/.*(CN=|CN = )//; print')
echo "Importing $alias"
keytool -import -file ${CERT} -alias "${alias}" -storepass ${storepassword} -keystore ${truststore} -noprompt
rm $CERT
done
rm ${mydir}/global-bundle.pem
echo "Trust store content is: "
keytool -list -v -keystore "$truststore" -storepass ${storepassword} | grep Alias | cut -d " " -f3- | while read alias
do
expiry=`keytool -list -v -keystore "$truststore" -storepass ${storepassword} -alias "${alias}" | grep Valid | perl -ne 'if(/until: (.*?)\\n/) { print "$1\\n"; }'`
echo " Certificate ${alias} expires in '$expiry'"
done
- Spring 프로젝트 DocumentDBConfig 작성
@Configuration
public class DocumentDBConfig {
private MongoProperties properties;
public static final String KEY_STORE_TYPE = "/tmp/certs/rds-truststore.jks";
public static final String DEFAULT_KEY_STORE_PASSWORD = <truststorePassword>;
public DocumentDBConfig(final MongoProperties properties) {
super();
this.properties = properties;
}
@Bean
public MongoClientSettings mongoClientSettings() {
setSslProperties();
return MongoClientSettings.builder()
.applyToSslSettings(builder -> builder.enabled(true))
.build();
}
private static void setSslProperties() {
System.setProperty("javax.net.ssl.trustStore", KEY_STORE_TYPE);
System.setProperty("javax.net.ssl.trustStorePassword",
DEFAULT_KEY_STORE_PASSWORD);
}
@Bean
public MongoPropertiesClientSettingsBuilderCustomizer mongoPropertiesCustomizer(final MongoProperties properties, final Environment environment) {
return new MongoPropertiesClientSettingsBuilderCustomizer(properties, environment);
}
}
2️⃣ Ubuntu에서 인증서 관련 설정
1. InstallCert.java 다운
2. Ubuntu 환경에 해당 파일을 복사해줌 (MovaXterm은 로컬에서 바로 복사가 가능한데 일반 터미널에서 하는 방법은 검색해보시길,,!)
- 우리 프로젝트의 경우
- /usr/lib/jvm/java-11-openjdk-amd64/lib/security/ 였음
여기서 ${JAVA_HOME}은 java가 설치된 위치
찾는 방법은 이 링크 참고
(https://codingdog.tistory.com/entry/%EC%9A%B0%EB%B6%84%ED%88%AC-java-%EC%84%A4%EC%B9%98%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B4%85%EC%8B%9C%EB%8B%A4)
sudo mv [경로]/InstallCert.java ${JAVA_HOME}/jre/lib/security/
3. 옮겨진 파일 위치로 들어가서, 컴파일 해줌
sudo javac -d . InstallCert.java
4. 컴파일 됐으면 파일 실행
sudo java -cp . InstallCert [IP/HOST]
- IP/HOST에는 우리 서비스 주소를 넣어줬음
5. https 보안 적용된 주소로 접속해서 보안인증서 다운
→ 주소창 옆 자물쇠눌러서 이 연결은 안전합니다 > 인증서가 유효함 > 세부사항 > 내보내기
6. ubuntu로 인증서 복사
7. 아래 명령어 입력
sudo keytool -importcert -file _.heartpath.site.crt -keystore /usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts -storepass changeit -noprompt
1번과 2번 과정이 동시다발로 이뤄져서 무엇이 문제다!는 말 못하지만, 예상컨데 TLS인증서 못찾음 + 자바가 인증서를 신뢰하지 못함 둘 다가 원인인 것 같다.
각자 설정을 마치고 테스트했을때 TLS관련 오류는 더이상 뜨지않고 mongoDB에 데이터가 잘 들어갔음을 확인했다.
23.11.29 추가
1번이나 2번 둘중에 하나만 해도 상관없음. 다만 적용하면 FCM이 오류나는 현상 발생,,,,ㅎ
참고자료
https://docs.aws.amazon.com/ko_kr/documentdb/latest/developerguide/connect_programmatically.html