Cloudera Impala JDBC 에 TLS + LDAP 인증 적용하기
PKIX 에러에서 해방되기. 인증서 체인 추출, 전용 TrustStore 만들기, Impala JDBC URL 작성까지 순수 JDBC로 구현하기.
개발 환경에서는 평문으로 잘 붙던 Cloudera Impala 가 운영 환경에서는 PKIX path building failed 에러와 함께 연결이 안 되는 상황을 현장에서 자주 봅니다. 이 글은 Cloudera Impala JDBC Connector (Simba 기반 상용 드라이버) 만 사용하고, 프레임워크 없이 순수 JDBC DriverManager 로 TLS + LDAP 인증을 적용해 Impala 에 접속하는 전체 과정을 정리합니다.
TL;DR
- 클러스터 관리자에게 root CA 인증서 PEM 파일 (
ca.pem) 을 요청해서 받도록 한다. keytool로 애플리케이션 전용 TrustStore (JKS) 를 새로 만들어 CA 를 import 한다.- JDBC URL 에
SSL=1;SSLTrustStore=...;AuthMech=3를 붙이고UID/PWD/SSLTrustStorePwd는Properties로 분리한다. Class.forName("com.cloudera.impala.jdbc.Driver")후DriverManager.getConnection().SELECT 1이 실행하면 완료.
1. 준비물
시작 전에 아래가 모두 있어야 합니다. 하나라도 없으면 클러스터 관리자에게 요청하세요.
| # | 항목 | 예시 |
|---|---|---|
| 1 | Cloudera Impala JDBC Connector JAR | ImpalaJDBC42.jar + 함께 제공되는 의존성 JAR가 있다면 추가 |
| 2 | TLS 가 켜진 Impala daemon 엔드포인트 | impalad-lb.corp.example.com:21050 |
| 3 | 클러스터의 root CA 인증서 (PEM) | ca.pem |
| 4 | LDAP 계정 | UID / PWD. 형식은 관리자에게 확인 |
| 5 | JDK 8 이상 | java -version |
체인 전체가 아니라 root CA 만 있으면 충분합니다.
2. TLS 인증서를 TrustStore 에 import
2.1 왜 애플리케이션 전용 TrustStore 를 쓰는가
JDK 의 cacerts 에 직접 넣는 방법도 있지만 다음 이유로 권장하지 않습니다.
- 같은 JDK 를 쓰는 다른 애플리케이션 전체에 영향을 줄 수 있음
- JDK 를 업그레이드하면
cacerts가 교체되어 import 가 사라짐 - 컨테이너 이미지를 매번 다시 빌드해야 함
대안은 애플리케이션과 함께 배포하는 전용 TrustStore 파일 (JKS) 을 새로 만드는 것입니다. JDK 교체에 영향이 없고, 보안 경계도 명확해지며, Docker 이미지에는 COPY truststore.jks /app/ 한 줄이면 끝납니다.
2.2 인증서 확보
방법 1 — 관리자에게 PEM 파일 받기 (권장)
받은 파일이 올바른지 먼저 확인합니다.
openssl x509 -in ca.pem -noout -subject -issuer -datesSubject: 와 Issuer: 가 같으면 root CA 입니다.
방법 2 — openssl s_client 로 서버에서 직접 추출
openssl s_client -connect impalad-lb.corp.example.com:21050 \
-showcerts </dev/null 2>/dev/null \
| awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' \
> impala-chain.pem체인에서 root CA 를 찾으려면 각 인증서의 subject/issuer 를 비교합니다.
csplit -z -s -f cert- -b '%02d.pem' impala-chain.pem \
'/-----BEGIN CERTIFICATE-----/' '{*}'
for f in cert-*.pem; do
echo "=== $f ==="
openssl x509 -in "$f" -noout -subject -issuer
doneSubject: 와 Issuer: 가 동일한 파일이 root CA 입니다.
2.3 keytool 로 TrustStore 만들기
이 글의 핵심 단계입니다.
keytool -importcert -noprompt \
-file ca.pem \
-alias cloudera-root-ca \
-keystore ./truststore.jks \
-storepass 'CHANGE_ME_STRONG_PASSPHRASE' \
-storetype JKS각 옵션의 의미는 다음과 같습니다.
| 옵션 | 설명 |
|---|---|
-importcert | X.509 인증서를 keystore 에 추가 |
-noprompt | 프롬프트 건너뛰기. 스크립트에서 필수 |
-file | 넣을 인증서 파일 |
-alias | keystore 내부 식별자. 같은 alias 로 다시 넣으면 덮어쓰기됨 |
-keystore | TrustStore 파일 경로. 없으면 새로 생성 |
-storepass | keystore 암호. 환경변수로 주입 권장 |
-storetype JKS | 포맷 명시. JDK 9+ 기본은 PKCS12 이므로 명시하는 편이 안전 |
2.4 TrustStore 검증
만든 TrustStore 에 CA 가 제대로 들어갔는지 확인합니다.
keytool -list -v \
-keystore ./truststore.jks \
-storepass 'CHANGE_ME_STRONG_PASSPHRASE' \
-alias cloudera-root-ca출력에서 확인할 포인트:
Owner:가 받은 root CA 의 DN 과 일치Valid from ... until:만료일SHA-256fingerprint 를 관리자가 알려준 값과 대조 (MITM 방어의 최후 보루)
2.5 자주 놓치는 함정
배포 전에 한 번 훑어보세요.
- Root CA 만 넣었는가?
-alias가 기존 것과 중복되지 않는가- TrustStore 경로가 실행 시점에 접근 가능한가. 상대경로 주의
- 인증서 만료 모니터링이 있는가
-storepass가 소스코드에 하드코딩되지 않았는가
3. Impala JDBC URL 작성
3.1 URL 템플릿
jdbc:impala://impalad-lb.corp.example.com:21050;AuthMech=3;SSL=1;SSLTrustStore=/app/truststore.jks3.2 파라미터 설명
| 파라미터 | 값 | 설명 |
|---|---|---|
AuthMech | 3 | User Name And Password. LDAP 바인드 인증을 의미 |
SSL | 1 | TLS 활성화. 반드시 대문자 SSL=1. 소문자 ssl=true 는 Apache Hive JDBC 문법이며 Cloudera 드라이버에서 인식 안 됨 |
SSLTrustStore | /app/truststore.jks | 앞서 만든 TrustStore 의 절대경로 |
3.3 보안 민감 정보는 URL 에 넣지 말 것
UID/PWD 와 SSLTrustStorePwd 는 URL 에 넣지 마세요. JDBC URL 은 로그·스레드 덤프·에러 스택트레이스에 흔히 찍힙니다. java.util.Properties 로 분리합니다.
Properties props = new Properties();
props.setProperty("UID", System.getenv("IMPALA_LDAP_UID"));
props.setProperty("PWD", System.getenv("IMPALA_LDAP_PWD"));
props.setProperty("SSLTrustStorePwd", System.getenv("IMPALA_TRUSTSTORE_PWD"));Cloudera 드라이버는 URL 파라미터와 Properties 를 동일하게 인식합니다.
4. 순수 JDBC 로 연결
프레임워크 없이 최소로 재현 가능한 예제입니다. main 메서드에 넣고 바로 실행할 수 있습니다.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
public class ImpalaJdbcSmokeTest {
private static final String DRIVER_CLASS =
"com.cloudera.impala.jdbc.Driver";
// 비밀 정보는 포함하지 않는다.
private static final String JDBC_URL =
"jdbc:impala://impalad-lb.corp.example.com:21050"
+ ";AuthMech=3"
+ ";SSL=1"
+ ";SSLTrustStore=/app/truststore.jks";
public static void main(String[] args) throws Exception {
Class.forName(DRIVER_CLASS);
Properties props = new Properties();
props.setProperty("UID", requireEnv("IMPALA_LDAP_UID"));
props.setProperty("PWD", requireEnv("IMPALA_LDAP_PWD"));
props.setProperty("SSLTrustStorePwd", requireEnv("IMPALA_TRUSTSTORE_PWD"));
try (Connection conn = DriverManager.getConnection(JDBC_URL, props);
PreparedStatement ps = conn.prepareStatement("SELECT 1 AS ok");
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
System.out.println("connected, result = " + rs.getInt("ok"));
}
}
}
private static String requireEnv(String name) {
String v = System.getenv(name);
if (v == null || v.isEmpty()) {
throw new IllegalStateException("환경변수 " + name + " 가 비어있습니다.");
}
return v;
}
}실행:
export IMPALA_LDAP_UID='alice'
export IMPALA_LDAP_PWD='...'
export IMPALA_TRUSTSTORE_PWD='CHANGE_ME_STRONG_PASSPHRASE'
java -cp 'ImpalaJDBC42.jar:libs/*:.' ImpalaJdbcSmokeTestconnected, result = 1 이 출력되면 TLS + LDAP 경로가 전부 통과한 것입니다.
디버그가 필요할 때
연결이 실패하면 JVM 에 SSL handshake 로그를 켜면 원인이 거의 항상 보입니다.
java -Djavax.net.debug=ssl:handshake \
-cp 'ImpalaJDBC42.jar:libs/*:.' \
ImpalaJdbcSmokeTestServerHello, Certificate, ServerHelloDone 단계의 출력을 보면 서버가 보낸 체인과 TrustStore 가 연결되는지 확인할 수 있습니다. 운영 배포 후에는 로그량 때문에 반드시 끄세요.
5. 자주 만나는 에러와 해결
PKIX path building failed
원인: Java 가 서버 인증서를 신뢰 체인까지 연결하지 못함. TrustStore 에 root CA 가 없거나 잘못된 파일을 가리키는 경우가 대부분입니다.
keytool -list로 alias 가 실제로 들어있는지 확인- JDBC URL 의
SSLTrustStore경로가 실행 시점 기준으로 맞는지 확인 - JVM 옵션(
-Djavax.net.ssl.trustStore=...) 과 URL 파라미터가 둘 다 있으면 URL 이 우선
Unrecognized SSL message, plaintext connection?
원인: 클라이언트는 TLS 로 말하는데 서버가 평문으로 응답. 포트 번호가 틀렸거나, TLS 가 아닌 포트에 SSL=1 을 붙인 경우입니다.
- TLS 가 켜진 Impala daemon 포트를 관리자에게 재확인
- HTTP transport 환경이라면
TransportMode=http;HttpPath=...가 추가로 필요한지 확인
No subject alternative names matching IP address
원인: 서버 인증서의 SAN 에 접속 hostname 이 빠져 있습니다. 내부 LB FQDN 이 인증서에 없는 경우가 대부분입니다.
- 관리자에게 SAN 에 LB FQDN 을 추가해 재발급 요청 (정공법)
AllowHostNameCNMismatch=1로 우회는 가능하지만 MITM 방어를 포기하는 설정이므로 운영 금지
Invalid operation: LDAP authentication failed
원인: TLS 는 통과했고 UID/PWD 가 서버에 도달했지만 LDAP 바인드가 실패했습니다.
- UID 형식 확인. sAMAccountName(
alice) /DOMAIN\alice/ DN(uid=alice,...) 중 어느 형식인지 관리자에게 재확인 - 비밀번호에 세미콜론·따옴표·공백이 있으면 반드시
Properties방식으로 분리 (URL 에 넣지 말 것) - 계정 잠금 상태인지 AD 관리자에게 확인
Could not open client transport
원인: TCP/SASL 수준에서 실패. 네트워크 이슈인 경우가 많습니다.
telnet impalad-lb.corp.example.com 21050로 TCP 자체가 열리는지 확인- 사내 프록시/방화벽에서 해당 포트 허용 여부 확인
- 클러스터가 실제로 Kerberos 를 강제하는데
AuthMech=3으로 붙고 있는 상황일 수도 있음. 관리자에게 실제 요구 인증 방식 재확인
6. 운영 체크리스트
개발 PC 에서는 되지만 운영 배포 며칠 뒤 터지는 시나리오를 막기 위한 리스트입니다.
- TrustStore 파일이 배포 파일(JAR/WAR/Docker 이미지)에 포함되어 함께 배포되는가
storepass가 환경변수 또는 Secret Store 로 주입되는가- JDBC URL 이 로그에 찍혀도 비밀 정보가 포함되지 않도록
Properties로 분리되었는가 - 인증서 만료일 모니터링이 있는가 (
openssl x509 -in ca.pem -noout -enddate) - Cloudera Impala JDBC Connector 버전이 고정되어 있는가
- CI 에서 실제 Impala 엔드포인트로
SELECT 1smoke test 가 돌고 있는가 - 연결 실패 시 실패 카운트 기반 알람이 있는가 (일시적 네트워크 지터와 구분)
여기까지 따라오셨다면 로컬에서 Cloudera Impala 에 TLS + LDAP 로 붙고, 문제가 생겼을 때 어느 층에서 실패했는지 진단하는 도구도 갖춘 것입니다.
— Data Dynamics 엔지니어링 팀