학습 목표

이 도메인이 묻는 것

문항은 대개 설정 조각을 주고 결과를 예측하게 하거나, 실패 증상을 주고 원인을 고르게 합니다. "무엇이 안전한가"를 묻는 개념 문항보다 "이 구성에서 클라이언트가 붙는가"를 묻는 판독 문항이 많습니다.

이 섹션의 전형적인 질문 형태
질문 형태실제로 확인하는 것
"이 server.properties에서 외부 클라이언트가 붙을 포트는?" 리스너 3종 조합 판독
"자격증명이 네트워크에 평문으로 흐르는 구성은?" SASL_PLAINTEXTSASL_SSL의 차이
"ACL이 하나도 없는 토픽에 접근하면?" 기본 거부 동작과 super.users, allow.everyone.if.no.acl.found
"컨슈머가 GROUP_AUTHORIZATION_FAILED를 받는다" 토픽 ACL만 주고 그룹 ACL을 빼먹은 실수
"각 SASL 메커니즘을 자격증명 저장 위치와 연결하시오" (matching) GSSAPI/PLAIN/SCRAM/OAUTHBEARER 구분

핵심 개념 요약 — 운영 관점

Kafka 보안은 네 겹입니다. 암호화(전송 구간 보호), 인증(누구인지 확인), 인가(무엇을 허용할지), 그리고 감사(누가 무엇을 했는지)입니다. 운영 사고는 거의 전부 앞의 세 겹 사이의 불일치에서 생깁니다 — 예를 들어 인증은 켰는데 인가를 안 켠 상태, 혹은 그 반대입니다.

보안 4계층 — 암호화 · 인증 · 인가 · 감사 보안을 네 계층으로 나눠 아래에서 위로 쌓는 그림입니다. 첫째 암호화는 security.protocol 을 SSL 또는 SASL_SSL 로 두고 keystore 와 truststore 를 설정해 전송 구간을 보호합니다. 리스너마다 다르게 걸 수 있으며 디스크에 저장된 데이터의 암호화는 Kafka 기능이 아니라 볼륨 암호화로 처리합니다. 둘째 인증은 SASL 의 PLAIN, SCRAM, GSSAPI, OAUTHBEARER 또는 mTLS 로 상대를 확인하고 그 결과로 principal 이 만들어집니다. 셋째 인가는 authorizer.class.name 에 StandardAuthorizer 를 지정하고 ACL 로 권한을 정하며, ACL 이 없는 리소스는 기본적으로 거부되고 super.users 만 접근할 수 있습니다. 넷째 감사는 authorizer 로그와 kafka-acls.sh --list 로 현황을 남기지만 Kafka 자체에 감사 저장소는 없습니다. 암호화 없이 SASL PLAIN 을 쓰면 비밀번호가 평문으로 흐르고 인증 없이 ACL 을 걸면 주체를 구분할 수 없다는 점이 계층 순서의 이유입니다. 보안 4계층 — 아래 계층이 없으면 위 계층은 의미가 없습니다 1. 암호화 — 전송 구간을 감춥니다 security.protocol security.protocol=SSL 또는 SASL_SSL ssl.keystore.location ssl.truststore.location 리스너마다 다르게 걸 수 있습니다 (listener.security.protocol.map · D-101). 디스크에 저장된 데이터의 암호화는 Kafka 기능이 아닙니다 — 볼륨·디스크 암호화로 처리합니다. 2. 인증 — 상대가 누구인지 확인합니다 SASL 또는 mTLS SASL: PLAIN · SCRAM-SHA-256/512 · GSSAPI · OAUTHBEARER | mTLS: ssl.client.auth 인증이 끝나면 principal(예: User:app-billing)이 만들어지고 이것이 인가의 주체가 됩니다. 인증 없이 인가만 켜면 모두가 같은 익명 주체가 되어 ACL 이 의미를 잃습니다. 3. 인가 — 무엇을 할 수 있는지 정합니다 ACL authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer ACL 이 하나도 없는 리소스는 기본적으로 거부되고 super.users 만 접근할 수 있습니다. allow.everyone.if.no.acl.found=true 로 바꾸면 ACL 없는 리소스가 전부 공개됩니다 (권장하지 않음). 4. 감사 — 무엇을 했는지 남깁니다 로그 · 메트릭 authorizer 로그로 거부·허용 기록을 남기고, 실패 코드는 클라이언트 예외로도 드러납니다. 현황 확인: kafka-acls.sh --bootstrap-server :9092 --list Kafka 자체에는 별도 감사 로그 저장소가 없습니다 — 로그 수집 파이프라인으로 보냅니다. 순서가 중요합니다 — 암호화 없이 SASL/PLAIN 을 쓰면 비밀번호가 평문으로 흐릅니다. 인증 없이 인가만 켜면 구분할 주체가 없어 ACL 이 의미를 잃습니다.
보안 4계층 — 암호화 · 인증 · 인가 · 감사가 각각 어느 설정으로 켜지는지

