Kafka, Spring SSL 연결

박찬섭·2025년 3월 6일

kafka

목록 보기
4/4

시나리오

카프카와 스프링부트가 SSL인증서를 통한 보안 연결에 있어서 세팅한 정보는 다음과같다.

스프링부트
Private KeyTrustStore에 카프카가 제공할 SSL인증서를 추가한다.

카프카
Private Key와 스프링부트에게 제공할 SSL인증서를 소지한다.

1 스프링부트에서 Kafka로 연결 요청

  • 스프링부트 클라이언트는 SSL/TLS 연결을 생성하면서 Kafka에 접속을 시도한다.

2 Kafka 서버의 SSL 핸드셰이크 시작

  • Kafka는 SSL 리스너(포트 9094)에서 클라이언트 연결 요청을 수신한다.
  • Kafka는 SSL 핸드셰이크의 일환으로 자신의 인증서를 클라이언트(스프링부트)에게 전송한다.

3 스프링부트가 Kafka 인증서 검증

  • 스프링부트는 수신한 Kafka의 인증서를 자신의 truststore와 비교하여 신뢰할 수 있는지 확인한다.
    이 과정에서 Kafka 인증서가 신뢰되면, 클라이언트는 핸드셰이크 절차를 계속 진행한다.

4 클라이언트 인증서 제출 (상호 TLS)

  • 스프링부트는 자신의 keystore에 저장된 SSL 인증서를 Kafka로 전달한다.

5 Kafka가 클라이언트 인증서 검증

  • Kafka는 스프링부트가 제출한 인증서를 자신의 신뢰할 수 있는 인증서 목록(예: 사전 등록된 인증서 또는 TrustStore를 통해 관리되는 인증서)과 비교하여 검증한다.
    클라이언트 인증서가 유효하다면, Kafka는 인증을 승인한다.

6 SSL/TLS 핸드셰이크 완료 및 보안 연결 수립

  • 양측 모두 서로의 인증서를 성공적으로 검증하면, SSL/TLS 핸드셰이크가 완료된다.

위 과정이 성공시 SSL인증서를 통한 보안 연결이 성공했다고 볼 수 있다.


개인 키, SSL 인증서 생성

로컬 환경에서 테스트로 진행했기 때문에 인증서도 자체인증 인증서이고,
스프링부트와 카프카가 서로 동일한 개인 키와 SSL 인증서를 사용했다.

스프링부트는 호스트머신에서 생성한 개인 키, SSL 인증서를 그대로 사용했고,
도커는 마운트를 통해 호스트머신에서 생성한 파일 그대로 마운트했다.

openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt -subj "//CN=localhost"

개인 키 server.key와, SSL 인증서인 server.crt 파일을 생성한다.

openssl pkcs12 -export -in server.crt -inkey server.key -out keystore.p12 -name myalias -passout pass:qwer1234

server.key와 server.crt를 이용하여 keyStore 생성

keytool -import -trustcacerts -alias myalias -file server.crt -keystore truststore.p12 -storetype pkcs12 -storepass qwer1234 -noprompt

server.crt를 trustStore에 등록하고 생성


Docker-compose.yml

services:
  kafka:
    image: apache/kafka:latest
    container_name: kafka
    ports:
      - "9092:9092"
      - "9094:9094"
    volumes:
      - "./secrets:/etc/kafka/secrets"
      - "./data:/var/lib/kafka/data"
    command: "sh -c 'set -x; /etc/kafka/docker/configure && /etc/kafka/docker/run'"
    user: root
    environment:
      KAFKA_KRAFT_MODE: 'true'
      CLUSTER_ID: "550e8400-e29b-41d4-a716-446655440000"
      KAFKA_SSL_KEYSTORE_TYPE: PKCS12
      KAFKA_SSL_TRUSTSTORE_TYPE: PKCS12

      KAFKA_LISTENERS: PLAINTEXT://kafka:9092, CONTROLLER://kafka:9093, SSL://kafka:9094
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092, SSL://localhost:9094
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT, CONTROLLER:PLAINTEXT, SSL:SSL
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_LOG_DIRS: /var/lib/kafka/data
      KAFKA_PROCESS_ROLES: broker, controller
      KAFKA_NODE_ID: 1
      KAFKA_SSL_KEYSTORE_FILENAME: keystore.p12
      KAFKA_SSL_KEYSTORE_LOCATION: /etc/kafka/secrets/keystore.p12
      KAFKA_SSL_KEYSTORE_PASSWORD: qwer1234
      KAFKA_SSL_KEY_PASSWORD: qwer1234

      KAFKA_SSL_TRUSTSTORE_FILENAME: truststore.p12
      KAFKA_SSL_TRUSTSTORE_LOCATION: /etc/kafka/secrets/truststore.p12
      KAFKA_SSL_TRUSTSTORE_PASSWORD: qwer1234

      KAFKA_SSL_CLIENT_AUTH: "required"

      KAFKA_SSL_KEY_CREDENTIALS: kafka_sslkey_creds.txt
      KAFKA_SSL_KEYSTORE_CREDENTIALS: kafka_keystore_creds.txt
      KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kafka_truststore_creds.txt

포트는 ssl인증서를 통한 보안 연결만을 원할 시 9094포트만 사용하면 된다.
일단 우선

