카프카와 스프링부트가 SSL인증서를 통한 보안 연결에 있어서 세팅한 정보는 다음과같다.
스프링부트
Private Key와 TrustStore에 카프카가 제공할 SSL인증서를 추가한다.
카프카
Private Key와 스프링부트에게 제공할 SSL인증서를 소지한다.
1 스프링부트에서 Kafka로 연결 요청
2 Kafka 서버의 SSL 핸드셰이크 시작
3 스프링부트가 Kafka 인증서 검증
4 클라이언트 인증서 제출 (상호 TLS)
5 Kafka가 클라이언트 인증서 검증
6 SSL/TLS 핸드셰이크 완료 및 보안 연결 수립
위 과정이 성공시 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에 등록하고 생성
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에서 필요한 내용들도 모두 공식 문서에서 찾아볼 수 있지만
구글링을 통해 찾은 내용이 더 많다.
공식문서는 양도 너무 많고 필요한 설정값인지 아닌지를 알기가 쉽지가 않다.
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를 선언해줘야 했다.