보안 프로토콜 4종 — SASL_PLAINTEXT를 주의하세요

리스너마다 보안 프로토콜 하나가 붙습니다. 선택지는 네 개뿐이고 (대소문자 구분 없음), 이름이 두 축의 조합이라는 점을 알면 외울 것이 없습니다: 앞부분은 인증 방식, 뒷부분은 전송 암호화입니다.

보안 프로토콜 4종 비교
프로토콜 전송 암호화 클라이언트 인증 자격증명이 평문으로 흐르는가 용도
PLAINTEXT 아니오 없음 해당 없음 격리된 개발 환경. 추가 설정이 전혀 필요 없습니다.
SSL 예 (TLS) mTLS (선택) 해당 없음 인증서 기반. ssl.client.auth=required로 켜야 클라이언트 인증이 됩니다.
SASL_PLAINTEXT 아니오 SASL 예 — 위험 신뢰된 사설망 내부 한정. 프로덕션 클라이언트 노출 금지.
SASL_SSL 예 (TLS) SASL 아니오 프로덕션 표준. SASL을 쓴다면 사실상 이것을 씁니다.

SASL 메커니즘 5종

보안 프로토콜이 SASL_*이면 그 위에서 메커니즘을 고릅니다. Kafka가 지원하는 메커니즘은 다음 5개입니다. 브로커는 sasl.enabled.mechanisms로 허용 목록을 정하고(기본값은 GSSAPI 하나), 클라이언트는 sasl.mechanism으로 하나를 고릅니다.

