Apache Pulsar 传输层加密实战指南:基于 TLS 的 Broker、Proxy 与多语言客户端全链路配置
【免费下载链接】pulsarApache Pulsar - distributed pub-sub messaging system项目地址: https://gitcode.com/gh_mirrors/pulsar28/pulsar
本文以 Apache Pulsar 官方安全文档 security-tls-transport.md 为主体,结合当前仓库中的 broker.conf、proxy.conf、client.conf 以及 PulsarService.java 等源码与配置,系统讲解 Pulsar 的 TLS 传输加密原理、证书签发全流程,以及 Broker、Proxy、CLI 工具和 Java/Python/C++/Node.js/C# 客户端的完整配置方法。读完本文,你将能够独立为 Pulsar 集群启用端到端的 TLS 传输加密,并掌握协议版本、密码套件与主机名校验等安全加固手段。
TLS 概述:为什么默认传输不安全
默认情况下,Apache Pulsar 客户端与 Pulsar 服务端之间以明文进行通信,也就是说所有数据(包括消息内容与元数据)在网络上都是裸奔的。攻击者只要能够监听网络(即典型的"中间人攻击"场景),就可以窃取或篡改传输中的数据。启用 TLS 可以对这条链路进行加密,从而抵御中间人窃听。
TLS 不仅可以用作传输加密,还可以在加密的同时承担身份认证职责。本文聚焦于纯粹的传输加密配置;如需在 TLS 之上叠加基于证书的客户端认证,可参见 security-tls-authentication.md;也可以先启用 TLS 传输加密,再叠加其他认证机制(如 Athenz)。
注意:启用 TLS 会引入加解密的计算开销,可能对性能产生一定影响,需要在安全性与吞吐之间做权衡。
TLS 核心概念
TLS 基于公钥密码学(public key cryptography)实现。每一对密钥由公钥与私钥组成:公钥用于加密消息,私钥用于解密消息。
要在 Pulsar 中启用 TLS 传输加密,你至少需要两类密钥对:
- 服务端密钥对(server key pairs):由每个 Broker / Proxy 持有。
- 证书颁发机构(Certificate Authority,CA):用于签发并背书上述证书。
如果需要客户端认证,还需要第三类密钥对——客户端密钥对(client key pairs),具体见 security-tls-authentication.md。
CA 的私钥必须存放在极其安全的位置(理想情况下是完全离线、物理隔离、全盘加密的计算机);而 CA 的公钥——即信任证书(trust cert)——可以自由分发。
对于客户端与服务端的每一对密钥,管理员的流程是统一的:先生成私钥和证书签名请求(CSR),再用 CA 私钥对 CSR 签名,最终生成一张证书(certificate)。这张证书本质上是该密钥对的公钥载体。
- 在传输加密场景下,客户端使用 CA 的trust cert来验证服务端持有的密钥对确实由该 CA 签发。中间人攻击者无法接触 CA 私钥,因此无法伪造出具有合法签名密钥对的服务端。
- 在TLS 认证场景下,服务端反过来用 trust cert 验证客户端密钥对是否由 CA 签发,并将**客户端证书的 Common Name(CN)**作为该客户端的角色令牌(role token),参见 安全概览。
此外,Pulsar 的密码套件与算法由Bouncy Castle Provider提供。如果需要 FIPS 版本的 Bouncy Castle Provider,请参考 security-bouncy-castle.md。
创建 TLS 证书
为 Pulsar 创建 TLS 证书需要依次完成三件事:创建证书颁发机构(CA)、创建服务端证书、创建客户端证书。下面的步骤全部基于 OpenSSL 命令行。你也可以参考网络上更详尽的 OpenSSL CA 搭建资料。
当前仓库中提供了一份可直接使用的 OpenSSL 配置文件:site2/website/static/examples/openssl.cnf。这份配置以环境变量CA_HOME定位 CA 目录下的所有文件与子目录(certs、crl、newcerts、private、index.txt、serial等),并预置了v3_ca、server_cert、usr_cert等扩展模板。另外,仓库的 tests/certificate-authority/openssl.cnf 还保存了一份用于集成测试的同类配置,可作为对照。
创建证书颁发机构(CA)
为 CA 创建证书。CA 将同时用于签发 Broker 证书和客户端证书,从而保证各方相互信任。CA 应存放在非常安全的位置(理想情况是完全离线、隔离网络且全盘加密)。
执行以下命令创建 CA 目录,并把 openssl 配置文件放入该目录。你可以按需修改配置文件中的默认公司名、部门等应答项。将 CA 目录位置导出为环境变量
CA_HOME——配置文件正是通过该变量定位其余文件与目录:
mkdir my-ca cd my-ca wget https://raw.githubusercontent.com/apache/pulsar/master/site2/website/static/examples/openssl.cnf export CA_HOME=$(pwd)- 执行以下命令创建必要的目录、密钥与证书:
mkdir certs crl newcerts private chmod 700 private/ touch index.txt echo 1000 > serial openssl genrsa -aes256 -out private/ca.key.pem 4096 chmod 400 private/ca.key.pem openssl req -config openssl.cnf -key private/ca.key.pem \ -new -x509 -days 7300 -sha256 -extensions v3_ca \ -out certs/ca.cert.pem chmod 444 certs/ca.cert.pem- 回答完提示问题后,CA 相关文件将存放在
./my-ca目录中:
certs/ca.cert.pem:公开证书,需要分发给所有参与方(即信任证书 / trust cert)。private/ca.key.pem:私钥,仅在为 Broker 或客户端签发新证书时需要用到,必须严加保管。
上面使用的v3_ca扩展在 openssl.cnf 中定义为basicConstraints = critical, CA:true与keyUsage = critical, digitalSignature, cRLSign, keyCertSign,这正是 CA 证书应有的关键属性。
创建服务端证书
创建好 CA 证书后,就可以生成证书请求并用 CA 为其签名。下面的命令会询问若干问题,然后生成证书。当被问到Common Name时,应填入 Broker 的主机名;也可以使用通配符匹配一组 Broker 主机名,例如*.broker.usw.example.com,这样多台机器可以复用同一张证书。
提示:有些场景下无法或没必要匹配主机名——例如 Broker 的主机名是随机生成的,或者你打算通过 IP 直连主机。此时应配置客户端关闭 TLS 主机名校验,详见下文客户端配置中的主机名校验小节。
- 执行以下命令生成私钥:
openssl genrsa -out broker.key.pem 2048Broker 期望私钥是PKCS 8格式,因此执行以下命令进行转换:
openssl pkcs8 -topk8 -inform PEM -outform PEM \ -in broker.key.pem -out broker.key-pk8.pem -nocrypt- 执行以下命令生成证书签名请求:
openssl req -config openssl.cnf \ -key broker.key.pem -new -sha256 -out broker.csr.pem- 用证书颁发机构对其签名:
openssl ca -config openssl.cnf -extensions server_cert \ -days 1000 -notext -md sha256 \ -in broker.csr.pem -out broker.cert.pem此时,你拥有了证书broker.cert.pem与私钥broker.key-pk8.pem,它们可以与ca.cert.pem一起用于为 Broker 和 Proxy 节点配置 TLS 传输加密。这里使用的server_cert扩展在 openssl.cnf 中定义了extendedKeyUsage = serverAuth,确保该证书仅用于服务端认证。
创建客户端证书
客户端证书的生成流程与服务端证书完全一致:生成私钥 → 生成 CSR → 用 CA 签名。区别在于签发时使用usr_cert扩展(其extendedKeyUsage = clientAuth, emailProtection,见 openssl.cnf)。客户端证书主要用于TLS 认证(即客户端向服务端证明身份),具体配置方法见 security-tls-authentication.md。如果你只需要传输加密,则不必为客户端签发证书。
配置 Broker
要为 Pulsar Broker 启用 TLS 传输加密,需要修改 Pulsar 安装目录conf下的 broker.conf。在 Pulsar 单机安装 中该文件位于conf/broker.conf。
在配置文件中追加以下配置项(将证书路径替换为实际路径):
brokerServicePortTls=6651 webServicePortTls=8081 tlsRequireTrustedClientCertOnConnect=true tlsCertificateFilePath=/path/to/broker.cert.pem tlsKeyFilePath=/path/to/broker.key-pk8.pem tlsTrustCertsFilePath=/path/to/ca.cert.pem各参数含义如下(均可在 conf/broker.conf 的 TLS 段找到对应注释):
| 参数 | 默认值 | 说明 |
|---|---|---|
brokerServicePortTls | 空(TLS 默认关闭) | 二进制协议(生产/消费)的 TLS 监听端口,示例取 6651 |
webServicePortTls | 空(TLS 默认关闭) | HTTP/HTTPS 管理接口的 TLS 监听端口,示例取 8081 |
tlsRequireTrustedClientCertOnConnect | false | 是否强制要求连接方提供受信任的客户端证书;为true时拒绝未通过tlsTrustCertsFilePath校验的客户端连接,等效于强制客户端 TLS 认证 |
tlsCertificateFilePath | 空 | Broker 服务端证书(如broker.cert.pem)路径 |
tlsKeyFilePath | 空 | Broker 私钥(必须是 PKCS 8 格式,如broker.key-pk8.pem)路径 |
tlsTrustCertsFilePath | 空 | 用于校验连接方证书的信任证书(如ca.cert.pem)路径,校验失败则连接被拒绝 |
关于端口,需要注意:brokerServicePortTls与webServicePortTls均为可选端口。从 PulsarService.java 可以看到,Broker 启动时会校验 web 服务端口与 TLS 端口至少存在一个,否则抛出IllegalArgumentException;随后启动的 TLS 监听地址会被记录为webServiceAddressTls与brokerServiceUrlTls,并在 PulsarService.java 中注入内部客户端与服务发现逻辑。
此外,broker.conf 还包含以下值得了解的 TLS 相关配置:
tlsCertRefreshCheckDurationSec=300:TLS 证书刷新检查周期(秒),设为0表示每个新连接都重新检查。这意味着证书轮换后无需重启 Broker 即可在下一个周期内生效。tlsAllowInsecureConnection=false:是否接受无法用tlsTrustCertsFilePath验证的客户端证书(注意这与客户端的allowInsecureConnection是不同方向的概念)。生产环境应保持false。tlsHostnameVerificationEnabled=false:Broker 与其他 Broker 建立 TLS 连接时是否校验主机名。tlsProvider:Broker 服务(二进制协议)使用的 TLS Provider。使用 CA 证书做 TLS 认证时可选OPENSSL或JDK;使用 KeyStore 时可选SunJSSE、Conscrypt等。webServiceTlsProvider=Conscrypt:Web 服务默认使用 Conscrypt 作为 TLS Provider。tlsEnabledWithKeyStore=false及一组tlsKeyStore*/tlsTrustStore*:启用 KeyStore 类型的 TLS 配置(JKS / PKCS12)。brokerClientTlsEnabled=false、brokerClientTrustCertsFilePath=:Broker 作为内部客户端(连接其他 Broker 或集群复制)时的 TLS 开关与信任证书路径。- 弃用项:
tlsEnabled(旧版统一开关)已弃用,请改用brokerServicePortTls与webServicePortTls。
更多参数及默认值的完整清单,见 Broker 配置参考。
TLS 协议版本与密码套件
可以配置 Broker(以及 Proxy)在 TLS 协商时强制使用指定的协议版本与密码套件,从而防止客户端回退(downgrade)到存在弱点的旧协议或旧密码。
协议版本与密码套件属性均支持以逗号分隔的多个值。可选值取决于你使用的 TLS Provider:Pulsar优先使用 OpenSSL(若系统可用),否则回退到 JDK 实现。
tlsProtocols=TLSv1.3,TLSv1.2 tlsCiphers=TLS_DH_RSA_WITH_AES_256_GCM_SHA384,TLS_DH_RSA_WITH_AES_256_CBC_SHA- OpenSSL 目前支持
TLSv1.1、TLSv1.2和TLSv1.3协议版本;可通过openssl ciphers -tls1_3等命令查询当前 OpenSSL 支持的密码套件列表。 - JDK 11 的支持值可从官方文档获取(TLS 协议参数与 SunJSSE 密码套件两个章节)。
需要注意的是,broker.conf 中还单独提供了webServiceTlsProtocols与webServiceTlsCiphers,用于分别控制Web 服务(HTTPS 管理接口)的协议与密码套件,而tlsProtocols/tlsCiphers控制的是二进制协议端口。同时,Broker 内部客户端(与其他 Broker 通信)的协议与密码由brokerClientTlsProtocols/brokerClientTlsCiphers控制——在 PulsarService.java 中可以看到这些值被注入到内部 Pulsar 客户端的setTlsCiphers/setTlsProtocols。
配置 Proxy
Proxy 需要在两个方向上配置 TLS:一是面向连接 Proxy 的客户端,二是 Proxy 连接Broker的方向。相关配置位于 conf/proxy.conf。
# For clients connecting to the proxy tlsEnabledInProxy=true tlsCertificateFilePath=/path/to/broker.cert.pem tlsKeyFilePath=/path/to/broker.key-pk8.pem tlsTrustCertsFilePath=/path/to/ca.cert.pem # For the proxy to connect to brokers tlsEnabledWithBroker=true brokerClientTrustCertsFilePath=/path/to/ca.cert.pem对应 conf/proxy.conf 中的实际配置项:
- 面向客户端方向:
tlsEnabledInProxy=true:该开关在 proxy.conf 中标注为已弃用,新的方式是直接设置 TLS 监听端口servicePortTls(二进制协议)与webServicePortTls(Web 服务)。弃用项保留仅为兼容。tlsCertificateFilePath/tlsKeyFilePath:Proxy 对外提供服务时使用的证书与 PKCS 8 私钥(可以复用为 Broker 签发的服务端证书)。tlsTrustCertsFilePath:用于校验客户端证书的信任证书。
- 面向 Broker 方向:
tlsEnabledWithBroker=true:是否在 Proxy 与 Broker 之间启用 TLS。brokerClientTrustCertsFilePath=/path/to/ca.cert.pem:Proxy 作为客户端连接 Broker 时使用的信任证书路径(见 proxy.conf 注释)。- 此外还可配置
tlsHostnameVerificationEnabled(Proxy 连接 Broker 时是否校验主机名)、tlsCertRefreshCheckDurationSec=300(证书刷新周期)等。
与 Broker 一样,proxy.conf 也提供tlsProtocols/tlsCiphers(二进制协议)与webServiceTlsProtocols/webServiceTlsCiphers(Web 服务)两组协议与密码套件配置,以及tlsRequireTrustedClientCertOnConnect(强制客户端证书)与 KeyStore 系列配置(tlsEnabledWithKeyStore等)。
客户端配置
启用 TLS 传输加密后,客户端需要改用加密协议与对应端口:
- Web 服务 URL:使用
https://,端口 8443(对应 Broker 的webServicePortTls)。 - Broker 服务 URL:使用
pulsar+ssl://,端口 6651(对应 Broker 的brokerServicePortTls)。
由于上文生成的服务端证书不属于任何系统默认信任链,你还必须显式指定 trust cert 路径(推荐),或者允许客户端信任未受信任的服务端证书(不推荐,见下文主机名校验讨论)。
主机名校验(Hostname verification)
主机名校验是 TLS 的一项安全特性:客户端在连接时,若服务器证书的Common Name(CN)与正在连接的主机名不匹配,则拒绝连接。默认情况下,Pulsar 客户端关闭主机名校验,因为开启它要求每个 Broker 都有对应的 DNS 记录和独立证书。
与此同时,由于管理员完全掌控证书颁发机构,攻击者极难实施中间人攻击。关于allowInsecureConnection(允许连接证书未经受信任 CA 签名的服务器):客户端默认关闭该选项,生产环境应当始终保持关闭。只要关闭allowInsecureConnection,中间人攻击就要求攻击者同时掌握 CA 的私钥——这正是把 CA 私钥离线保管的价值所在。
一个推荐开启主机名校验的场景是:多个 Proxy 节点位于 VIP 之后,且 VIP 有 DNS 记录(例如pulsar.mycompany.com)。此时可以为pulsar.mycompany.com生成一张 CN 为此域名的 TLS 证书,然后在客户端开启主机名校验。
下面示例以 Java 客户端展示主机名校验的显式关闭写法,其实该选项默认就是关闭的,可以省略。C++/Python/Node.js 客户端目前不支持配置该项。
CLI 工具
命令行工具(如pulsar-admin、pulsar-perf、pulsar-client)统一读取 Pulsar 安装目录中的 conf/client.conf。要使这些工具走 TLS,需要在该文件中追加以下参数:
webServiceUrl=https://broker.example.com:8443/ brokerServiceUrl=pulsar+ssl://broker.example.com:6651/ useTls=true tlsAllowInsecureConnection=false tlsTrustCertsFilePath=/path/to/ca.cert.pem tlsEnableHostnameVerification=false对照 conf/client.conf 中的实际注释:
webServiceUrl:REST API(管理操作)地址,TLS 时为https://…:8443/。brokerServiceUrl:二进制协议(生产/消费)地址,TLS 时为pulsar+ssl://…:6651/。tlsAllowInsecureConnection=false:是否允许连接证书无法验证的服务器,默认false。tlsEnableHostnameVerification=false:是否要求服务器主机名与证书 CN 一致,默认false。tlsTrustCertsFilePath:信任证书路径,用于校验服务器证书是否由该 CA 签发,校验失败则连接被断开。- 此外,若需要客户端证书认证,可配置
authPlugin=org.apache.pulsar.client.impl.auth.AuthenticationTls与authParams=tlsCertFile:/path/to/client-cert.pem,tlsKeyFile:/path/to/client-key.pem;以及 KeyStore 模式下的useKeyStoreTls/tlsTrustStore*系列参数。
Java 客户端
import org.apache.pulsar.client.api.PulsarClient; PulsarClient client = PulsarClient.builder() .serviceUrl("pulsar+ssl://broker.example.com:6651/") .enableTls(true) .tlsTrustCertsFilePath("/path/to/ca.cert.pem") .enableTlsHostnameVerification(false) // false by default, in any case .allowTlsInsecureConnection(false) // false by default, in any case .build();Python 客户端
from pulsar import Client client = Client("pulsar+ssl://broker.example.com:6651/", tls_hostname_verification=True, tls_trust_certs_file_path="/path/to/ca.cert.pem", tls_allow_insecure_connection=False) # defaults to false from v2.2.0 onwardsC++ 客户端
#include <pulsar/Client.h> ClientConfiguration config = ClientConfiguration(); config.setUseTls(true); // shouldn't be needed soon config.setTlsTrustCertsFilePath(caPath); config.setTlsAllowInsecureConnection(false); config.setAuth(pulsar::AuthTls::create(clientPublicKeyPath, clientPrivateKeyPath)); config.setValidateHostName(true);Node.js 客户端
const Pulsar = require('pulsar-client'); (async () => { const client = new Pulsar.Client({ serviceUrl: 'pulsar+ssl://broker.example.com:6651/', tlsTrustCertsFilePath: '/path/to/ca.cert.pem', }); })();C# 客户端
var certificate = new X509Certificate2("ca.cert.pem"); var client = PulsarClient.Builder() .TrustedCertificateAuthority(certificate) //If the CA is not trusted on the host, you can add it explicitly. .VerifyCertificateAuthority(true) //Default is 'true' .VerifyCertificateName(false) //Default is 'false' .Build();验证与后续
完成上述配置后,可以用pulsar-client(已配置 TLS 的 CLI)尝试生产/消费一条消息,确认链路可通;也可以使用openssl s_client -connect broker.example.com:6651 -CAfile ca.cert.pem之类的手段直接检查 TLS 握手与证书链。
如果在启用 TLS 传输加密的同时还需要客户端身份认证,请继续阅读 security-tls-authentication.md;若需要 FIPS 合规的 Bouncy Castle Provider,参考 security-bouncy-castle.md;所有 Broker 配置项的完整默认值清单见 reference-configuration.md。
【免费下载链接】pulsarApache Pulsar - distributed pub-sub messaging system项目地址: https://gitcode.com/gh_mirrors/pulsar28/pulsar
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考