Blog
clouderatlsopensslkeytoolsecurity

Cloudera 요구사항에 맞는 self-signed TLS 인증서 만들기

sha256WithRSAEncryption, SAN, clientAuth + serverAuth 를 모두 만족하는 내부 CA 와 호스트별 인증서를 openssl 로 만드는 방법.

Data Dynamics2026年4月11日17 min read
This post is not yet translated. The original Korean version is shown below.

운영 환경에서는 공인 CA 발급 인증서를 쓰는 것이 원칙이지만, 내부 테스트 클러스터나 PoC 환경에서는 자체 CA 를 만들어 호스트별 인증서를 발급 하는 편이 훨씬 빠릅니다. 이 글은 Cloudera CDP Private Cloud Base 의 TLS 인증서 요구사항 문서 를 만족하는 self-signed 인증서를 생성하는 정확한 절차를 정리합니다. 자동화가 필요하면 TheOpenCloudEngine/certificates-generator 를 바로 쓰면 되고, 이 글은 그 스크립트가 내부적으로 무엇을 하는지, 왜 각 옵션이 필요한지를 한 줄씩 풀어서 설명합니다.

1. Cloudera 가 요구하는 조건

문서에 명시된 조건을 먼저 한 표로 정리합니다. 생성 스크립트가 이 모든 조건을 동시에 만족해야 합니다.

항목요구사항
Signature algorithmsha256WithRSAEncryption (SHA-256)
Extended Key Usage (EKU)clientAuthserverAuth 모두 포함
Key UsageDigitalSignature, KeyEncipherment
Subject Alternative Name (SAN)필수. 최소한 호스트의 FQDN 포함. 로드밸런서 FQDN 도 필요 시 추가
Wildcard 인증서사용 불가. 클러스터 노드마다 고유 인증서 발급
KeyStore한 KeyStore 에 PrivateKeyEntry하나만
KeyStore 비밀번호KeyStore 비밀번호와 인증서 비밀번호가 같거나 양쪽 모두 미설정. 클러스터 전체 노드에서 동일

추가로 문서가 권장하는 것은 "가능하면 공인 CA 를 쓰고 self-signed 는 피하라" 이지만, 이 글은 테스트/내부 환경 에서 self-signed 가 필요할 때의 정공법을 다룹니다.

2. 전체 흐름

                 [1] 내부 CA 생성


                 ca.key  +  ca.crt

           ┌──────────┼──────────┐
           ▼          ▼          ▼
   [2] host01       host02      host03
       .key          .key        .key
       .csr          .csr        .csr
       .ext (SAN)    .ext        .ext
       .crt (CA서명) .crt         .crt


              [3] 검증 (openssl verify)


              [4] JKS TrustStore / PKCS12 KeyStore 로 패키징

Cloudera 노드는 결국 두 종류의 키스토어 를 필요로 합니다.

  • KeyStore — 그 노드 자신의 private key + 서명된 leaf cert. 노드마다 다름.
  • TrustStore — 공통. 내부 CA 의 public cert 만 들어감. 모든 노드가 동일한 파일을 공유.

3. 내부 CA 생성

CA 는 한 번만 만들면 끝입니다. 만료를 길게 (10년 / 3650일) 잡고 키 크기를 4096 비트 로 크게 가져가는 것이 관행입니다. 일단 CA 를 분실하면 모든 하위 인증서가 의미 없어지기 때문에 CA 키의 보안을 강하게 잡는 것이 맞습니다.

3.1 디렉터리 준비

mkdir -p certs/ca
cd certs/ca

3.2 CA 개인키

openssl genrsa -out ca.key 4096
chmod 600 ca.key

ca.key절대 외부로 유출되면 안 됩니다. 생성 즉시 권한을 600 으로 내려두고, 운영 배포 아티팩트에 포함되지 않도록 주의하세요.

3.3 CA self-signed 인증서

openssl req -x509 -new -nodes \
  -key ca.key \
  -sha256 \
  -days 3650 \
  -out ca.crt \
  -subj "/C=KR/ST=Gyeonggi-do/L=Yongin-si/O=Data Dynamics/OU=Infrastructure/CN=Data Dynamics Internal CA" \
  -extensions v3_ca \
  -config <(cat <<'EOF'
[req]
distinguished_name = req_dn
prompt = no
 
[req_dn]
CN = Data Dynamics Internal CA
 
[v3_ca]
basicConstraints = critical, CA:TRUE
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
EOF
)