SASL 메커니즘 4종 비교 — PLAIN · SCRAM · GSSAPI · OAUTHBEARER SASL/PLAIN 은 브로커의 정적 JAAS 설정에 자격 증명을 두므로 사용자 변경 시 재시작이 필요하고 반드시 SASL_SSL 과 함께 써야 합니다. 메커니즘 이름 PLAIN 과 보안 프로토콜 PLAINTEXT 는 다른 개념입니다. SASL/SCRAM 은 SHA-256 과 SHA-512 를 지원하며 자격 증명을 클러스터 메타데이터 로그에 salt 와 iterations, StoredKey, ServerKey 형태로 저장하므로 kafka-configs.sh 로 브로커 재시작 없이 사용자를 추가하고 변경할 수 있습니다. SASL/GSSAPI 는 Kerberos 기반으로 KDC 와 keytab 을 쓰며 기존 엔터프라이즈 SSO 환경에 적합하지만 설정이 복잡하고 시간 동기에 민감합니다. SASL/OAUTHBEARER 는 외부 인증 서버가 발급한 토큰을 검증하는 방식이며 Kafka 4.0 부터 시스템 속성 org.apache.kafka.sasl.oauthbearer.allowed.urls 를 명시해야 하고 기본값이 빈 목록이라 업그레이드 후 엔드포인트가 막히는 사고가 자주 발생합니다. 어느 메커니즘이든 인증 결과는 하나의 principal 로 수렴하고 그 principal 로 ACL 을 검사합니다. SASL 메커니즘 4종 비교 — 자격 증명을 어디에 두는가 SASL/PLAIN 가장 단순 · 평문 전송 자격 증명: 브로커의 JAAS 설정(정적 파일). 사용자를 추가·변경하면 브로커 재시작이 필요합니다. 반드시 SASL_SSL 과 함께 씁니다 — SASL_PLAINTEXT 로 쓰면 비밀번호가 평문으로 흐릅니다. 이름이 PLAINTEXT 프로토콜과 비슷해 혼동하기 쉽습니다. PLAIN 은 메커니즘, PLAINTEXT 는 프로토콜입니다. SASL/SCRAM-SHA-256 · SCRAM-SHA-512 동적 사용자 관리 자격 증명: 클러스터의 메타데이터 로그에 저장됩니다 (salt · iterations · StoredKey · ServerKey). kafka-configs.sh --alter --entity-type users --entity-name alice --add-config 'SCRAM-SHA-256=[password=...]' 브로커 재시작 없이 사용자를 추가·변경할 수 있고, 비밀번호 원문이 저장되지 않습니다. SASL/GSSAPI (Kerberos) 엔터프라이즈 SSO 자격 증명: KDC 가 관리하고 서비스는 keytab 을 씁니다. 티켓 갱신 주기 관리가 필요합니다. 기존 Active Directory·Kerberos 인프라가 있는 조직에 적합합니다. 설정 항목이 많고 시간 동기(clock skew)에 민감합니다. SASL/OAUTHBEARER 외부 IdP 토큰 자격 증명: 외부 인증 서버가 발급한 토큰(JWT). Kafka 는 검증만 합니다. 4.0 부터 시스템 속성으로 허용 URL 을 명시해야 합니다: org.apache.kafka.sasl.oauthbearer.allowed.urls 기본값이 빈 목록이므로 4.0 으로 올린 뒤 토큰·jwks 엔드포인트가 막히는 사고가 자주 납니다. 어느 메커니즘이든 인증 결과는 principal 하나로 수렴하고, 그 principal 로 ACL 을 검사합니다. 여러 메커니즘을 동시에 허용할 수 있습니다: sasl.enabled.mechanisms=SCRAM-SHA-512,OAUTHBEARER
SASL 메커니즘 비교 — PLAIN · SCRAM · GSSAPI · OAUTHBEARER의 자격증명 흐름
SASL 메커니즘과 자격증명 저장 위치
메커니즘 자격증명 저장 위치 운영 포인트
GSSAPI Kerberos 프린시펄 + keytab KDC (Active Directory 등) 모든 호스트가 FQDN으로 해석되어야 합니다. sasl.kerberos.service.name이 브로커 프린시펄과 일치해야 합니다.
PLAIN 사용자명 · 비밀번호 JAAS 설정 파일 또는 sasl.jaas.config 사용자를 추가하려면 브로커 재시작이 필요합니다(기본 구현 기준). 반드시 TLS와 함께.
SCRAM-SHA-256 salted challenge-response 메타데이터 로그 사용자를 동적으로 추가·변경할 수 있습니다. 기본 반복 횟수 4096, 최소도 4096입니다.
SCRAM-SHA-512 동일 (해시 강도만 다름) 메타데이터 로그 SHA-256과 별개 자격증명입니다. 두 메커니즘을 다 쓰려면 각각 등록해야 합니다.
OAUTHBEARER OAuth 2.0 토큰(JWT) 외부 인가 서버 토큰 검증기·재시도 설정이 별도로 있습니다. 프로덕션 사용은 비프로덕션 예제 설정과 구분됩니다.

리스너 3종 조합 판독 — 운영자 최다 실수

세 설정의 역할은 다음과 같습니다. 이 세 문장만 정확히 구분하면 대부분의 문항이 풀립니다.