command: "sh -c 'set -x; /etc/kafka/docker/configure && /etc/kafka/docker/run'"
#configure폴더내 sh 명령어를 출력 및 디버깅용

user: root
#root로 설정하지 않으면 SSL파일에 권한을 얻지 못해 실행되지 않는다.

KAFKA_SSL_LISTENERS: SSL://kafka:9094
#ports로 9094:9094로 포팅하였기에 9094포트는 SSL을 사용하는걸로 설정

KAFKA_ADVERTISED_LISTENERS: SSL://localhost:9094
#외부에 노출될 URI 세팅값

KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT, CONTROLLER:PLAINTEXT, SSL:SSL
#SSL 요청에 대한 매핑

KAFKA_SSL_KEYSTORE_FILENAME: keystore.p12
KAFKA_SSL_KEYSTORE_LOCATION: /etc/kafka/secrets/keystore.p12
KAFKA_SSL_KEYSTORE_PASSWORD: qwer1234

KAFKA_SSL_TRUSTSTORE_FILENAME: truststore.p12
KAFKA_SSL_TRUSTSTORE_LOCATION: /etc/kafka/secrets/truststore.p12
KAFKA_SSL_TRUSTSTORE_PASSWORD: qwer1234

KAFKA_SSL_KEY_CREDENTIALS: kafka_sslkey_creds.txt
#개인 키를 사용할때 필요한 암호 정보를 담고 있음

KAFKA_SSL_KEYSTORE_CREDENTIALS: kafka_keystore_creds.txt
KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kafka_truststore_creds.txt

# keyStore: 개인 키와 SSL인증서를 저장할 파일 이름, 위치, 비밀번호
# trustStore: 신뢰할 수 있는 SSL인증서를 저장할 파일 이름, 위치, 비밀번호 


#밑에 2 가지 옵션은 솔직히 왜 필요한지는 모르겠다. 애초에 비밀번호를 위에서 입력했기 때문에
#필요없을거라 생각하고 지웠을때 카프카가 실행되지 않았음, 필요한 필수 옵션이듯

이 Docker-compose.yml에서 필요한 내용들도 모두 공식 문서에서 찾아볼 수 있지만
구글링을 통해 찾은 내용이 더 많다.
공식문서는 양도 너무 많고 필요한 설정값인지 아닌지를 알기가 쉽지가 않다.


application.yml

spring:
  application:
    name: kafka

  kafka:
    bootstrap-servers: localhost:9094
    security:
      protocol: SSL
    ssl-keystore-type: PKCS12
    ssl-keystore-location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/keystore.p12"
    ssl-keystore-password: qwer1234
    ssl-truststore-type: PKCS12
    ssl-truststore-location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/truststore.p12"
    ssl-truststore-password: qwer1234

    consumer:
      group-id: test
      auto-offset-reset: earliest
      properties:
        security:
          protocol: SSL
        ssl.keystore.type: PKCS12
        ssl.keystore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/keystore.p12"
        ssl.keystore.password: qwer1234
        ssl.truststore.type: PKCS12
        ssl.truststore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/truststore.p12"
        ssl.truststore.password: qwer1234
    producer:
      retries: 3
      acks: all
      properties:
        security:
          protocol: SSL
        ssl.keystore.type: PKCS12
        ssl.keystore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/keystore.p12"
        ssl.keystore.password: qwer1234
        ssl.truststore.type: PKCS12
        ssl.truststore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/truststore.p12"
        ssl.truststore.password: qwer1234
    topic:
      name: test-topic
      partitions: 3
      replication-factor: 1

값 설정이 정말 어려웠다.
스프링 카프카 공식문서
위는 공식문서인데 아무리 찾아보려해도 ssl 관련 yml설정값들을 찾아 볼 수가 없었다.

security:
      protocol: SSL
    ssl-keystore-type: PKCS12
    ssl-keystore-location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/keystore.p12"
    ssl-keystore-password: qwer1234
    ssl-truststore-type: PKCS12
    ssl-truststore-location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/truststore.p12"
    ssl-truststore-password: qwer1234

위 값들은 AdminClient을 생성하기 위한 값들이다.
카프카또한 스프링부트에서 SSL인증서를 받아서 인증이 필요하기 때문에 keyStore, trustStore 모두 필요하다.

consumer:
      group-id: test
      auto-offset-reset: earliest
      properties:
        security:
          protocol: SSL
        ssl.keystore.type: PKCS12
        ssl.keystore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/keystore.p12"
        ssl.keystore.password: qwer1234
        ssl.truststore.type: PKCS12
        ssl.truststore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/truststore.p12"
        ssl.truststore.password: qwer1234
        
producer:
      retries: 3
      acks: all
      properties:
        security:
          protocol: SSL
        ssl.keystore.type: PKCS12
        ssl.keystore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/keystore.p12"
        ssl.keystore.password: qwer1234
        ssl.truststore.type: PKCS12
        ssl.truststore.location: "C:/Users/qkrckstjq/Desktop/backend/kafka/secrets/truststore.p12"
        ssl.truststore.password: qwer1234

처음에는 kafka 바로 밑에 선언된 keystore, trustStore 값들로 Consumer, Producer 모두
위 값들을 사용 할 거다. 라고 생각했는데 Consumer, Producer 모두 각각 keyStore, trustStore를 선언해줘야 했다.

profile
백엔드 개발자를 희망하는

0개의 댓글