백엔드 데이터 인프라 114편 — Kafka SSL/TLS (Keystore · mTLS · 인증서 운영)

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 114편. Kafka SSL/TLS 깊이 — Keystore·Truststore 생성, CA 인증서, mTLS (`ssl.client.auth=required`), TLS 1.3 vs 1.2, 인증서 갱신·rolling restart, 자주 발생하는 SSL 함정까지 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 114편 — Kafka SSL/TLS (Keystore · mTLS · 인증서 운영)

이 글은 백엔드 데이터 인프라 시리즈 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 인증서가 있으면 검증, 없어도 OK
  • none = 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 에서 자세한 사양·예제를 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!