리스너 3종의 역할
설정무엇을 정하는가기본값
listeners 브로커가 실제로 바인딩할 주소·포트와 리스너 이름 PLAINTEXT://:9092
advertised.listeners 클라이언트·다른 브로커에게 알려 줄 주소. 설정하지 않으면 listeners 값이 쓰입니다 null
listener.security.protocol.map 리스너 이름 → 보안 프로토콜 매핑 SASL_SSL:SASL_SSL,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT
listeners · advertised.listeners · listener.security.protocol.map 의 관계 세 설정이 각각 다른 질문에 답한다는 점을 보여 줍니다. listeners 는 어느 주소와 포트에 바인딩할지, advertised.listeners 는 클라이언트에게 어디로 오라고 알려 줄지, listener.security.protocol.map 은 리스너 이름별 보안 프로토콜이 무엇인지를 정합니다. 예시 설정에서 브로커는 INTERNAL 9092 와 EXTERNAL 9094 두 리스너를 열고 각각 SASL_SSL 로 매핑하며 브로커 간 통신은 INTERNAL 을 씁니다. 내부 클라이언트는 broker1.internal 9092 로, 외부 클라이언트는 kafka.example.com 9094 로 붙습니다. 정상 동작에서 접속은 두 번 일어납니다. 먼저 bootstrap.servers 주소로 붙어 메타데이터를 받고, 브로커가 돌려준 advertised.listeners 주소로 다시 붙어 실제 전송을 합니다. 가장 흔한 실수는 advertised.listeners 를 내부 호스트명으로만 두는 것입니다. 이때 bootstrap 은 성공하지만 외부 클라이언트가 해석할 수 없는 내부 이름을 받아 가서 두 번째 접속에서 타임아웃이 나므로 연결은 되는데 곧 끊기는 것처럼 보입니다. KRaft 에서는 controller.listener.names 를 따로 두며 inter.broker.listener.name 과 같은 값일 수 없습니다. listeners · advertised.listeners · listener.security.protocol.map 운영자가 가장 많이 틀리는 지점입니다. 세 설정은 각각 다른 질문에 답합니다. broker-1 의 server.properties — 내부·외부를 분리한 예 listeners=INTERNAL://0.0.0.0:9092,EXTERNAL://0.0.0.0:9094 advertised.listeners=INTERNAL://broker1.internal:9092, EXTERNAL://kafka.example.com:9094 listener.security.protocol.map=INTERNAL:SASL_SSL,EXTERNAL:SASL_SSL inter.broker.listener.name=INTERNAL listeners 어느 주소·포트에 바인딩할까 advertised.listeners 어디로 오라고 알려 줄까 listener.security. protocol.map 이름별 보안 프로토콜은 무엇일까 내부 클라이언트 같은 네트워크의 앱 broker1.internal:9092 broker-1 INTERNAL 9092 EXTERNAL 9094 외부 클라이언트 인터넷·다른 VPC 의 앱 kafka.example.com:9094 정상 동작 — 접속은 두 번 일어납니다 1) 클라이언트가 bootstrap.servers 주소로 붙어 메타데이터를 요청합니다. 2) 브로커는 자기가 붙은 리스너에 해당하는 advertised.listeners 값을 돌려줍니다. 3) 클라이언트는 그 주소로 다시 붙어 실제 produce·fetch 를 합니다. 가장 흔한 실수 — "연결은 되는데 곧 끊긴다" advertised.listeners 를 비우거나 내부 호스트명으로만 두면, 1) 단계는 성공하고 2) 단계에서 외부 클라이언트가 broker1.internal 을 받아 갑니다. 그 이름은 외부에서 해석되지 않으므로 3) 단계에서 타임아웃이 납니다 — bootstrap 은 됐으니 "연결은 된다"고 보이는 것입니다. 컨테이너·클라우드 환경에서 0.0.0.0 을 advertised 로 두는 것도 같은 이유로 잘못입니다. KRaft 에서는 controller.listener.names 를 따로 두며 inter.broker.listener.name 과 같은 값일 수 없습니다.
리스너 3종 관계 — 내부 클라이언트와 외부 클라이언트가 각각 어느 주소로 붙는지, advertised.listeners가 왜 필요한지

