카프카의 보안 설정
- 인증(authentication): 사용자가 누구인지 식별
- 인가(authorization): 사용자가 무엇을 할 수 있는지 결정
- 암호화(encryption): 누설과 위조로부터 데이터 보호
- 감사(auditing): 사용자가 무엇을 했는지, 하려 했는지 추적
- 쿼터(quotas): 자원을 얼마나 많이 사용할 수 있는지 조절
안전한 카프카 클러스터는 아래와 같은 특징을 가진다.
- 클라이언트 진정성(client authenticity): 클라이언트가 브로커로 연결 맺을 때, 브로커는 전송된 메시지가 진짜 그 클라이언트로부터 온 것인지 클라이언트 인증해야 함
- 서버 진정성(server authenticity): 리더 브로커로 메시지 보내기 전에, 클라이언트는 해당 연결이 실제 브로커와 맺어진게 맞는지 검증해야 함
- 기밀성(data privacy): 메시지가 전달되는 중간에 있는 모든 연결은 암호화되거나, 물리적으로 보호되어 외부에서 내용을 보거나 훔칠 수 없어야 함
- 무결성(data integrity): 내용물 변조 시 알아차릴 수 있어야 함
- 접근 제거(access control): 메시지를 로그에 쓰기 전, 리더 브로커는 클라이언트가 해당 토픽에 쓰기 권한이 있는지 확인해야 하며, 컨슈머에 메시지를 전달할 때 역시 브로커는 클라이언트가 해당 토픽에 읽기 권한이 있는지 체크해야 함
- 감사 가능성(auditability): 클라이언트들이 수행한 모든 작업을 보여주는 감사용 기록이 남아야 함
- 가용성(availability): 브로커는 몇몇 클라이언트가 사용 가능한 대역폭을 독차지하거나 브로커에 서비스 거부 공격 가하는 것을 방지하기 위해 쿼터와 제한을 두어야 함
보안 프로토콜
카프카는 2개의 표준 기술(TLS, SASL)을 사용해 4개의 보안 프로토콜을 지원한다.
| 종류 | 설명 |
| PLAINTEXT | 전송 계층에는 인증이 존재하지 않는다. 네트워크 안에서 인증 및 암호화가 필요하지 않을 정도로 민감하지 않은 데이터 처리할 때만 적합하다. |
| SSL | 선택적으로 클라이언트 SSL 인증을 수행할 수 있다. 암호화뿐 아니라 클라이언트/서버 인증도 지원되므로 안전하지 않은 네트워크에서 적절하다. |
| SASL_PLAINTEXT | SASL 인증과 PLAINTEXT 전송 계층이 합쳐진 것이다. 어떤 SASL 메커니즘은 서버 인증 역시 지원한다. 암호화는 지원하지 않으므로 사설 네트워크 안에서만 적합하다. |
| SASL_SSL | SASL 인증과 SSL 전송 계층이 합쳐진 것이다. 암호화 뿐 아니라 클라이언트/서버 인증도 지원되어 안전하지 않은 네트워크에서 적절하다. |
SASL vs SSL
SASL와 SSL은 용도가 약간 겹칠 뿐 목적 자체가 전혀 다른 기술이다.
SSL은 공개 키 암호화를 사용한 통신 프로토콜로 종단 사이에 교환되는 메시지를 안전하게 보호하는 것을 목표로 한다. 반면, SASL은 종단 간 사용할 인증 방식을 서로 합의할 수 있도록 해주는 프레임워크이다. 인증 방식을 합의하는 방법과 절차를 정의할 뿐, 구체적인 인증 작업은 특정 인증 프로토콜이 구현하는 것이다.
인증
인증은 서버와 클라이언트 사이에서 서로의 신원을 확인하는 과정이다.
SSL
카프카 리스너의 보안 프로토콜이 SSL 또는 SASL_SSL로 설정되면, 해당 리스너로 연결에 TLS 보안 전송 계층이 사용된다. TLS를 통해 연결이 생성되면, TLS 핸드셰이크 과정에서 인증, 암호 암호화 매개변수 교환, 암호화를 위한 공유 키 생성 등이 처리된다. SSL을 통한 모든 트래픽은 암호화되므로 안전하지 못한 네트워크에서 적절하다. 단, SSL 채널은 암호화되기 때문에 CPU 사용에 오버헤드를 초래하여 트래픽 패턴에 따라 2~30% 오버헤드가 발생할 수 있다.
SASL
SASL 인증은 서버가 챌린지를 내놓으면 클라이언트가 여기에 응답을 보내는 과정을 일정한 순서로 수행하는 식으로 동작한다. 카프카 브로커는 기본적으로 아래와 같은 SASL 메커니즘과 함께 기존 보안 인프라와 통합하기 위한 커스터마이즈 가능한 콜백을 지원한다.
- GSSAPI: SASL/GSSAPI를 사용하는 케르베로스 인증이 지원되며, Active Directory나 OpenLDAP과 같은 케르베로스 서버와 통합하는 데 사용될 수 있음
- PLAIN: 사용자 이름/비밀번호 인증, 보통 외부 비밀번호 저장소를 사용해서 비밀번호를 검증하는 서버측 커스텀 콜백과 함께 사용됨
- SCRAM-SHA-256 and SCRAM-SHA-512: 추가적인 비밀번호 저장소를 설정할 필요 없이 카프카 설치 후 바로 사용할 수 있는 사용자 이름/비밀번호 인증
- OAUTHBEARER: OAuth bearer 토큰을 사용한 인증. 보통 표준화된 OAuth 서버에서 부여된 토큰을 추출하고 검증하는 커스텀 콜백과 함께 사용됨
카프카는 자바 인증 및 인가 서비스(Java Authentication and Authorization Service, JAAS)를 사용해 SASL을 설정한다. 카프카가 지원하는 SASL 메커니즘은 콜백 핸들러를 사용해 서드 파티 인증 서버와 통합할 수 있다. 로그인 과정을 커스터마이즈하기 위해 브로커나 클라이언트에 로그인 콜백 핸들러를 설정할 수도 있고, 클라이언트가 제출한 자격 증명을 인증하기 위해 서버 콜백 핸들러를 사용할 수도 있으며, 클라이언트 자격 증명을 JAAS 설정에 포함하는 대신 클라이언트 콜백 핸들러를 사용해 주입할 수도 있다.
SASL/PLAIN
SASL/PLAIN의 기본 구현은 브로커의 JAAS 설정을 비밀번호 저장소로서 사용한다. 모든 클라이언트 사용자 이름과 비밀번호는 로그인 옵션으로 주어지는데, 브로커는 인증 과정에서 클라이언트가 제출한 비밀번호가 로그인 옵션으로 주어진 비밀번호에 포함되는지를 검증한다.
브로커의 JAAS 설정에 모든 비밀번호들을 저장하는 기본 구현은 안전하지 않고 사용자를 추가/제거할 때마다 모든 브로커를 재시작해야 하기 때문에 유연성도 떨어진다. 운영 환경에서 SASL/PLAIN을 사용한다면 브로커와 서드파티 비밀번호 서버를 통합하기 위한 커스텀 서버 콜백 핸들러를 사용할 수 있다.
SASL/PLAIN은 비밀번호를 암호화되지 않은 형태로 전달하기 때문에, 안전한 전송 계층을 보장하기 위해서는 PLAIN 메커니즘을 활성화할 때 SASL_SSL 암호화 역시 켜줘야 한다. 브로커와 클라이언트의 JAAS 설정에 암호화되지 않은 형태로 저장된 비밀번호는 안전하지 않아 비밀번호를 암호화하거나, 외부의 안전한 저장소에 저장하는 것을 고려해도 좋다.
재인증
새로운 자격 증명은 기본적으로 새로운 연결에만 적용되도록 되어 있다. 예전 자격 증명을 사용해 인증에 성공한 기존 연결들은 요청 타임아웃이나 유휴 타임아웃이나 네트워크 에러 등 이유로 연결이 끊어질 때까지 멀쩡하게 작동한다. 오랫동안 유지되는 연결의 경우, 인증에 사용된 자격 증명이 만료된 지 한참 후에도 계속해서 작동할 수 있다.
암호화
암호화는 데이터의 기밀성과 무결성을 보장하기 위해 사용된다. 앞서 살펴본 보안 프로토콜들을 통해 안전한 채널을 사용하면 전송하는 데이터를 보호할 수 있다. 하지만, 카프카 로그를 저장하는 디스크 내 민감한 데이터는 열어볼 수 없도록 완전히 보호하려면 추가적인 조치가 필요하다.
종단 암호화
프로듀서, 컨슈머의 시리얼라이저와 디시리얼라이저는 암호화 라이브러리에 통합될 수 있다. 암호화 라이브러리와 통합되면, 직렬화 도중 메시지 암호화를 할 수 있고, 역직렬화 도중 메시지 복호화 설정도 가능하다.
인가
인가는 사용자가 자원에 대해 어떠한 작동을 수행할 수 있는지를 결정하는 절차이다. 카프카는 인가 설정을 할 수 있는 AclAuthorizer 권한 부여자를 제공하는데, AclAuthorizer는 ACL을 주키퍼에 저장하기 때문에 주키퍼에 대한 접근 역시 제한되어야 한다.
카프카 운영하기
토픽 작업
kafka-topics.sh를 사용하면 대부분의 토픽 작업을 할 수 있다. 예를 들면 토픽 생성, 삭제, 목록 조회 등을 할 수 있다.
- <connection-string>:<port>은 localhost:9092와 같은 카프카 클러스터에 속한 서버를 지정하면 된다
// 토픽 생성
$ bin/kafka-topics.sh --bootstrap-server <connection-string>:<port> --create --topic <string> --replication-factor <integer> --partitions <integer>
- 토픽 생성 시 같은 이름의 토픽이 이미 있어서 에러 리턴하지 않도록 --if-not-exists 인수를 사용할 수 있음
// 토픽 목록 조회
$ bin/kafka-topics.sh --bootstrap-server <connection-string>:<port> --list
// 토픽 삭제
$ bin/kafka-topics.sh --bootstrap-server <connection-string>:<port> --delete --topic <topic-name>
- 토픽 삭제는 비동기적인 작업이므로, 명령어를 수행하자마자 바로 토픽이 삭제되는 것은 아니다.
// 파티션 개수 늘리기
$ bin/kafka-topics.sh --bootstrap-server <connection-string>:<port> --alter --topic <topic-name> --partitions <늘린 후 최종 파티션 개수>
- 키를 가진 메시지를 갖는 토픽이라면, 파티션 추가 시 많은 사항을 고려해야 한다. 파티션 수가 바뀌면 동일 키가 기존과 다른 파티션에 들어올 수 있기 때문이다.
토픽명 유의사항
토픽 이름에는 영문 혹은 숫자, '_', '-', '.'을 사용할 수 있다. 하지만, 토픽 이름에 '.'을 사용하는 것은 권장하지 않는다.
카프카 내부적으로 사용하는 지표에서 '.'를 '_'로 변환해서 처리하기 때문에 지표 내 토픽 이름이 충돌이 생길 수 있기 때문이다.
컨슈머 그룹
kafka-consumer-group.sh를 사용하면 클러스터에서 토픽을 읽고 있는 컨슈머 그룹을 관리할 수 있다. 이 툴은 컨슈머 그룹 목록 조회, 상세 내용, 컨슈머 그룹 삭제, 오프셋 조정 등을 할 수 있다.
// 컨슈머 그룹 목록 조회
$ bin/kafka-consumer-groups.sh --bootstrap-server <connection-string>:<port> --list
// 컨슈머 그룹 상세 내역 조회
$ bin/kafka-consumer-groups.sh --bootstrap-server <connection-string>:<port> --describe --group <consumer-group-name>
- GROUP: 컨슈머 그룹명
- TOPIC: 읽고 있는 토픽명
- PARTITION: 읽고 있는 파티션 ID
- CURRENT-OFFSET: 컨슈머 그룹이 파티션에서 다음번에 읽어올 메시지 오프셋
- LOG-END-OFFSET: 브로커 토픽 파티션의 하이 워터마크 오프셋 현재값. 이 파티션에 쓰여질 다음번 메시지 오프셋
- LAG: 컨슈머의 CURRENT-OFFSET과 브로커의 LOG-END-OFFSET 간 차이
- CONSUMER-ID: 설정된 client-id 값을 기준으로 생성된 고유한 consumer-id
- HOST: 컨슈머 그룹이 읽고 있는 호스트 IP 주소
- CLIENT-ID: 컨슈머 그룹에 속한 클라이언트를 식별하기 위해 클라이언트에 설정된 문자열
// 컨슈머 그룹 삭제
$ bin/kafka-consumer-groups.sh --bootstrap-server <connection-string>:<port> --delete --group <consumer-group-name>
- 컨슈머 그룹 내 모든 컨슈머가 내려간 상태로 active 상태인 컨슈머가 없어야 삭제 가능
- --topic <topic-name> 매개변수를 추가하면, 컨슈머 그룹 전체를 삭제하는 대신 컨슈머 그룹이 읽어오는 특정 토픽에 대한 오프셋만 삭제도 가능
// 오프셋 리셋
$ bin/kafka-consumer-groups.sh --bootstrap-server <connection-string>:<port> --group <consumer-group-name> --topic <topic-name> --reset-offsets <offset 초기화 위치>
- --dry-run 옵션을 사용하면, 오프셋을 리셋하지 않고 해당 컨슈머 그룹의 토픽에 대한 오프셋 정보를 조회할 수 있음
- 오프셋 리셋 역시 컨슈머 그룹 삭제와 마찬가지로 현재 active 상태인 컨슈머가 없어야 가능
동적 설정 변경
토픽, 클라이언트, 브로커 등 많은 설정이 클러스터를 끄거나 재설치할 필요 없이 돌아가는 와중에 동적으로 바꿀 수 있는 설정이 많다. 이는 kafka-configs.sh를 활용해 변경할 수 있다.
// 토픽 설정 기본값 재정의
$ bin/kafka-configs.sh --bootstrap-server <connection-string>:<port> --alter --entity-type topics --entity-name <topic-name> --add-config {key}={value}[,{key}={value}...]
// 클라이언트와 사용자 설정 기본값 재정의
$ bin/kafka-configs.sh --bootstrap-server <connection-string>:<port> --alter --entity-type clients --entity-name {client-id} --entity-type users --entity-name {user-id}--add-config {config-content}
- 서로 호환되는 사용자 설정과 클라이언트 설정에 대한 변경은 위 예시처럼 함께 지정 가능
// 재정의된 설정 상세 조회
$ bin/kafka-configs.sh --bootstrap-server <connection-string>:<port> --describe --entity-type <토픽|브로커|사용자|클라이언트> --entity-name <entity-name>
kafka-configs.sh는 재정의된 설정값만 보여주고, 클러스터 기본값을 따르는 설정은 보여주지 않는다. 따라서, 이 툴을 사용해 자동으로 토픽이나 클라이언트 설정을 확인하고자 한다면, 클러스터 기본값에 대해 미리 알고 있어야 한다.
쓰기 작업과 읽기 작업
콘솔 프로듀서와 콘솔 컨슈머를 이용해서 수동으로 메시지를 쓰거나 메시지를 읽어올 수 있다.
콘솔 프로듀서
kafka-console-producer.sh를 사용해 카프카 토픽에 메시지를 써넣을 수 있다. 기본적으로 메시지는 줄 단위로, 키와 밸류값은 탭 문자를 기준으로 구분된다. 메시지 전송을 종료하고자 한다면 end-of-file 문자를 입력하거나 Ctrl + D를 눌러 터미널 종료하는 방법으로 끝낼 수 있다.
// 메시지 쓰기
$ bin/kafka-console-producer.sh --bootstrap-server <connection-string>:<port> --topic <topic-name>
- 위와 같이 명령어 친 후 엔터를 치면 >가 나온다. 이때, 메시지를 적어서 엔터를 누르면 메시지가 전송된다.
> 메시지내용1
> 메시지내용2
...
- key와 value를 함께 전송하고자 한다면 아래와 같이 --property를 지정하여 보낼 수 있다 (아래와 같이 설정 후 메시지를 보내면 key:value 형태로 메시지가 분리되어 전송됨)
$ bin/kafka-console-producer.sh --bootstrap-server <connection-string>:<port> --topic <topic-name> --property "parse.key=true" --property "key.separator=:"
> key1:value1
> key2:value2
...
콘솔 컨슈머
kafka-console-consumer.sh 툴을 사용하면 카프카 클러스터 안의 1개 이상의 토픽에 대해 메시지를 읽어올 수 있다. 메시지는 표준 출력에 한 줄씩 출력된다. 기본적으로 키나 형식 같은 것 없이 메시지 안에 저장된 raw byte 뭉치가 출력된다.
// whitelist에 지정된 정규식에 매핑된 토픽들 데이터 모두 조회
$ bin/kafka-console-consumer.sh --bootstrap-server <connection-string>:<port> --whitelist <정규식> --from-beginning
- 정규식에 'earth.*'처럼 작성할 경우, earth로 시작하는 토픽들의 데이터를 가장 앞에서부터 읽음
- --from-beginning을 설정하지 않으면 가장 최근 오프셋부터 읽어옴
// 특정 토픽명에 대해 데이터 조회
$ bin/kafka-console-consumer.sh --bootstrap-server <connection-string>:<port> --topic <topic-name>
파티션 관리
카프카는 파티션 관리에 사용할 수 있는 스크립트도 제공한다.
토픽 내 특정 메시지가 오염되어 컨슈머가 처리할 수 없는 경우, 특정 메시지의 내용물을 열어봐야 한다. 이때 kafka-dump-log.sh 툴을 활용하면 토픽을 컨슈머로 읽어올 필요 없이 각각의 메시지를 바로 열어볼 수 있다.
// 카프카 브로커에 저장된 특정 로그 세그먼트 조회
$ bin/kafka-dump-log.sh --files {log-file-name}
안전하지 않은 작업
아래 작업들은 카프카가 제공은 하지만, 극단적인 상황이 아니라면 시도되지 않아야 하는 운영 작업이다.
- 클러스터 컨트롤러 이전하기
- 삭제될 토픽 제거하기
- 기본적으로 토픽 삭제 명령을 하면 언젠가 삭제가 될 수 있도록 설정하는 것인데, 삭제 요청이 멈출 경우 직접 제거를 할 수도 있다.
- 이때는 주키퍼에 저장된 데이터도 제거하는 등의 작업이 필요하기 때문에 반드시 해야 하는 게 아니라면 지양한다.
- 수동으로 토픽 삭제하기
- 토픽 삭제 기능이 꺼져 있는 클러스터를 운영하거나, 일반적이지 않은 상황에서 토픽 삭제해야 할 경우, 수동으로 클러스터에서 삭제할 수 있다.
- 단, 브로커가 하나도 작동중인게 없어야 하기 때문에 클러스터 내 브로커를 모두 내려야 한다.
'SYSTEM & INFRA > KAFKA' 카테고리의 다른 글
| [카프카 핵심 가이드] 카프카 모니터링하기, 스트림 처리 (0) | 2025.09.07 |
|---|---|
| [카프카 핵심 가이드] 데이터 파이프라인 구축하기, 클러스터간 데이터 미러링하기 (3) | 2025.08.23 |
| [카프카 핵심 가이드] 신뢰성 있는 데이터 전달, '정확히 한 번' 의미 구조 (5) | 2025.08.15 |
| [카프카 핵심 가이드] 프로그램 내에서 코드로 카프카 관리하기, 카프카 내부 메커니즘 (3) | 2025.08.09 |
| [카프카 핵심 가이드] 카프카 컨슈머: 카프카에서 데이터 읽기 (3) | 2025.08.02 |