Skip to content

[ISSUE #8920] Refactor SSL context loading process to support multiple protocols dynamic loading #9483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 27, 2025

Conversation

EnableAsync
Copy link
Contributor

@EnableAsync EnableAsync commented Jun 17, 2025

Which Issue(s) This PR Fixes

Fixes #8920

Brief Description

This PR adds functionality to dynamically reload TLS certificates without requiring a server restart. The implementation monitors certificate and key files for changes and automatically updates the TLS configuration when both files are modified.

Key changes:

  • Added file monitoring for TLS certificate and private key files
  • Implemented logic to detect simultaneous changes to both files
  • Added certificate reload mechanism that updates TLS configuration in-place
  • Eliminated the need for service restarts when renewing certificates

This feature improves system availability by allowing certificate updates with zero downtime, which is particularly valuable for production environments with automatic certificate renewal processes.

How Did You Test This Change?

I've implemented comprehensive tests to validate the dynamic TLS certificate update feature. The tests cover several key aspects:

  1. TlsCertificateManager Tests
  • Verified the certificate monitoring logic correctly detects changes in certificate files
  • Confirmed reload is triggered when both certificate and key files are modified
  • Validated that changes to trust certificates trigger immediate reload
  • Ensured the listener notification system works properly when certificate files change

These tests ensure the system can update TLS certificates without requiring a service restart, improving system availability during certificate renewals.

- 在 GrpcServer 和 RemotingProtocolServer 中添加文件监视服务,用于监控 TLS 证书和密钥的变化
- 实现证书和密钥变更时重新加载 SSL 上下文的逻辑
- 优化 ProxyAndTlsProtocolNegotiator 中的 SSL 上下文加载过程
- 添加日志记录,方便调试和监控 TLS 相关操作
- 重构 GrpcServer 中的证书监控逻辑,提取到独立的 GrpcCertKeyFileWatchListener 类中
- 优化证书变更处理流程,提高代码可读性和维护性
- 新增 GrpcServerTest 类,为 gRPC服务器和证书监控添加单元测试- 测试覆盖了各种证书变更场景,包括单独变更和组合变更
- 验证了证书变更时 SSLContext 的重新加载和错误处理

Signed-off-by: Async <raisinata@foxmail.com>
- 重构 GrpcServer 中的证书监控逻辑,提取到独立的 GrpcCertKeyFileWatchListener 类中
- 优化证书变更处理流程,提高代码可读性和维护性
- 新增 GrpcServerTest 类,为 gRPC服务器和证书监控添加单元测试- 测试覆盖了各种证书变更场景,包括单独变更和组合变更
- 验证了证书变更时 SSLContext 的重新加载和错误处理

Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
- 移除 FileWatchService,改用 TlsCertificateManager 统一管理 TLS证书
- 实现 TlsContextReloadListener 接口,响应 TLS 证书更新
- 优化 GrpcServer 和 RemotingProtocolServer 中的 TLS 证书更新逻辑
- 新增单元测试验证 TLS 证书更新功能

Signed-off-by: Async <raisinata@foxmail.com>
- 重构了多个测试类中的重复代码- 提高了测试的可读性和维护性
- 确保在测试中正确关闭资源

Signed-off-by: Async <raisinata@foxmail.com>
- 移除了不必要的导入项
- 显式导入了所有活动类,提高了代码的可读性和维护性

Signed-off-by: Async <raisinata@foxmail.com>
}
}

static class GrpcTlsReloadHandler implements TlsCertificateManager.TlsContextReloadListener {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no static

Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
}
}

public static TlsCertificateManager getInstance() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid using a singleton? use Dependency Injection instead

sslContext = loadSslContext();
try {
loadSslContext();
log.info("SSLContext created for proxy server");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSLContext / SslContext 需要保持一致,保持日志的严谨性

try {
loadSslContext();
log.info("SSLContext created for proxy server");
} catch (IOException | CertificateException e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest catch throwable

Copy link
Contributor Author

@EnableAsync EnableAsync Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I want to crash directly if an exception is thrown during SSLContext initialization

@@ -231,7 +245,7 @@ protected void handleHAProxyTLV(HAProxyTLV tlv, Attributes.Builder builder) {
return;
}
Attributes.Key<String> key = AttributeKeys.valueOf(
HAProxyConstants.PROXY_PROTOCOL_TLV_PREFIX + String.format("%02x", tlv.typeByteValue()));
HAProxyConstants.PROXY_PROTOCOL_TLV_PREFIX + String.format("%02x", tlv.typeByteValue()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format

@codecov-commenter
Copy link

codecov-commenter commented Jun 18, 2025

Codecov Report

Attention: Patch coverage is 13.97059% with 117 lines in your changes missing coverage. Please review.

Project coverage is 47.98%. Comparing base (cfddd12) to head (99dc82a).
Report is 5 commits behind head on develop.

Files with missing lines Patch % Lines
...etmq/proxy/service/cert/TlsCertificateManager.java 0.00% 48 Missing ⚠️
...etmq/proxy/grpc/ProxyAndTlsProtocolNegotiator.java 36.11% 21 Missing and 2 partials ⚠️
...ava/org/apache/rocketmq/proxy/grpc/GrpcServer.java 0.00% 14 Missing ⚠️
...ocketmq/proxy/remoting/RemotingProtocolServer.java 0.00% 10 Missing ⚠️
...n/java/org/apache/rocketmq/proxy/ProxyStartup.java 0.00% 5 Missing ⚠️
.../apache/rocketmq/proxy/grpc/GrpcServerBuilder.java 0.00% 5 Missing ⚠️
...e/rocketmq/remoting/netty/NettyRemotingClient.java 0.00% 5 Missing ⚠️
...mq/proxy/remoting/MultiProtocolRemotingServer.java 0.00% 2 Missing ⚠️
...protocol/http2proxy/Http2ProtocolProxyHandler.java 0.00% 2 Missing ⚠️
...e/rocketmq/remoting/netty/NettyRemotingServer.java 33.33% 2 Missing ⚠️
... and 1 more
Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #9483      +/-   ##
=============================================
- Coverage      48.15%   47.98%   -0.17%     
+ Complexity     12017    11984      -33     
=============================================
  Files           1308     1309       +1     
  Lines          92174    92351     +177     
  Branches       11790    11814      +24     
=============================================
- Hits           44382    44314      -68     
- Misses         42302    42526     +224     
- Partials        5490     5511      +21     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

//import static org.mockito.Mockito.when;
//
//@ExtendWith(MockitoExtension.class)
//public class GrpcServerTest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为社么注释掉这些了

- 将 TlsCertificateManager 实例化移至 ProxyStartup 类
- 更新 GrpcServer 和 RemotingProtocolServer 类以使用 TlsCertificateManager
- 移除冗余的 TLS 证书管理相关测试用例
- 优化 TLS 上下文重载逻辑

Signed-off-by: Async <raisinata@foxmail.com>
- 将 cert file changed 日志信息改为更通用的 File changed
- 保持代码风格一致性,提高日志的可读性和维护性

Signed-off-by: Async <raisinata@foxmail.com>
- 增加对 TlsCertificateManager 各种方法的单元测试
- 涉及到的测试场景包括:
  - 构造函数
  - 启动和关闭
  - 注册和注销监听器
  - 文件变更通知(证书、密钥、未知文件等)
  - 多个监听器的情况
  - 监听器抛出异常的情况
  - 增加对内部 CertKeyFileWatchListener 的测试

Signed-off-by: Async <raisinata@foxmail.com>
@EnableAsync EnableAsync force-pushed the feat/cert-update-online branch from 7c4e38f to 6b5c4a5 Compare June 20, 2025 10:49
-移除了未使用的 import 语句
- 替换了 import static语句,使其更加有序
- 删除了未使用的静态方法断言(verify、times、never)
- 重置了 mock 对象以避免测试之间的干扰

Signed-off-by: Async <raisinata@foxmail.com>
} else {
String tlsCertPath = ConfigurationManager.getProxyConfig().getTlsCertPath();
String tlsKeyPath = ConfigurationManager.getProxyConfig().getTlsKeyPath();
try (InputStream serverKeyInputStream = Files.newInputStream(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format strange

Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Signed-off-by: Async <raisinata@foxmail.com>
Copy link
Contributor

@lollipopjin lollipopjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lollipopjin lollipopjin merged commit 832562f into apache:develop Jun 27, 2025
10 of 12 checks passed
WJ66880 pushed a commit to WJ66880/rocketmq that referenced this pull request Jul 1, 2025
…ultiple protocols dynamic loading (apache#9483)

* feat(proxy): 添加 gRPC 和 Remoting 服务器的 TLS 证书热更新支持

- 在 GrpcServer 和 RemotingProtocolServer 中添加文件监视服务,用于监控 TLS 证书和密钥的变化
- 实现证书和密钥变更时重新加载 SSL 上下文的逻辑
- 优化 ProxyAndTlsProtocolNegotiator 中的 SSL 上下文加载过程
- 添加日志记录,方便调试和监控 TLS 相关操作

* refactor(proxy): 重构 gRPC 证书监控逻辑并添加单元测试

- 重构 GrpcServer 中的证书监控逻辑,提取到独立的 GrpcCertKeyFileWatchListener 类中
- 优化证书变更处理流程,提高代码可读性和维护性
- 新增 GrpcServerTest 类,为 gRPC服务器和证书监控添加单元测试- 测试覆盖了各种证书变更场景,包括单独变更和组合变更
- 验证了证书变更时 SSLContext 的重新加载和错误处理

Signed-off-by: Async <raisinata@foxmail.com>

* refactor(proxy): 重构 gRPC 证书监控逻辑并添加单元测试

- 重构 GrpcServer 中的证书监控逻辑,提取到独立的 GrpcCertKeyFileWatchListener 类中
- 优化证书变更处理流程,提高代码可读性和维护性
- 新增 GrpcServerTest 类,为 gRPC服务器和证书监控添加单元测试- 测试覆盖了各种证书变更场景,包括单独变更和组合变更
- 验证了证书变更时 SSLContext 的重新加载和错误处理

Signed-off-by: Async <raisinata@foxmail.com>

* fix: code format

Signed-off-by: Async <raisinata@foxmail.com>

* test: add test cases

Signed-off-by: Async <raisinata@foxmail.com>

* fix: code format

Signed-off-by: Async <raisinata@foxmail.com>

* refactor(proxy): 重构 TLS证书更新逻辑

- 移除 FileWatchService,改用 TlsCertificateManager 统一管理 TLS证书
- 实现 TlsContextReloadListener 接口,响应 TLS 证书更新
- 优化 GrpcServer 和 RemotingProtocolServer 中的 TLS 证书更新逻辑
- 新增单元测试验证 TLS 证书更新功能

Signed-off-by: Async <raisinata@foxmail.com>

* test(proxy): 优化 TLS 相关测试用例

- 重构了多个测试类中的重复代码- 提高了测试的可读性和维护性
- 确保在测试中正确关闭资源

Signed-off-by: Async <raisinata@foxmail.com>

* refactor(proxy): 优化代码导入结构

- 移除了不必要的导入项
- 显式导入了所有活动类,提高了代码的可读性和维护性

Signed-off-by: Async <raisinata@foxmail.com>

* update

* fix: no static

Signed-off-by: Async <raisinata@foxmail.com>

* fix: add SingletonHolder for TlsCertificateManager

Signed-off-by: Async <raisinata@foxmail.com>

* refactor

* refactor(proxy): 重构 TLS证书管理

- 将 TlsCertificateManager 实例化移至 ProxyStartup 类
- 更新 GrpcServer 和 RemotingProtocolServer 类以使用 TlsCertificateManager
- 移除冗余的 TLS 证书管理相关测试用例
- 优化 TLS 上下文重载逻辑

Signed-off-by: Async <raisinata@foxmail.com>

* refactor(proxy): 优化日志信息内容

- 将 cert file changed 日志信息改为更通用的 File changed
- 保持代码风格一致性,提高日志的可读性和维护性

Signed-off-by: Async <raisinata@foxmail.com>

* test(proxy): 重构并增强 TlsCertificateManager 测试用例- 重新设计测试用例,使用临时文件模拟证书和密钥
- 增加对 TlsCertificateManager 各种方法的单元测试
- 涉及到的测试场景包括:
  - 构造函数
  - 启动和关闭
  - 注册和注销监听器
  - 文件变更通知(证书、密钥、未知文件等)
  - 多个监听器的情况
  - 监听器抛出异常的情况
  - 增加对内部 CertKeyFileWatchListener 的测试

Signed-off-by: Async <raisinata@foxmail.com>

* refactor

* test(proxy): 优化 TlsCertificateManager 单元测试

-移除了未使用的 import 语句
- 替换了 import static语句,使其更加有序
- 删除了未使用的静态方法断言(verify、times、never)
- 重置了 mock 对象以避免测试之间的干扰

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

* fix format

Signed-off-by: Async <raisinata@foxmail.com>

---------

Signed-off-by: Async <raisinata@foxmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Enhancement] Refactor SSL context loading process to support multiple protocols dynamic loading.
5 participants