핵심 규칙은 네 개입니다.

  1. 리스너 이름이 보안 프로토콜 이름이 아니면 listener.security.protocol.map반드시 정의해야 합니다.
  2. 같은 보안 프로토콜을 두 개 이상의 포트에서 쓰려면 map으로 이름을 갈라 줘야 합니다.
  3. advertised.listeners에는 0.0.0.0을 쓸 수 없습니다. listeners에서는 가능합니다.
  4. 브로커 간 리스너는 inter.broker.listener.name으로 지정합니다. security.inter.broker.protocol동시에 설정하면 오류입니다.
server.properties — 내부/외부 리스너 분리 (판독 연습용)
process.roles=broker
node.id=1

# 실제 바인딩: 내부용 9092, 외부용 9094, 컨트롤러 통신 수신은 없음(브로커 전용 노드)
listeners=INTERNAL://0.0.0.0:9092,EXTERNAL://0.0.0.0:9094

# 알려 줄 주소: 내부는 사설 DNS, 외부는 공개 DNS
advertised.listeners=INTERNAL://kafka-1.internal:9092,EXTERNAL://kafka-1.example.com:9094

# 이름 → 프로토콜. 이름이 프로토콜명이 아니므로 반드시 필요
listener.security.protocol.map=INTERNAL:SASL_SSL,EXTERNAL:SASL_SSL,CONTROLLER:SASL_SSL

# 브로커 간 통신은 내부 리스너로
inter.broker.listener.name=INTERNAL

# 컨트롤러 리스너: 브로커 전용 노드도 이름과 보안 설정을 선언해야 합니다
controller.listener.names=CONTROLLER
controller.quorum.bootstrap.servers=controller-1.internal:9093,controller-2.internal:9093,controller-3.internal:9093

sasl.enabled.mechanisms=SCRAM-SHA-512
sasl.mechanism.inter.broker.protocol=SCRAM-SHA-512
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer

인가 — ACL 모델

KRaft 클러스터의 기본 authorizer는 org.apache.kafka.metadata.authorizer.StandardAuthorizer이고, ACL은 클러스터 메타데이터에 저장됩니다. 이 설정은 브로커·컨트롤러·결합 노드 전부에 넣어야 합니다.

ACL 한 줄은 이렇게 읽습니다 — "프린시펄 P는 호스트 H에서, 리소스 패턴 RP에 매칭되는 리소스 R에 대해 작업 O를 허용/거부한다."

ACL 모델 — principal, resource, operation 의 조합과 판정 규칙 ACL 한 줄은 principal, host, operation, resource, patternType, permission 으로 구성됩니다. 예를 들어 User:app-billing 에게 모든 호스트에서 Topic orders 에 대한 Read 를 LITERAL 패턴으로 ALLOW 하는 형태입니다. Operation 은 Read, Write, Create, Delete, Alter, Describe, ClusterAction, DescribeConfigs, AlterConfigs, IdempotentWrite, CreateTokens, DescribeTokens, All 의 13종이고 Resource type 은 Topic, Group, Cluster, TransactionalId, DelegationToken, User 의 6종입니다. 판정은 순서가 있습니다. DENY 가 하나라도 일치하면 거부하고, 그다음 ALLOW 가 일치하면 허용하며, 아무 ACL 도 일치하지 않으면 거부하되 super.users 만 예외입니다. allow.everyone.if.no.acl.found 를 true 로 두면 마지막 규칙이 허용으로 바뀌지만 권장되지 않습니다. 컨슈머는 토픽 Read 와 그룹 Read 가 모두 필요하며 한쪽만 부여하면 GROUP_AUTHORIZATION_FAILED 가 발생합니다. ACL 모델 — principal × resource × operation (+ host · 허용/거부) ACL 한 줄의 구성 Principal User:app-billing Host * 또는 IP Operation Read Resource Topic:orders PatternType LITERAL Permission ALLOW | DENY Operation (13종) · Read · Write · Create · Delete · Alter · Describe · ClusterAction · DescribeConfigs · AlterConfigs · IdempotentWrite · CreateTokens · DescribeTokens · All Resource type (6종) · Topic 토픽 읽기·쓰기 · Group 컨슈머 그룹 참여 · Cluster 클러스터 단위 작업 · TransactionalId 트랜잭션 · DelegationToken 위임 토큰 · User 토큰 생성·조회 대상 판정 규칙 순서가 정해져 있습니다 1) DENY 가 하나라도 일치하면 거부합니다 — ALLOW 보다 우선합니다. 2) ALLOW 가 일치하면 허용합니다. 3) 아무 ACL 도 일치하지 않으면 거부합니다. super.users 만 예외입니다. allow.everyone.if.no.acl.found=true 로 두면 3)이 "허용"으로 바뀝니다 (권장하지 않음) PatternType 은 LITERAL(정확히 일치) 또는 PREFIXED(접두어 일치) 중 하나입니다. 컨슈머는 토픽 Read 와 그룹 Read 가 둘 다 필요합니다 — 한쪽만 주면 실패합니다. kafka-acls.sh --bootstrap-server :9092 --add --allow-principal User:app-billing --operation Read --topic orders --group billing
ACL 모델 — principal × resource × operation의 3축 구조와 패턴 타입(literal · prefixed · wildcard)
리소스 타입과 인가 실패 시 오류 이름
리소스무엇을 보호하는가실패 오류
Topic토픽 읽기·쓰기·생성·삭제TOPIC_AUTHORIZATION_FAILED (29)
Group컨슈머 그룹 참여·오프셋 커밋GROUP_AUTHORIZATION_FAILED (30)
Cluster클러스터 전역 작업, 멱등 프로듀서의 IdempotentWriteCLUSTER_AUTHORIZATION_FAILED (31)
TransactionalId트랜잭션 프로듀서TRANSACTIONAL_ID_AUTHORIZATION_FAILED (53)
DelegationToken위임 토큰 조회·갱신토큰 관련 인가 오류
User다른 사용자의 토큰 생성·조회 권한동일

