Docker Compose中Kafka Broker的SSL握手失败问题排查与修复
终极目标
研究Kafka与OPA插件的集成方式,以及生产环境中OPA插件的使用便捷性。
操作步骤
遵循OPA官方教程配置,目前卡在Docker Compose环境下Broker无法正常工作,无法识别localhost。
我的docker-compose.yaml配置:
services: nginx: image: nginx:1.21.4 volumes: - "./bundles:/usr/share/nginx/html" ports: - "80:80" opa: image: openpolicyagent/opa:0.51.0 ports: - "8181:8181" command: - "run" - "--server" - "--set=decision_logs.console=true" - "--set=services.authz.url=http://nginx" - "--set=bundles.authz.service=authz" - "--set=bundles.authz.resource=bundle.tar.gz" depends_on: - nginx zookeeper: image: confluentinc/cp-zookeeper:6.2.1 ports: - "2181:2181" environment: - ALLOW_ANONYMOUS_LOGIN=yes - ZOOKEEPER_CLIENT_PORT=2181 broker: image: confluentinc/cp-kafka:6.2.1 ports: - "9093:9093" environment: # Set cache expiry to low value for development in order to see decisions KAFKA_OPA_AUTHORIZER_CACHE_EXPIRE_AFTER_SECONDS: 10 KAFKA_OPA_AUTHORIZER_URL: http://opa:8181/v1/data/kafka/authz/allow KAFKA_AUTHORIZER_CLASS_NAME: org.openpolicyagent.kafka.OpaAuthorizer KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" KAFKA_ADVERTISED_LISTENERS: SSL://localhost:9093 KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" KAFKA_SSL_KEYSTORE_FILENAME: server.keystore KAFKA_SSL_KEYSTORE_CREDENTIALS: credentials.txt KAFKA_SSL_KEY_CREDENTIALS: credentials.txt KAFKA_SSL_TRUSTSTORE_FILENAME: server.truststore KAFKA_SSL_TRUSTSTORE_CREDENTIALS: credentials.txt KAFKA_SSL_CLIENT_AUTH: required # KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "" # KAFKA_PRODUCER_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "" # KAFKA_CONSUMER_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "" CLASSPATH: "/plugin/*" volumes: - "./plugin:/plugin" - "./cert/server:/etc/kafka/secrets" depends_on: - opa - zookeeper
文件夹结构与教程一致,已生成证书等文件。
错误信息
broker_1 | [2023-04-27 08:40:46,391] INFO [Controller id=1, targetBrokerId=1] Failed authentication with localhost/127.0.0.1 (SSL handshake failed) (org.apache.kafka.common.network.Selector) broker_1 | [2023-04-27 08:40:46,392] ERROR [Controller id=1, targetBrokerId=1] Connection to node 1 (localhost/127.0.0.1:9093) failed authentication due to: SSL handshake failed (org.apache.kafka.clients.NetworkClient) broker_1 | [2023-04-27 08:40:46,393] WARN [RequestSendThread controllerId=1] Controller 1's connection to broker localhost:9093 (id: 1 rack: null) was unsuccessful (kafka.controller.RequestSendThread) broker_1 | org.apache.kafka.common.errors.SslAuthenticationException: SSL handshake failed broker_1 | Caused by: javax.net.ssl.SSLHandshakeException: No name matching localhost found broker_1 | at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) broker_1 | at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:349) broker_1 | at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:292) broker_1 | at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:287) broker_1 | at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1357) broker_1 | at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1232) broker_1 | at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1175) broker_1 | at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) broker_1 | at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) broker_1 | at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074) broker_1 | at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061) broker_1 | at java.base/java.security.AccessController.doPrivileged(Native Method) broker_1 | at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008) broker_1 | at org.apache.kafka.common.network.SslTransportLayer.runDelegatedTasks(SslTransportLayer.java:430) broker_1 | at org.apache.kafka.common.network.SslTransportLayer.handshakeUnwrap(SslTransportLayer.java:514) broker_1 | at org.apache.kafka.common.network.SslTransportLayer.doHandshake(SslTransportLayer.java:368) broker_1 | at org.apache.kafka.common.network.SslTransportLayer.handshake(SslTransportLayer.java:291) broker_1 | at org.apache.kafka.common.network.KafkaChannel.prepare(KafkaChannel.java:178) broker_1 | at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:543) broker_1 | at org.apache.kafka.common.network.Selector.poll(Selector.java:481) broker_1 | at org.apache.kafka.clients.NetworkClient.poll(NetworkClient.java:561) broker_1 | at org.apache.kafka.clients.NetworkClientUtils.awaitReady(NetworkClientUtils.java:74) broker_1 | at kafka.controller.RequestSendThread.brokerReady(ControllerChannelManager.scala:292) broker_1 | at kafka.controller.RequestSendThread.doWork(ControllerChannelManager.scala:246) broker_1 | at kafka.utils.ShutdownableThread.run(ShutdownableThread.scala:96) broker_1 | Caused by: java.security.cert.CertificateException: No name matching localhost found broker_1 | at java.base/sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:234) broker_1 | at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:103) broker_1 | at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455) broker_1 | at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:429) broker_1 | at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283) broker_1 | at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:141) broker_1 | at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1335) broker_1 | ... 20 more
调试步骤
- 尝试禁用SSL端点识别配置:
结果:消息无法确认,Broker无法访问。KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "" KAFKA_PRODUCER_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "" KAFKA_CONSUMER_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "" - 尝试将
localhost替换为internal.docker.host和0.0.0.0:- 使用
internal.docker.host时出现相同错误 - 使用
0.0.0.0无法路由
- 使用
已查找其他解决方案,可用信息有限,多集中在上述两种方法;版本变更也无济于事,Kafka新版本的Docker Compose配置需调整(如Zookeeper已被弃用)。
机器信息
- Docker引擎:colima(Docker官方引擎替代方案)
- 操作系统:MacOS
问题
如何修复当前问题?是否有其他更常见、易修复的方案?
解决方案
1. 修复Kafka证书的主机名匹配问题
错误核心是SSL证书中未包含localhost和容器内部服务名broker的标识。重新生成证书时,需在证书的**Subject Alternative Name (SAN)**中添加这两个主机名:
- 生成证书请求时,添加
-ext SAN=DNS:localhost,DNS:broker参数(具体命令根据证书生成工具调整) - 确保证书的Common Name (CN)至少包含其中一个主机名,优先用SAN覆盖多场景
2. 调整Kafka监听配置,区分内部/外部通信
当前KAFKA_ADVERTISED_LISTENERS仅配置了localhost,导致Broker内部控制器通信也尝试用localhost访问,而容器内部localhost指向自身,引发证书匹配冲突。修改为双监听配置:
KAFKA_LISTENERS: SSL://0.0.0.0:9093,SSL://0.0.0.0:9092 KAFKA_ADVERTISED_LISTENERS: SSL://localhost:9093,SSL://broker:9092 KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL
9093端口用于外部(Mac主机)访问,对应localhost9092端口用于容器内部(控制器、OPA等组件)访问,对应服务名broker- 内部通信将使用
broker主机名,与证书中的SAN匹配,避免localhost的证书验证问题
3. 正确配置SSL端点识别(可选)
若暂时无法重新生成证书,可启用禁用SSL端点识别的配置,但需保证所有相关组件配置一致:
KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: ""
注意:仅建议在开发环境使用此配置,生产环境必须保证证书与主机名严格匹配。
4. 验证证书内容
生成新证书后,可使用以下命令检查证书中的SAN和CN:
keytool -printcert -v -file server.crt
确认输出中包含DNS:localhost和DNS:broker。
内容的提问来源于stack exchange,提问作者Dmytro Chasovskyi
相关产品推荐
相关产品推荐