여기서 중요한 포인트는 세 가지입니다.

  • -sha256: Cloudera 가 요구하는 signature algorithm 을 지정. 이 옵션이 없으면 openssl 기본값이 MD5 나 SHA1 일 수 있어서 검증을 통과하지 못합니다.
  • basicConstraints = critical, CA:TRUE: 이 인증서가 "다른 인증서를 서명할 수 있는 CA" 임을 명시. critical 플래그로 반드시 검사하게 만듭니다.
  • keyUsage = critical, keyCertSign, cRLSign: CA 역할에 필요한 최소 Key Usage 만 부여. 이 CA 는 "다른 인증서 서명" 과 "CRL 서명" 만 할 수 있고 자기 자신이 서버가 되지는 못합니다.

3.4 CA 인증서 확인

openssl x509 -in ca.crt -noout -text | \
  grep -A1 -E "Signature Algorithm|Basic Constraints|Key Usage|Subject:"

출력에 Signature Algorithm: sha256WithRSAEncryption, CA:TRUE, Certificate Sign, CRL Sign 이 모두 보이면 정상입니다.

4. 호스트별 인증서 발급

이제 클러스터 노드마다 반복할 단계입니다. hosts.txt 같은 파일에 호스트 목록을 적어두고 루프를 돌리는 것이 편합니다.

hosts.txt
---------
node01.example.com
node02.example.com
node03.example.com
cm-server.example.com

4.1 디렉터리와 경로

HOST="node01.example.com"
mkdir -p "certs/${HOST}"
cd "certs/${HOST}"

4.2 노드 개인키

openssl genrsa -out "${HOST}.key" 2048
chmod 600 "${HOST}.key"

노드 인증서는 2048 비트로 충분합니다. CA 와 달리 수명이 짧고 (보통 825일 이내, 이후 설명) 갱신 주기가 있기 때문에 4096 까지 키울 필요는 없습니다.

4.3 CSR (Certificate Signing Request)

openssl req -new \
  -key "${HOST}.key" \
  -out "${HOST}.csr" \
  -subj "/C=KR/ST=Gyeonggi-do/L=Yongin-si/O=Data Dynamics/OU=Infrastructure/CN=${HOST}"
  • CN=${HOST}: 과거에는 CN 을 hostname 으로 쓰는 것이 표준이었습니다. 지금도 관행적으로 채우긴 하지만, 현대 TLS 클라이언트(Java 11+, 최신 브라우저, gRPC 등)는 CN 을 무시하고 SAN 만 본다 는 점을 기억하세요. SAN 이 없으면 CN 만으로는 절대 검증되지 않습니다.

4.4 확장 파일 (.ext) — Cloudera 요구사항의 핵심

openssl 이 CSR 에 서명할 때 붙일 확장들을 별도 파일로 준비합니다. 이 파일이 Cloudera 요구사항을 만족하는지 여부를 결정 합니다.

cat > "${HOST}.ext" <<EOF
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
 
[alt_names]
DNS.1 = ${HOST}
DNS.2 = ${HOST%%.*}
EOF

각 필드가 Cloudera 요구사항의 어느 항목을 커버하는지 짚고 넘어갑니다.

ext 파일 필드Cloudera 요구사항
basicConstraints = CA:FALSE이 인증서는 leaf 이며 다른 인증서를 서명할 수 없음을 명시
keyUsage = digitalSignature, keyEncipherment요구되는 Key Usage 2종
extendedKeyUsage = serverAuth, clientAuthEKU 양쪽 필수. 하나라도 빠지면 Cloudera 서비스 일부가 인식 못함
subjectAltName = @alt_namesSAN 필수 조건
DNS.1 = ${HOST}FQDN 포함
DNS.2 = ${HOST%%.*}bare hostname (짧은 이름). 노드 간 통신에서 짧은 이름을 쓰는 환경을 위해

주의: 참고한 오픈소스 스크립트는 SAN 에 DNS.3 = *.example.com 같은 와일드카드 를 포함하지만, 이 글이 목표로 하는 Cloudera CDP Private Cloud Base 7.3.1 의 요구사항에는 "certificates must not use wildcards" 가 명시되어 있습니다. 와일드카드 SAN 은 넣지 말고 노드별로 정확한 FQDN 만 나열하세요. 로드밸런서를 앞에 두는 경우에는 DNS.3 = lb.example.com 처럼 LB FQDN 을 추가로 명시하면 됩니다.