작업(operation) 목록은 다음과 같습니다: Read, Write, Create, Delete, Alter, Describe, ClusterAction, DescribeConfigs, AlterConfigs, IdempotentWrite, CreateTokens, DescribeTokens, All.

필수 CLI 명령어

SCRAM 자격증명 관리 — 기동 전과 기동 후가 다릅니다
# [기동 전] 브로커 간 통신용 초기 자격증명을 포맷 단계에서 심는다
bin/kafka-storage.sh format -t $(bin/kafka-storage.sh random-uuid) \
    -c config/server.properties \
    --add-scram 'SCRAM-SHA-256=[name="admin",password="admin-secret"]'

# [기동 후] 클라이언트 사용자 추가 (반복 횟수 지정 가능, 미지정 시 4096)
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
    --add-config 'SCRAM-SHA-256=[iterations=8192,password=alice-secret]' \
    --entity-type users --entity-name alice --command-config client.properties

# 등록된 자격증명 확인
bin/kafka-configs.sh --bootstrap-server localhost:9092 --describe \
    --entity-type users --entity-name alice --command-config client.properties

# 삭제 (메커니즘 단위)
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
    --delete-config 'SCRAM-SHA-256' \
    --entity-type users --entity-name alice --command-config client.properties
ACL 관리 — --producer / --consumer 편의 옵션을 쓰세요
# 프로듀서 권한 한 번에 (Write + Describe + 필요한 Create)
bin/kafka-acls.sh --bootstrap-server localhost:9092 --add \
    --allow-principal User:Bob --producer --topic orders

# 컨슈머 권한 — 그룹을 반드시 함께 지정해야 합니다
bin/kafka-acls.sh --bootstrap-server localhost:9092 --add \
    --allow-principal User:Alice --consumer --topic orders --group order-workers

# 프리픽스 패턴: orders- 로 시작하는 모든 토픽
bin/kafka-acls.sh --bootstrap-server localhost:9092 --add \
    --allow-principal User:Jane --producer --topic orders- --resource-pattern-type prefixed

# 특정 토픽에 영향을 주는 ACL 전부 보기 (literal + wildcard + prefixed)
bin/kafka-acls.sh --bootstrap-server localhost:9092 --list \
    --topic orders --resource-pattern-type match

# 삭제 — --add 를 --remove 로 바꾸고 나머지는 동일하게
bin/kafka-acls.sh --bootstrap-server localhost:9092 --remove \
    --allow-principal User:Bob --producer --topic orders

# 브로커에 접근할 수 없으면 컨트롤러로
bin/kafka-acls.sh --bootstrap-controller localhost:9093 --list

반드시 외워야 할 설정값

Kafka 4.3 보안 관련 기본값. 기본값이 "안전하지 않은 쪽"인 항목에 주의하세요.
설정 기본값 의미 운영 포인트
listeners PLAINTEXT://:9092 바인딩할 리스너 목록 기본값이 암호화·인증 없음입니다. 아무것도 안 하면 열린 클러스터입니다.
advertised.listeners null 클라이언트에 알릴 주소 미설정 시 listeners가 그대로 광고됩니다 — 컨테이너/클라우드에서 사고의 원인.
listener.security.protocol.map 4종 프로토콜의 자기 매핑 리스너 이름 → 프로토콜 커스텀 이름을 쓰는 순간 직접 정의해야 합니다.
security.inter.broker.protocol PLAINTEXT 브로커 간 통신 프로토콜 inter.broker.listener.name동시 설정 시 오류입니다.
inter.broker.listener.name null 브로커 간 통신에 쓸 리스너 이름 컨트롤러 리스너와 같은 값을 줄 수 없습니다.
controller.listener.names 비어 있음 컨트롤러 통신용 리스너 이름 목록 KRaft에서 필수. 브로커 전용 노드도 선언해야 합니다. 나가는 요청은 첫 번째를 씁니다.
sasl.enabled.mechanisms GSSAPI 브로커가 허용하는 SASL 메커니즘 SCRAM을 쓰려면 명시해야 합니다. 기본값은 Kerberos만입니다.
sasl.mechanism.inter.broker.protocol GSSAPI 브로커 간 통신에 쓸 메커니즘 sasl.enabled.mechanisms에 포함되어 있어야 합니다.
ssl.client.auth none mTLS 클라이언트 인증 요구 수준 기본값은 인증하지 않음. required로 바꿔야 mTLS가 인증으로 동작합니다. requested는 선택 제출입니다.
ssl.endpoint.identification.algorithm https 서버 인증서의 호스트명 검증 방식 비우면 검증이 꺼져 MITM에 노출됩니다. 인증서 SAN에 실제 advertised 호스트명이 들어 있어야 합니다.
authorizer.class.name 빈 문자열 인가 구현체 기본은 인가 없음 — 인증만 켜면 인증된 누구나 전부 할 수 있습니다.
allow.everyone.if.no.acl.found false ACL 없는 리소스의 기본 동작 기본은 거부(super user만 허용). 마이그레이션 중에만 한시적으로 켭니다.
super.users 미설정 ACL을 우회하는 프린시펄 목록 구분자는 세미콜론. User는 대소문자 구분.

장애 시나리오와 대응

시나리오 1 — 브로커는 떴는데 외부 클라이언트가 붙지 못한다

가장 흔한 보안·네트워크 사고입니다. 증상은 "부트스트랩은 성공했는데 이후 요청이 타임아웃" 또는 "내부 호스트명으로 연결을 시도한다"는 로그입니다. 원인은 거의 항상 advertised.listeners입니다.

advertised.listeners가 없으면 listeners가 그대로 광고됩니다. 클라이언트는 메타데이터로 0.0.0.0:9092 또는 컨테이너 내부 호스트명을 받아 연결에 실패합니다.

server.properties
listeners=EXTERNAL://0.0.0.0:9094
listener.security.protocol.map=EXTERNAL:SASL_SSL
# advertised.listeners 없음

외부에서 실제로 도달 가능한 주소를 명시합니다. advertised.listeners에는 0.0.0.0을 쓸 수 없습니다.

