백엔드 데이터 인프라 114편. Kafka SSL/TLS 깊이 — Keystore·Truststore 생성, CA 인증서, mTLS (`ssl.client.auth=required`), TLS 1.3 vs 1.2, 인증서 갱신·rolling restart, 자주 발생하는 SSL 함정까지 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 114편이에요. 113편 에서 3축 종합 을 잡았다면, 이번 114편은 Encryption 의 깊이 — SSL/TLS (Transport Layer Security, 통신 암호화 표준).
SSL/TLS가 어렵게 느껴지는 이유
PKI (Public Key Infrastructure, 공개키 기반 인프라) · 인증서 · CA (Certificate Authority, 인증서 발급 기관) 같은 암호학 영역이라 한 번 설정해보면 흐름이 익숙해지지만 처음에는 막막한 게 사실이에요.
첫째, Keystore vs Truststore 헷갈림. 두 가지 모두 .jks (Java KeyStore, 자바 키 저장 포맷) 파일인데 용도가 다릅니다.
둘째, mTLS (mutual TLS, 양방향 인증) 와 일반 TLS 차이. server 만 인증할지, client 도 함께 인증할지의 trade-off가 갈리죠.
셋째, 인증서 갱신 운영. 만료되면 모든 연결이 거부되니까 자동화가 필수입니다.
이 글에서 keystore 생성부터 mTLS, 운영 패턴, 함정까지 짚어볼게요.
Keystore vs Truststore
Keystore — 내 신원
Keystore = 내 private key + 내 certificate
서버의 신원증명이에요. broker 가 client 에게 "나는 broker-1.example.com 이다" 라고 증명하는 용도.
Truststore — 내가 신뢰하는 CA
Truststore = 신뢰하는 CA 의 certificate
상대방 인증서를 신뢰할지 판단하는 기준입니다. broker 가 client 의 cert 를 검증할 때 이걸 봅니다.
비유하자면:
- Keystore = 내 신분증
- Truststore = 내가 신뢰하는 발급 기관 목록
인증서 생성 — keytool
Step 1: CA 인증서 생성 (자체 CA)
$ openssl genrsa -out ca-key 4096
$ openssl req -new -x509 -key ca-key -out ca-cert -days 3650 \
-subj "/CN=KafkaCA"
Step 2: Broker Keystore 생성
$ keytool -keystore broker-1.keystore.jks \
-alias broker-1 -validity 365 -genkey \
-keyalg RSA -keysize 2048 \
-dname "CN=broker-1.example.com,OU=Engineering,O=MyCompany,L=Seoul,C=KR" \
-storepass keystore-pass -keypass key-pass
여기서 CN (Common Name) 이 broker 의 hostname 입니다. 클라이언트가 인증서를 검증할 때 hostname 이 일치하는지 확인해요.
Step 3: CSR (Certificate Signing Request, 인증서 서명 요청) 생성
$ keytool -keystore broker-1.keystore.jks \
-alias broker-1 -certreq -file broker-1.csr \
-storepass keystore-pass
Step 4: CA 가 broker cert 발급
$ openssl x509 -req -CA ca-cert -CAkey ca-key \
-in broker-1.csr -out broker-1-cert -days 365 \
-CAcreateserial \
-extfile <(printf "subjectAltName=DNS:broker-1.example.com,DNS:broker-1")
subjectAltName (SAN) 은 추가 hostname을 담는 필드인데, 최신 브라우저나 java 는 이걸 필수로 요구합니다.
Step 5: CA 인증서를 broker keystore 에 import
$ keytool -keystore broker-1.keystore.jks \
-alias CARoot -import -file ca-cert \
-storepass keystore-pass -noprompt
$ keytool -keystore broker-1.keystore.jks \
-alias broker-1 -import -file broker-1-cert \
-storepass keystore-pass -noprompt
Step 6: Truststore 생성 (CA cert 만)
$ keytool -keystore kafka.truststore.jks \
-alias CARoot -import -file ca-cert \
-storepass trust-pass -noprompt
모든 broker 와 client 가 같은 truststore 를 쓰는 게 표준이에요.
Broker 측 설정
# Listeners
listeners=SSL://0.0.0.0:9093
advertised.listeners=SSL://broker-1.example.com:9093
# SSL
security.inter.broker.protocol=SSL
ssl.keystore.location=/etc/kafka/ssl/broker-1.keystore.jks
ssl.keystore.password=keystore-pass
ssl.key.password=key-pass
ssl.truststore.location=/etc/kafka/ssl/kafka.truststore.jks
ssl.truststore.password=trust-pass
# Protocol·cipher (현대)
ssl.protocol=TLSv1.3
ssl.enabled.protocols=TLSv1.3,TLSv1.2
ssl.keystore.type=JKS
# mTLS (client 인증)
ssl.client.auth=required # required / requested / none
ssl.client.auth 옵션은 세 가지로 나뉩니다:
required= client 인증서 필수 (mTLS)requested= client 인증서가 있으면 검증, 없어도 OKnone= client 인증서 X (server 만 인증)
Client 측 설정
Java/Spring Boot
spring:
kafka:
bootstrap-servers: broker-1.example.com:9093
properties:
security.protocol: SSL
ssl.truststore.location: /etc/kafka/ssl/kafka.truststore.jks
ssl.truststore.password: trust-pass
ssl.endpoint.identification.algorithm: https
ssl.endpoint.identification.algorithm=https 를 켜면 hostname 검증이 활성화돼서 보안이 강해집니다.
mTLS Client (client 인증서)
spring:
kafka:
properties:
ssl.keystore.location: /etc/kafka/ssl/client-1.keystore.jks
ssl.keystore.password: client-keystore-pass
ssl.key.password: client-key-pass
Client 도 자기 인증서와 truststore 가 모두 필요해요.
mTLS — Principal 추출
mTLS 환경에서는 client cert 의 DN (Distinguished Name, 인증서 고유 식별자) 이 그대로 Kafka principal 로 사용됩니다:
DN: CN=app-user-1,OU=Engineering,O=MyCompany
↓ ssl.principal.mapping.rules
Principal: User:app-user-1
ssl.principal.mapping.rules 로 DN 을 principal 로 매핑할 수 있어요:
ssl.principal.mapping.rules=RULE:^CN=(.*?),.*$/$1/L,DEFAULT
이 principal 이 ACL (Access Control List, 접근 제어 목록) 의 User 로 그대로 쓰이니까 mTLS 는 인증과 권한이 하나로 묶이는 셈이죠.
TLS Protocol·Cipher 선택
Protocol
ssl.enabled.protocols=TLSv1.3,TLSv1.2
- TLSv1.3 (권장) — 빠르고 안전
- TLSv1.2 — 호환성 (옛 클라이언트)
- TLSv1.1·1.0 — deprecated, 사용 X
Cipher Suite
ssl.cipher.suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,...
기본은 JVM 의 default 입니다. 현대 AEAD (Authenticated Encryption with Associated Data, 인증 결합 암호화) cipher 인 GCM 계열만 권장하고, CBC·RC4 같은 deprecated cipher 는 빼는 게 맞습니다.
인증서 운영 — Rolling Renewal
만료 모니터링
$ keytool -list -v -keystore broker-1.keystore.jks -storepass keystore-pass
...
Valid from: ... until: Fri Dec 31 23:59:59 KST 2026
만료 30~60일 전에 알람이 울리게 잡아두세요.
갱신 자동화
Option 1: Let's Encrypt + ACME → 자동 갱신
Option 2: HashiCorp Vault PKI → API 기반 발급
Option 3: AWS Certificate Manager / GCP Certificate Manager
Option 4: 자체 PKI + Ansible/Terraform 스크립트
Hot Reload (Kafka 2.2+)
ssl.truststore.location=/etc/kafka/ssl/kafka.truststore.jks
같은 파일 위치를 그대로 쓰면 broker 재시작 없이도 갱신이 가능해요. Kafka 가 주기적으로 reload 해주거든요.
ssl.truststore.location 동적 변경은 kafka-configs.sh --alter 로도 됩니다.
자주 일어나는 SSL 함정
1. Unable to find valid certification path
Client 의 truststore 에 CA cert 가 없거나, 있더라도 broker 가 쓰는 CA 와 다른 경우예요.
해결: client truststore 에 broker 가 사용한 CA cert 를 그대로 추가.
2. No subject alternative names matching IP address...
ssl.endpoint.identification.algorithm=https 가 켜져 있는데 cert 의 SAN 에 broker hostname 이 빠진 경우입니다.
해결: cert 발급 시 SAN 을 넣거나, 임시로 endpoint identification 을 비활성하는 방법도 있지만 보안이 약해져요.
3. SSL handshake failed
원인이 여럿이라 잡기 까다로워요:
- 인증서 만료
- Protocol mismatch (
ssl.enabled.protocols양쪽이 안 맞음) - Cipher suite mismatch
해결: 로그를 자세히 보고 양쪽 protocol·cipher 를 동일하게 맞추기.
4. mTLS 환경에서 client cert 없음
ssl.client.auth=required 인데 client 가 keystore 를 안 박은 경우.
해결: client 측 keystore 설정 다시 확인.
5. 인증서 만료
운영 사고 1순위예요. 모니터링과 자동 갱신은 선택이 아니라 필수.
6. 자체 CA 의 trust 확장
모든 client 와 broker 의 truststore 에 같은 CA 를 등록해둬야 합니다. 한 곳이라도 빠지면 그 연결은 끊겨요.
성능 부담
- SSL 활성 = 처리량 ~15~30% 하강 (CPU 부담)
- TLSv1.3 < TLSv1.2 부담 (handshake 빠름)
- AEAD cipher (GCM) = HW accel (AES-NI) 활용 가능
- TLS termination proxy (HAProxy 등) 로 broker 부담 분리 가능
한계·실무 함정 (요약)
1. PLAINTEXT listener 와 혼합
listeners=PLAINTEXT://9092,SSL://9093
방화벽으로 9092 를 안 막으면 우회가 그대로 열려요. 운영은 SSL 전용으로 가는 게 맞습니다.
2. SSL endpoint identification 비활성
ssl.endpoint.identification.algorithm=
빈 값으로 두면 hostname 검증이 빠져서 MITM (Man-in-the-Middle, 중간자 공격) 에 노출됩니다. 반드시 https 로 두세요.
3. Self-signed cert 운영
학습용은 괜찮지만 운영은 조직 CA 나 public CA 로 가야 합니다.
4. Inter-broker SSL 누락
security.inter.broker.protocol=PLAINTEXT 로 두고 client 만 SSL 을 쓰면 broker 사이는 평문이에요. inter-broker 도 SSL.
시험 직전 한 번 더 — Kafka SSL 함정 압축 노트
- Keystore = 내 private key + 내 cert (broker 신원)
- Truststore = 신뢰하는 CA cert (상대 검증용)
- 모든 broker·client = 같은 truststore 권장
- 인증서 생성 = keytool + openssl 흐름 (CA → broker cert)
- SAN (subjectAltName) = hostname 추가 (현대 java 필수)
- CN = broker hostname
- Broker 설정 =
listeners=SSL://...·ssl.keystore.*·ssl.truststore.*·security.inter.broker.protocol=SSL ssl.client.auth=required(mTLS) ·requested·none- Client 설정 =
security.protocol=SSL+ truststore + (mTLS 시) keystore ssl.endpoint.identification.algorithm=https= hostname 검증 (MITM 방어)- mTLS Principal = client cert DN → Kafka principal
ssl.principal.mapping.rules로 DN → User 매핑- TLS Protocol = TLSv1.3 + TLSv1.2 (TLSv1.0·1.1 deprecated)
- Cipher = 현대 AEAD (GCM) 만 권장
- 인증서 운영 = 만료 모니터링 + 자동 갱신 (Let's Encrypt·Vault·ACM·자체)
- Hot Reload (Kafka 2.2+) = 재시작 없이 truststore 갱신 가능
- 자주 함정 —
Unable to find valid certification path(truststore CA 누락) · SAN missing · handshake failed · mTLS client cert 없음 · 만료 모니터링 누락 · 자체 CA trust 확장 - 성능 부담 = ~15~30% 하강 (TLS 1.3 < 1.2)
- 완화 = AES-NI HW accel · TLS termination proxy
- 함정 — PLAINTEXT 와 혼합 (방화벽 차단 필수)
- 함정 — endpoint identification 비활성 = MITM
- 함정 — Self-signed 운영
- 함정 — Inter-broker SSL 누락
공식 문서: Kafka Security SSL 에서 자세한 사양·예제를 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 109편 — Kafka Network Layer (NIO · Selector · Thread Pool)
- 110편 — Kafka Message Format (Record Batch v2 · 바이트 구조)
- 111편 — Kafka Consumer Rebalance Protocol (KIP-848 새 모델)
- 112편 — Kafka Transaction Protocol (EOS 내부 메커니즘)
- 113편 — Kafka Security Overview (3축 종합)
다음 글: