Strimzi Kafka Operator UserST 深度解析:KafkaUser 认证、配额与证书生命周期的系统测试验证
【免费下载链接】strimzi-kafka-operatorApache Kafka® running on Kubernetes项目地址: https://gitcode.com/GitHub_Trending/st/strimzi-kafka-operator
UserST 是 Strimzi Kafka Operator 系统测试框架中专门覆盖 User Operator 的测试套件(源码位于 UserST.java),它通过 8 个端到端测试用例,验证KafkaUser自定义资源从创建、更新到删除全生命周期中 TLS / SCRAM-SHA-512 / tls-external 三种认证方式、配额(quotas)下发、Secret 前缀管理、证书有效期控制等关键行为。读完本文,你不仅能理解每个用例验证的运维语义,还能顺藤摸瓜看到 User Operator 源码中对应参数的真实生效位置,从而在生产环境中正确配置和排障。
测试套件总览与前置准备
UserST 通过@SuiteDoc注解声明了套级描述与前置步骤,这些注解最终被工具生成到 UserST.md 文档中,形成"测试代码即文档"的闭环。套件携带REGRESSION与USER两个标签(@Tag(REGRESSION)、@Tag(USER)),用于按功能域筛选测试。
前置步骤(Before test execution steps):
| 步骤 | 动作 | 结果 | | - | - | - | | 1. | 初始化共享测试存储并部署带必要配置的 Kafka 集群。 | Kafka 集群和 scraper pod 部署就绪,可供测试使用。 |
对应源码中的@BeforeAll setup()方法:先通过SetupClusterOperator以默认配置安装集群 Operator,再创建 1 副本 broker 池与 1 副本 controller 池(KafkaNodePoolTemplates),部署Kafka集群时额外配置了一个名为scramshatls的 9095 端口 SCRAM-SHA-512 TLS 监听器,并显式将 Clients CA 的validityDays设为caValidityDays = 10、renewalDays设为caRenewalDays = 5——这两个值正是后文testTlsValidityDays断言默认有效期的依据。最后部署一个 scraper pod,供测试通过 Kafka CLI 工具在集群内直接查询配额状态。
套件统一标签为 user-operator,该标签文档说明这些测试覆盖 User Operator 对 KafkaUser 资源的管理:认证机制(TLS、SCRAM-SHA-512、外部 TLS)、ACL 授权、配额强制执行、带自定义前缀的 Secret 管理,以及用户生命周期操作。
testCreatingUsersWithSecretPrefix:自定义 Secret 前缀
验证目标:为组织用户 Secret 而使用自定义 secret 前缀时,TLS 与 SCRAM 用户的创建、更新、删除全程正确。
| 步骤 | 动作 | 结果 | | - | - | - | | 1. | 使用自定义 secret 前缀配置集群 Operator。 | 集群 Operator 以指定前缀重新配置。 | | 2. | 创建 TLS 用户和 SCRAM-SHA-512 用户。 | 两种认证方式的用户创建成功。 | | 3. | 验证用户 Secret 以正确前缀创建。 | Secret 名称中包含配置的前缀。 | | 4. | 测试消息收发。 | 两种认证方式均成功收发消息。 | | 5. | 更新用户并验证 Secret 更新。 | 用户更新反映在带前缀的 Secret 中。 | | 6. | 删除用户并验证清理。 | 用户删除后带前缀的 Secret 被正确移除。 |
测试在独立命名空间中以@ParallelNamespaceTest运行,自建 3 副本 broker/controller 池集群,监听器为 plain(9092,SCRAM-SHA-512 认证)与 TLS(9093,TLS 认证)两个,核心配置是在spec.entityOperator.userOperator.secretPrefix中设置前缀"top-secret-"。随后断言top-secret-encrypted-leopold(TLS 用户)与top-secret-scramed-leopold(SCRAM 用户)两个 Secret 均存在,分别用两种认证方式完成消息收发,最后删除 KafkaUser 并验证带前缀的 Secret 随 OwnerReference 级联删除(assertNull确认 Secret 不再存在)。
源码佐证:前缀参数在 User Operator 中由环境变量STRIMZI_SECRET_PREFIX注入,定义于 UserOperatorConfig.java(默认值为空字符串);而集群 Operator 侧则通过EntityUserOperator模型把 CRD 里的secretPrefix翻译成该环境变量,见 EntityUserOperator.java,未配置时使用EntityUserOperatorSpec.DEFAULT_SECRET_PREFIX。Secret 命名规则本身是简单拼接:KafkaUserModel.getSecretName() 返回secretPrefix + username,这解释了为何客户端取证书时必须使用带前缀的名称。
testScramUserWithQuotas / testTlsUserWithQuotas / testTlsExternalUserWithQuotas:三类认证的配额下发
这三个用例分别验证 SCRAM-SHA-512、TLS、TLS-external 三种认证类型的用户都能配置配额:
| 用例 | 动作 | 结果 | | - | - | - | | testScramUserWithQuotas | 创建带配额配置的 SCRAM-SHA-512 用户。 | 用户创建成功,SCRAM 认证与配额设置均生效。 | | testTlsUserWithQuotas | 创建带配额配置的 TLS 用户。 | 用户创建成功,TLS 认证与配额设置均生效。 | | testTlsExternalUserWithQuotas | 创建带配额配置的 TLS-external 用户。 | 用户创建成功,外部 TLS 认证与配额设置均生效。 |
三者都委托给公共辅助方法testUserWithQuotas(KafkaUser user),其验证流程如下:
- 创建带配额的用户:固定四组配额值——
prodRate = 1111(producer_byte_rate)、consRate = 2222(consumer_byte_rate)、reqPerc = 42(request_percentage)、mutRate = 10(controller_mutation_rate),通过KafkaUserTemplates.userWithQuotas(...)注入。 - 用 Kafka CLI 验证配额真正落到集群:通过 scraper pod 执行
KafkaCmdClient.describeUserUsingPodCli(...),断言输出中同时包含Quota configs for user-principal '...' are、request_percentage=42、producer_byte_rate=1111、consumer_byte_rate=2222、controller_mutation_rate=10.0。这是端到端验证——配额不只是写在 CRD 上,而是通过 Admin API 下发到了 Kafka 集群。 - 按认证类型分别配置客户端:SCRAM 用户切到 9095 端口并使用
ClientsAuthentication.configureTlsScramSha(...);TLS 用户用configureTls(...);tls-external 用户则先由SecretUtils.createExternalTlsUserSecret(...)用外部 CA 的证书构建客户端 Secret,再按 TLS 方式连接。随后用 producer/consumer Job 收发指定条数的消息并等待成功。 - 删除用户并验证配额清理:以
DeletionPropagation.FOREGROUND策略删除 KafkaUser,再次 describe 断言用户名及四项配额配置全部消失。
源码佐证:CRD 中的KafkaUserQuotas与 Kafka Admin API 的ClientQuotaAlteration.Op之间的双向转换实现在 QuotaUtils.java(fromClientQuota/toClientQuotaAlterationOps/quotasEquals),配额的实际写入由 QuotasOperator.java 完成,并经由 QuotasBatchReconciler.java 对 Admin API 请求做微批处理,这与UserOperatorConfig中STRIMZI_BATCH_QUEUE_SIZE(默认 1024)、STRIMZI_BATCH_MAXIMUM_BLOCK_SIZE(默认 100)等参数相呼应。
testTlsExternalUser:外部 TLS 认证与 Simple ACL 授权
验证目标:使用外部(非 Strimzi 签发)证书进行 tls-external 认证,且 Simple 授权(ACL)能正确控制访问。
| 步骤 | 动作 | 结果 | | - | - | - | | 1. | 部署带 TLS 认证与 Simple 授权的 Kafka 集群。 | 集群启用 TLS 监听器与 Simple ACL 授权。 | | 2. | 创建带 ACL 权限的 TLS-external 用户。 | 用户按指定 ACL 规则获得 topic 访问权限。 | | 3. | 为用户创建自定义外部 TLS Secret。 | 外部 TLS Secret 携带自定义证书。 | | 4. | 使用 tls-external 用户测试消息收发。 | 使用外部 TLS 证书成功收发消息。 |
源码层面的关键断言值得注意(UserST.java):
- 用户创建的 ACL 规则包括:对 topic 的
READ/WRITE/DESCRIBE/CREATE操作(LITERAL模式),以及对消费者组的READ操作。 - Operator 不应为 tls-external 用户创建 Secret:
assertThat(...secrets()...withName(username).get(), nullValue())。这是因为证书由外部体系签发,User Operator 无法也无权为其生成证书 Secret。 - KafkaUser 状态中的
username字段为CN=<用户名>形式——这与 KafkaUserModel.getTlsUserName() 和 getUserName() 的实现一致:TLS 与 tls-external 用户的 Kafka 主体(principal)是CN=前缀的证书主题,而 SCRAM 用户直接使用资源名。 - ACL 收紧后的负向验证:将用户授权缩减为仅
READ/DESCRIBE后,更换 producer Job 名称重新投递,断言 Pod 日志出现"Not authorized",即写入被 ACL 拒绝——验证了授权变更会实时生效。
testTlsValidityDays:KafkaUser 级证书有效期与自动续期
验证目标:KafkaUser的spec.authentication中配置的 mTLSvalidityDays与renewalDays生效,且证书会按新值重新签发。
| 步骤 | 动作 | 结果 | | - | - | - | | 1. | 在现有集群创建 KafkaTopic。 | topic 创建完成。 | | 2. | 创建不配置validityDays/renewalDays的 TLS 用户,使用 User Operator(Clients CA)默认值。 | 用户以 Operator 侧默认值创建。 | | 3. | 读取用户 Secret,检查证书有效期。 | 默认有效期为 10 天(本测试套件中 Clients CA 的caValidityDays,文档步骤 3 描述的"200 天"是生产默认口径,本套件通过spec.kafka.clientsCa显式覆盖为 10)。 | | 4. | 用该 TLS 用户收发消息。 | 收发成功。 | | 5. | 将validityDays/renewalDays改为 40 和 20。 | KafkaUser 更新成功。 | | 6. | 更新后证书自动续期。 | 用户证书已续期。 | | 7. | 再次读取 Secret 检查有效期。 | 有效期变为 40 天。 | | 8. | 用新证书再收发消息。 | 收发成功。 |
源码实现链条非常清晰:
- KafkaUserModel.maybeGenerateCertificates() 中的核心逻辑:
int validityDays = kafkaUserTlsClientAuthentication.getValidityDays() != null ? kafkaUserTlsClientAuthentication.getValidityDays() : caValidityDays;——即KafkaUser 自身配置优先,否则回退到 Clients CA 的默认值(caValidityDays/caRenewalDays由 Operator 配置STRIMZI_CA_VALIDITY(默认 365)与STRIMZI_CA_RENEWAL(默认 30,见 UserOperatorConfig.java 提供,可被Kafka.spec.kafka.clientsCa覆盖)。 - 强制续期机制:修改
validityDays后证书并不会立即重签,测试中通过给 Secret 打strimzi.io/force-renew: "true"注解触发(Annotations.ANNO_STRIMZI_IO_FORCE_RENEW),源码中 maybeGenerateCertificates() 检测到该注解即强制生成新证书;测试随后用waitForCertToChange等待user.crt内容变化,并用KafkaUserUtils.getValidityDaysOfCertificate(...)从证书 notBefore/notAfter 解析出实际有效天数做精确断言(10 → 40)。
testUpdateUser:从 TLS 切换到 SCRAM-SHA-512 的 Secret 内容迁移
验证目标:将用户认证方式从 TLS 更新为 SCRAM-SHA-512 后,Secret 内容随之正确迁移。
| 步骤 | 动作 | 结果 | | - | - | - | | 1. | 创建 TLS Kafka 用户。 | Secret 中包含 TLS 证书。 | | 2. | 校验 TLS 用户 Secret 内容。 | 含ca.crt、user.crt、user.key字段。 | | 3. | 用 TLS 用户测试消息收发。 | 收发成功。 | | 4. | 将认证更新为 SCRAM-SHA-512。 | 更新成功。 | | 5. | 校验 SCRAM 用户 Secret 内容。 | 含password字段,TLS 证书被移除。 | | 6. | 用 SCRAM 用户测试消息收发。 | 收发成功。 |
源码中的 Secret 生成规则印证了两类 Secret 的数据键差异(KafkaUserModel.generateSecret()):TLS 用户写入ca.crt、user.key、user.crt(若启用 PKCS12 还有user.p12/user.password);SCRAM 用户写入password与sasl.jaas.config(由 getSaslJsonConfig() 生成的 ScramLoginModule 配置串)。测试更新认证类型后先等待observedGeneration递增再断言新 Secret 的$.data.password非空,最后切换到 9095 的 SCRAM TLS 监听器完成收发验证。
testUserWithNameMoreThan64Chars:TLS 用户名 64 字符上限
验证目标:超过 64 字符的用户名在 TLS 认证下被拒绝,而 SASL(SCRAM)用户不受此限制。
| 步骤 | 动作 | 结果 | | - | - | - | | 1. | 创建合法名(64 字符)的 Kafka 用户。 | 用户创建成功并进入就绪状态。 | | 2. | 创建长名(65 字符)的 SASL 用户。 | SCRAM 用户创建成功,因其支持更长名称。 | | 3. | 尝试创建长名(65 字符)的 TLS 用户。 | 创建失败,抛出校验错误。 | | 4. | 校验错误条件与消息。 | 错误条件指明用户名长度限制并给出对应错误信息。 |
源码佐证:该限制根源于 OpenSSL 对证书 CN 长度的限制。KafkaUserModel.validateTlsUsername() 只在认证类型为KafkaUserTlsClientAuthentication时检查name.length() > OpenSslCertIssuer.MAXIMUM_CN_LENGTH(64),并抛出InvalidResourceException;SCRAM 用户不走证书路径,因此不受限。测试断言KafkaUser.status.conditions[0]的 message 包含"only up to 64 characters"、reason 为ExecutionException,这正是该异常经 Operator 处理后写入 CRD 状态的最终形态。
相关文档与延伸阅读
- User Operator 功能域标签与关联用例索引:user-operator.md,其中还列出了性能/可扩展性用例
testCapacity、testLatencyUnderLoad、testScalability(对应 UserOperatorPerformance.md 与 UserOperatorScalabilityPerformance.md)。 - KafkaUser 资源 YAML 示例:examples/user/kafka-user.yaml。
- KafkaUser API 参考:KafkaUserQuotas.adoc、KafkaUserTemplate.adoc、AclRule.adoc。
- User Operator 核心实现:KafkaUserModel.java(认证/Secret/证书模型)、UserOperatorConfig.java(环境变量与默认值)、QuotasOperator.java 与 ScramCredentialsOperator.java(Admin API 写入)。
- 单元测试对本文结论的直接印证:KafkaUserModelTest.java(含 64 字符校验、validityDays 覆盖、secret 前缀等用例)。
小结
UserST 用 8 个用例覆盖了 KafkaUser 生命周期的关键运维场景:Secret 前缀管理、三类认证的配额下发与清理、外部 TLS + ACL 授权、证书有效期与强制续期、认证方式热切换、以及 TLS 用户名的 64 字符硬限制。每个用例的断言都能在 User Operator 源码中找到对应实现——STRIMZI_SECRET_PREFIX到getSecretName()的拼接、KafkaUserQuotas到ClientQuotaAlteration.Op的转换、validityDays的用户级覆盖逻辑、以及validateTlsUsername()的 CN 长度校验。这些测试不仅作为回归防线,也实质上充当了 User Operator 行为的"活文档":当文档 UserST.md 中的步骤与代码注解不一致时,以@TestDoc/@SuiteDoc注解及其对应的断言逻辑为准。
【免费下载链接】strimzi-kafka-operatorApache Kafka® running on Kubernetes项目地址: https://gitcode.com/GitHub_Trending/st/strimzi-kafka-operator
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考