server.properties
listeners=EXTERNAL://0.0.0.0:9094
advertised.listeners=EXTERNAL://kafka-1.example.com:9094
listener.security.protocol.map=EXTERNAL:SASL_SSL
  1. 클라이언트가 받은 메타데이터를 먼저 확인합니다. 부트스트랩 성공 후 실패라면 광고 주소 문제입니다.
  2. 브로커에서 advertised.listeners가 실제 도달 가능한 주소인지 확인합니다.
  3. TLS를 쓰면 인증서 SAN에 그 광고 호스트명이 포함되어야 합니다. 없으면 호스트명 검증에서 끊깁니다.
  4. 포트가 각각 열려 있는지, 리스너 이름이 map에 정의되어 있는지 확인합니다.
  5. 수정 후에는 롤링 재시작이 필요합니다(advertised.listeners는 read-only 설정).

시나리오 2 — 클라이언트가 인증에 실패한다

인증(authentication) 실패와 인가(authorization) 실패는 다른 문제입니다. 먼저 어느 쪽인지 가른 뒤 조치해야 합니다.

인증 실패 진단 순서
단계확인전형적 원인
1 클라이언트의 security.protocol이 그 리스너의 프로토콜과 같은가 SASL_SSL 리스너에 SASL_PLAINTEXT로 접속
2 클라이언트 sasl.mechanism이 브로커 sasl.enabled.mechanisms에 있는가 브로커가 GSSAPI만 열어 둔 상태에서 SCRAM-SHA-512 시도
3 해당 메커니즘의 자격증명이 등록되어 있는가 SHA-256만 등록하고 SHA-512로 접속 (별개 자격증명)
4 sasl.jaas.config의 로그인 모듈이 메커니즘과 맞는가 PlainLoginModule로 SCRAM 시도
5 TLS 쪽 문제인가 truststore에 CA 없음, 인증서 만료, 호스트명 검증 실패
6 Kerberos라면 FQDN·keytab·시계 역방향 DNS가 안 되어 SASL 핸드셰이크가 느려지는 증상도 여기서 나옵니다

시나리오 3 — 프로듀서는 되는데 컨슈머만 실패한다

  1. 오류 이름을 확인합니다. GROUP_AUTHORIZATION_FAILED그룹 ACL 문제입니다.
  2. kafka-acls.sh --add --allow-principal ... --consumer --topic X --group Y로 토픽과 그룹을 함께 부여합니다. --consumer는 그룹 지정이 필수입니다.
  3. TOPIC_AUTHORIZATION_FAILED인데 ACL이 있다고 보인다면 --resource-pattern-type match로 실제 적용 ACL을 다시 확인합니다.
  4. 멱등 프로듀서가 CLUSTER_AUTHORIZATION_FAILED를 받으면 Cluster 리소스의 IdempotentWrite가 없는 경우입니다. enable.idempotence 기본값이 true이므로 의도하지 않아도 걸립니다.
  5. 트랜잭션 프로듀서라면 TransactionalId 리소스에 Write가 필요합니다.

시나리오 4 — 보안을 켠 뒤 브로커가 기동하지 않는다

  1. 리스너 이름이 보안 프로토콜명이 아닌데 listener.security.protocol.map에 없는지 확인합니다.
  2. inter.broker.listener.namesecurity.inter.broker.protocol둘 다 설정하지 않았는지 확인합니다. 동시 설정은 오류입니다.
  3. controller.listener.namesinter.broker.listener.name과 같은 값이 아닌지 확인합니다.
  4. 브로커 간 통신용 SCRAM 자격증명이 포맷 단계에서 심겼는지 확인합니다. 없으면 브로커끼리 인증하지 못해 복제가 시작되지 않습니다.
  5. keystore/truststore 경로와 비밀번호, 파일 권한(브로커 실행 사용자가 읽을 수 있는가)을 확인합니다.

자주 나오는 함정

관련 케이스 스터디

설정 문법과 전체 목록은 보안 설정 치트시트, 운영 배경은 11장 운영 기초에 있습니다. 같은 오류를 인가 관점이 아니라 클러스터 관점에서 보는 방법은 Troubleshooting에서 다룹니다.

미니 퀴즈

리스너 조합 판독과 오류 이름 매칭이 중심입니다.

공식 문서 출처