文章摘要
本文整理自 2022 年 8 月 Apache Pulsar Meetup 上傅腾题为《Apache Pulsar 企业级安全实践》的分享。数据安全已经成为企业的一项重要竞争优势。傅腾针对集群环境分享 Apache Pulsar 的认证和授权实现,讲述企业如何构建安全的 Apache Pulsar 集群并打造满足个性化要求的 Pulsar 认证或授权插件,以满足不同的企业安全需求。
作者介绍
傅腾,StreamNative 技术支持工程师,有着 9 年电信运营商行业大数据相关经验,熟悉大数据的平台架构、建设、安全和维护,对实时数仓、云原生和 AI 具有平台类产品建设经验。
多种安全组合
Apache Pulsar 提供了多种安全组合,包括认证(支持全链路组件认证)、授权(ACL)、传输加密(TLS 和 mTLS)以及端到端加密(仅生产者和消费者可加解密数据),企业可根据实际环境的安全需求来搭配使用。
全链路可信
上图为全链路可信场景。该场景通常见于开放集群或内部测试、功能验证等。该场景中 Broker、Bookie 均开放,生产者明文写入数据。
内网可信
上面两图皆为内网可信场景。其中,第一种设计会做传输层加密和 Broker 认证授权,内网依旧开放。第二种设计将传输层加密放在外部与 Proxy 之间,Proxy 也要做认证。
上图为内网可信场景中的第三种设计,即常见的负载均衡场景,外部到负载均衡器做传输层加密,后者将数据交给需要认证的 Proxy,再流向 Broker。
内网不可信
在内网不可信场景中,内部节点可能因为各种原因有安全隐患,这就需要在每一传输层都开启加密和认证,在 Broker 开启授权。该场景常见于企业跨部门协作、与子公司协作、与外部沟通等情况。
服务不可信(端到端加密)
服务不可信场景一般是指第三方(如云厂商)提供 Pulsar 服务,为此所有链路都要端到端加密,且只有生产者和消费者才能获取原始数据。
易扩展的安全框架
Apache Pulsar 有着简洁、非常容易扩展的安全框架。企业可方便地自定义安全插件,包括可插拔式的认证和授权、易集成的代码结构、同时支持多种认证的认证链、认证缓存和检测机制。
对于可插拔式的认证和授权:
- • Pulsar 服务端(如 Broker、Proxy)会对客户端做身份认证,并记录一个 role 作为后续授权的客户端身份标识。该 role 可视为用户实际登录的 token。
- • 默认支持的认证插件:TLS、Athenz、Kerberos、JWT、OAuth2、Basic。
- • 支持链式认证,同时支持多个认证源。服务端会将所有 Provider 缓存到本地并初始化,根据客户端传来的标识类型寻找对应的 Provider 做认证。链式认证在云环境中很常见。配置 authenticationProviders:
- • Pulsar 支持两种鉴权插件:
- • AuthorizationProvider:
- • MultiRolesTokenAuthorizationProvider,仅适用于认证插件是 JWT 的认证方式:
认证的触发时机发生在客户端建立连接时:
1.客户端将一个 CommandConnect 发送给 Broker;
2.AuthenticationService 从 CommandConnect 中可以获取认证方法类型(AuthMethodName);
3.AuthenticationService 再根据缓存的认证插件,调用 authenticate 方法进行认证。
4.注意:缓存认证凭证和认证的过期检查(AuthChallenge,默认值 authenticationRefreshCheckSeconds=60s)
Pulsar 的权限体系中,支持的鉴权级别分别是:
- • Broker,支持 superuser;
- • Tenant,支持 tenant admin;
- • Namespace,支持生产、消费和 Functions;
- • Topics,支持生产和消费。
插件实现有几点需要注意:initialize() 可以获取 Broker 级别的配置信息和管理元数据;每次鉴权需要先检查传进来的用户是否是 superuser 和 tenant admin,再检查具体的权限。还要注意,只有 superuser 或者 tenant admin 才能为用户赋权。
案例介绍
案例一:使用 JWT 做认证
JSON Web Token(RFC-7519)是 Web 服务中常用的一种认证方案,简称 JWT 认证。JWT 基于一个 token 字符串来识别用户身份。该 token 有着严格的三层结构:
- • Header 层:JSON 串,用于指定签名算法(Pulsar 中默认使用 HS256),使用 base64url 编码。
- • Payload 层:JSON 串,用于指定用户名标识(Pulsar 中定义为 sub)和过期时间,使用 base64url 编码。
- • Signature 层:使用特定算法,将 Header 和 Payload 加一个 secret 值编码而来。签名保证了 token 不被中途篡改。
需要注意前两层是透明的,可以反解出来。
基于 JWT 的特性,在 Pulsar 中使用需要注意几点:
1.使用 JWT 可以做认证和授权,但数据仍然处于暴露状态,所以在安全要求较高的环境建议开启 TLS 对数据传输加密(会牺牲一小部分性能),以进一步巩固安全性。
2.JWT 最重要的特性是不需要借助第三方服务(例如 Kerberos 之于 KDC),凭借 JWT 内容本身就能验证 token 是否有效。这也带来一个问题,即 token 一旦签发,在有效期间将会一直有效,无法撤回。因此对于执行某些重要操作的 token,有效期要尽量设置短。
3.Pulsar 支持两种 JWT 加密方式,即使用对称秘钥(secret key)和使用非对称密钥(private/public key),选择其中一个即可。
4.注意 token 的配置容易出错(例如需要操作 pulsar-admin 但仅给了 test-user 的 token),建议配置后可使用 validate 命令做验证。
5.Pulsar Broker 会缓存客户端的认证信息,并会在一个固定时间(默认 60 秒)来检查每个连接的认证是否过期,这个刷新时间可以配置 broker.conf 中的 authenticationRefreshCheckSeconds 参数来自定义。
6.注意 token 参数的配置既可使用字符串形式,也可使用文件形式,可择优选择。
7.使用 bin/pulsar tokens show 命令可以查看 token 的 header 和 payload:
8.使用 bin/pulsar tokens validate 命令可以用 secret key 或者 public key 验证 token:
案例二:使用 Kerberos 做认证
Kerberos 是大数据领域常用的认证方案,以简单易用和稳定性著称。Pulsar 使用 Java 的 JAAS 机制来支持通过 Kerberos 做身份认证。JAAS 中一个用户信息对应一个 section 。对于 Kerberos 认证而言,一个用户信息中最重要的是 principle 和 keytab,现在可以方便地封装到一个 section 里,最后将这些用户信息拼装到 jaas.conf 文件中。
例如:
SectionName 是指定了用户名标识,内部封装了一个 Kerberos 用户信息。Pulsar 使用 Kerberos 认证时,从配置上而言,就是告知进程该以哪个 section 的身份来启动程序。以 broker 为例:
1.在 broker.conf 中指定服务使用的 section 。
2.而 section 信息是在 pulsar_env.sh 中通过指定 jaas.conf 文件来加载获得的。
使用 Kerberos 做认证时有几点需要注意:
1.Broker service principal 的 {hostname} 和 advertisedAddress 保持一致。例如 Broker service 使用的 Principal 是 "broker/172.17.0.7@SNIO",则 advertisedAddress 也必须配置为相同的 IP:172.17.0.7。因为客户端从 TGT Server 获取到的 Broker 服务端 Principal 中的 hostname 部分是 advertisedAddress 定义的 IP,可从 KDC 日志(/var/log/krb5kdc.log)查看到问题。Broker 服务会涉及两个 Principal,一个是 Broker 服务注册的 Principal,另一个是 TGT Server 返回的 Principal。所以如果两个 Principal 内部的 IP 不一致,用户会永远无法访问到服务,毕竟用户一次只能携带一个服务端Principal 信息。
2.服务端 Principal 命名规则注意:
- • 必须包含完整的三部分:service/hostname@REALM。
- • service 命名部分,pulsar 里建议使用关键字如
broker
、proxy
(其他会报 warn)。 - • 主机地址部分为了防止多块网卡的 IP 配置问题,均建议使用 hostname。但如果没有 DNS 解析环境,请参考第一条。可使用 IP,保证服务 IP 和 advertisedAddressIP 一致。
- • REALM 域名建议使用大写,注意不要复制错。
3.在配置 FQDN 解析时,Kerberos 所有主机都需要支持 FQDN 解析(少量机器可在 /etc/hosts 配置所有主机 DNS 信息)。
案例三:自定义一个鉴权插件
这里以一个鉴权插件为例介绍可插拔的认证和授权实践。案例背景是我们需要界面化的鉴权过程,为此选用大数据领域常用的鉴权组件 Ranger 进行界面化权限管理。具体的开发流程是:
1.根据 Pulsar 现有的权限体系,结合 Ranger 的权限结构,将 Pulsar 注册为 Ranger 的一个服务。
2.在鉴权插件实现 Pulsar 鉴权接口,并在鉴权方法(尤其是初始化方法)中创建 Ranger Client 连接 Ranger 服务做初始化操作。每一个鉴权点都要读取配置好的策略信息进行鉴权。
编写 Ranger 服务定义文件时分为几步:
1.定义资源(包括租户、命名空间和 topic)
2.定义权限类型(如生产和消费)
Pulsar 中的鉴权接口代码如下:
Ranger 方法的代码如下:
最终完成效果如下:
Q & A
Q:JWT 认证读取本地 token 文件时是否实时?是否需要重启 Proxy 或 Broker?
A:Client 是被动读取 token 文件的。客户端 token 文件变更不影响服务端,即不需要重启。
Q:生产者是否支持在 HTTP 发送消息过程中在连接时认证?
A:Pulsar 使用 Pulsar 协议发送消息,在第一次连接的时候发起认证。对于管理侧如 pulsar-admin 使用 HTTP 协议,而 HTTP 协议每次连接都会发起认证。
Q:对外鉴权时,是否只需配置 Proxy 而不管 Broker?
A:鉴权是由 Broker 来做的,因此需要配置 Broker。
Q:请具体介绍使用 JWT 时 Broker 对客户端认证信息的缓存策略。
A:有线程专门负责周期性检测所缓存的客户端认证是否过期。如果发现过期,就发 auth challenge 命令给客户端,客户端接收到 auth challenge 命令后会读取 token 文件发送给 Broker;如果检测到更新后的 token 文件,认证就能继续通过,Broker 端重新缓存认证信息。如果客户端在下次检查周期内没有返回认证数据,就会关闭连接。