4.5 CA 로 CSR 서명

openssl x509 -req \
  -in "${HOST}.csr" \
  -CA ../ca/ca.crt \
  -CAkey ../ca/ca.key \
  -CAcreateserial \
  -out "${HOST}.crt" \
  -days 825 \
  -sha256 \
  -extfile "${HOST}.ext"
  • -sha256: 서명 알고리즘을 SHA-256 으로 명시. Cloudera 요구사항의 핵심.
  • -days 825: 825일. 이는 현재 대부분의 브라우저와 라이브러리가 허용하는 leaf 인증서 최대 유효기간입니다 (Apple/Google 이 2020년 이후 브라우저에 적용한 상한). Cloudera 자체는 이 값을 강제하지 않지만, 같은 인증서를 감시 대시보드의 HTTPS UI 에서 신뢰시키려면 825일 이하로 맞추는 편이 안전합니다.
  • -extfile: 위에서 만든 확장 파일을 주입. 이게 없으면 openssl 은 기본 확장만 붙이고 EKU/SAN 이 모두 사라집니다.
  • -CAcreateserial: 첫 실행 시 CA 의 일련번호 파일(ca.srl)을 자동 생성. 두 번째 호스트부터는 -CAserial ../ca/ca.srl 로 대체해도 동일.

4.6 체인 파일 조립

배포 편의상 "leaf cert + CA cert" 를 한 파일로 합친 체인 파일 을 만들어 두는 것이 일반적입니다.

cat "${HOST}.crt" ../ca/ca.crt > "${HOST}-chain.pem"

또한 "private key + 체인" 을 한 파일로 합친 풀 번들 도 유용합니다 (nginx, HAProxy 등 일부 서버가 이 형식을 요구).

cat "${HOST}.key" "${HOST}.crt" ../ca/ca.crt > "${HOST}-full.pem"
chmod 600 "${HOST}-full.pem"

5. 생성된 인증서 검증

5.1 CA 가 정말로 서명했는가

openssl verify -CAfile ../ca/ca.crt "${HOST}.crt"

node01.example.com.crt: OK 가 나오면 통과입니다.

5.2 Cloudera 요구 확장이 다 들어갔는가

openssl x509 -in "${HOST}.crt" -noout -text | \
  grep -A1 -E "Signature Algorithm|Key Usage|Extended Key Usage|Subject Alternative Name"

출력에서 반드시 확인해야 할 것:

  • Signature Algorithm: sha256WithRSAEncryption — 두 번 나옵니다 (tbsCertificate 와 outer)
  • X509v3 Key Usage: criticalDigital Signature, Key Encipherment
  • X509v3 Extended Key Usage:TLS Web Server Authentication, TLS Web Client Authentication
  • X509v3 Subject Alternative Name:DNS:node01.example.com, DNS:node01

하나라도 빠져 있으면 .ext 파일과 -extfile 옵션을 다시 확인하세요.

6. KeyStore / TrustStore 패키징

Cloudera 서비스와 Java 기반 컴포넌트(HiveServer2, Impala coordinator, KMS 등) 는 Java KeyStore 형식 을 원합니다. openssl 이 만든 .key/.crt 를 JKS 로 옮기는 표준 절차는 다음과 같습니다.

6.1 PKCS12 로 먼저 묶기

openssl 은 JKS 를 직접 만들 수 없으므로 PKCS12 로 먼저 묶은 뒤 keytool 로 변환합니다.

openssl pkcs12 -export \
  -in "${HOST}-chain.pem" \
  -inkey "${HOST}.key" \
  -name "${HOST}" \
  -out "${HOST}.p12" \
  -password pass:"${STORE_PASSWORD}"
  • -name "${HOST}": KeyStore 내부 alias. 노드 식별용.
  • -password: 프로덕션에서는 env:STORE_PASSWORD 로 바꿔 환경변수에서 읽게 하는 편이 안전합니다.

6.2 JKS 로 변환

keytool -importkeystore \
  -srckeystore "${HOST}.p12" \
  -srcstoretype PKCS12 \
  -srcstorepass "${STORE_PASSWORD}" \
  -destkeystore "${HOST}.jks" \
  -deststoretype JKS \
  -deststorepass "${STORE_PASSWORD}" \
  -noprompt

