SASL(Simple Authentication and Security Layer)은 프로토콜과 인증 방식을 분리해 끼워 넣는 틀이다. Kafka 는 그 틀 위에서 GSSAPI(Kerberos) · PLAIN · SCRAM-SHA-256 · SCRAM-SHA-512 · OAUTHBEARER 를 지원한다.
전송 보안은 별개의 축이다. 이 둘을 조합한 것이 리스너의 보안 프로토콜이다.
| 보안 프로토콜 | 인증 | 암호화 |
|---|---|---|
PLAINTEXT |
없음 | 없음 |
SSL |
인증서(양방향이면) | 있음 |
SASL_PLAINTEXT |
SASL | 없음 |
SASL_SSL |
SASL | 있음 |
SASL_PLAINTEXT 는 인증은 하되 통신 내용이 평문으로 흐른다는 뜻이다. SASL 의 반대가 SSL 인 것이 아니라, 서로 다른 계층이다.
클라이언트 설정 파일을 만들고 CLI 에 넘긴다. sasl.jaas.config 를 쓰면 별도 JAAS 파일이 필요 없다.
security.protocol=SASL_PLAINTEXT
sasl.mechanism=GSSAPI
sasl.kerberos.service.name=kafka
sasl.jaas.config=com.sun.security.auth.module.Krb5LoginModule required \
useKeyTab=true \
storeKey=true \
keyTab="/opt/kafka/config/svcuser.keytab" \
principal="svcuser@EXAMPLE.COM";
BS=broker01.example.com:9093
kafka-console-producer.sh --bootstrap-server $BS --topic order-events \
--producer.config /opt/kafka/config/client.properties
kafka-console-consumer.sh --bootstrap-server $BS --topic order-events --from-beginning \
--consumer.config /opt/kafka/config/client.properties
sasl.kerberos.service.name 은 브로커 principal 의 서비스 부분이다. 브로커가 kafka/broker01.example.com@EXAMPLE.COM 이면 kafka 다.
예전 방식으로 JAAS 파일을 쓰려면 JVM 인자로 지정한다. 한 JVM 에 하나만 적용되므로, 여러 주체를 쓰는 애플리케이션에서는 sasl.jaas.config 방식이 낫다.
export KAFKA_OPTS="-Djava.security.auth.login.config=/opt/kafka/config/kafka_client_jaas.conf"
KafkaClient {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/opt/kafka/config/svcuser.keytab"
principal="svcuser@EXAMPLE.COM";
};
티켓 캐시를 쓰려면 useTicketCache=true · useKeyTab=false 로 두고 미리 kinit 한다. 장기 구동 프로세스는 티켓이 만료되므로 keytab 방식을 쓴다.
확인 명령은 다음과 같다.
klist -kt /opt/kafka/config/svcuser.keytab
kinit -kt /opt/kafka/config/svcuser.keytab svcuser@EXAMPLE.COM
klist
Failed authentication with ... (Unexpected Kafka request of type METADATA during SASL handshake)
브로커가 SASL 리스너인데 클라이언트가 인증 절차 없이 평문 요청(METADATA)을 먼저 보냈다는 뜻이다. 즉 클라이언트 쪽 security.protocol 이 PLAINTEXT 로 남아 있다. 서버가 아니라 클라이언트 설정을 고쳐야 한다.
여기서 중요한 점은 librdkafka 는 JAAS 파일을 읽지 않는다 는 것이다. sasl.jaas.config 도 없다. JAAS 는 JVM 의 개념이며, librdkafka 는 자체 설정 키로 같은 정보를 받는다.
security.protocol=SASL_PLAINTEXT
sasl.mechanisms=GSSAPI
sasl.kerberos.service.name=kafka
sasl.kerberos.principal=svcuser@EXAMPLE.COM
sasl.kerberos.keytab=/opt/app/conf/svcuser.keytab
sasl.kerberos.min.time.before.relogin=60000
설정 키 이름이 Java 클라이언트와 조금 다르다. 특히 복수형 sasl.mechanisms 에 주의한다. 값이 잘못되면 알 수 없는 설정이라는 오류를 내므로 오탈자는 곧바로 드러난다.
librdkafka 는 내부적으로 kinit 을 실행해 티켓을 갱신한다. 그래서 다음 조건이 필요하다.
rd_kafka_conf_set 이 sasl.mechanisms=GSSAPI 를 거부하면 이 모듈이 빠진 빌드다.krb5-workstation 등 kinit 명령이 설치돼 있어야 한다. 갱신 명령은 sasl.kerberos.kinit.cmd 로 바꿀 수 있다./etc/krb5.conf 가 올바르고 KDC 로 통신이 되어야 한다.rpm -q cyrus-sasl-gssapi krb5-workstation
kinit -kt /opt/app/conf/svcuser.keytab svcuser@EXAMPLE.COM && klist
진단할 때는 디버그 로그를 켜서 어느 단계에서 끊기는지 본다.
debug=security,broker,protocol
kafkacat(kcat)로 같은 설정을 넣어 보면 애플리케이션 문제인지 환경 문제인지 빠르게 가른다.
kcat -b broker01.example.com:9093 -L \
-X security.protocol=SASL_PLAINTEXT -X sasl.mechanisms=GSSAPI \
-X sasl.kerberos.service.name=kafka \
-X sasl.kerberos.keytab=/opt/app/conf/svcuser.keytab \
-X sasl.kerberos.principal=svcuser@EXAMPLE.COM
브로커의 advertised.listeners 가 클라이언트에서 접근 가능한 이름이어야 한다. 부트스트랩은 성공했는데 이후 메타데이터의 주소로 접속하지 못해 실패하는 경우가 많다.
정방향·역방향 DNS 가 맞아야 한다. Kerberos 는 이름 기반 인증이라 역방향 조회가 어긋나면 principal 을 못 찾는다.
시각이 어긋나면 인증이 실패한다. 기본 허용 오차는 5 분이므로 NTP 를 맞춘다.
ACL 이 켜진 클러스터라면 인증에 성공해도 권한이 없어 실패할 수 있다. 오류 메시지가 인증 실패가 아니라 권한 거부로 나온다.