Cloudera 문서의 "KeyStore password 와 certificate password 가 같거나 둘 다 미설정" 조건은 여기서 충족됩니다. srcstorepassdeststorepass 를 동일한 값으로 맞추세요. keypass 를 별도로 지정하지 않으면 keytool 은 자동으로 storepass 와 동일한 값을 쓰므로 이 조건이 자연스럽게 맞아떨어집니다.

6.3 공용 TrustStore 만들기 (CA 만)

TrustStore 는 클러스터 전체가 공유합니다. 여기에는 CA 인증서만 들어가며 private key 는 들어가지 않습니다.

keytool -importcert -noprompt \
  -file ../ca/ca.crt \
  -alias internal-ca \
  -keystore truststore.jks \
  -storepass "${TRUSTSTORE_PASSWORD}" \
  -storetype JKS

truststore.jks 한 파일을 모든 노드에 배포하면 됩니다.

7. 자동화 — 참고 스크립트

위 절차를 매번 손으로 돌리는 건 비효율적입니다. 저희가 이 글을 준비하면서 참고한 오픈소스 스크립트가 있습니다.

이 스크립트는 hosts.txt 를 입력으로 받아 위에서 설명한 3~6단계를 자동으로 반복 해서 certs/<host>/ 디렉터리 안에 .key, .csr, .crt, .ext, 체인 파일, 풀 번들까지 한꺼번에 생성합니다. 사용 순서는 이렇습니다.

git clone https://github.com/TheOpenCloudEngine/certificates-generator.git
cd certificates-generator
 
# hosts.txt 에 클러스터 노드 FQDN 을 한 줄씩 입력
cat > hosts.txt <<EOF
node01.example.com
node02.example.com
node03.example.com
cm-server.example.com
EOF
 
./generate_certs.sh

단, 그대로 운영에 쓰기 전에 아래 두 가지는 반드시 확인 하세요.

  • 스크립트 상단의 CA Subject (C, ST, O, CN) 가 조직 규칙에 맞는지. 기본값은 샘플이라 회사 정보로 수정이 필요합니다.
  • SAN 에 와일드카드 도메인 을 추가하는 부분이 있다면 제거. 앞서 설명했듯이 Cloudera CDP Private Cloud Base 7.3.1 은 wildcard 를 금지합니다. 노드별 FQDN 만 넣도록 루프를 수정하세요.

이 두 부분만 회사 환경에 맞게 손보면, 사실상 Cloudera 요구사항을 만족하는 인증서 묶음을 5분 안에 발급받을 수 있습니다.

8. 운영 주의사항

  • CA 키 보관: ca.key 는 암호화된 별도 볼트(HashiCorp Vault, AWS KMS, 사내 HSM 등)에 보관하는 것이 원칙입니다. 일반 Git 리포에 절대 커밋하지 마세요. 컴프로마이즈되면 이 CA 로 서명된 모든 노드 인증서 가 동시에 신뢰를 잃습니다.
  • 만료 모니터링: leaf 인증서(825일)뿐 아니라 CA 인증서(3650일)도 만료일을 모니터링해야 합니다. CA가 만료되면 재발급해야 하는 범위가 전 노드로 퍼집니다.
  • 일련번호(serial) 파일 관리: ca.srl 은 다음 발급에서 이어서 써야 합니다. 분실하거나 여러 팀이 병렬로 인증서를 발급하면 같은 일련번호가 두 인증서에 부여되어 충돌이 날 수 있습니다. 중앙에서 단일 CA 워크스테이션을 정해두는 편이 안전합니다.
  • 재발급 절차 미리 연습: "만료 2주 전에 갱신" 정책을 문서화해두고, 1년에 한 번 이상 실제 재발급을 돌려 절차가 유효한지 확인하세요. 첫 만료일이 닥쳐서야 스크립트를 찾는 것이 가장 흔한 장애 패턴입니다.

이 글은 인증서를 만드는 쪽만 다뤘습니다. 만든 CA 인증서를 JDBC 클라이언트 쪽에서 신뢰하도록 TrustStore 에 import 하고 실제로 Impala 에 연결하는 과정은 "Cloudera Impala JDBC 에 TLS + LDAP 인증 적용하기" 에서 이어집니다.

질문이나 사내 환경에서만 나타나는 특이 케이스가 있으면 알려주세요.

— Data Dynamics 엔지니어링 팀