diff --git a/docs/guides/database-validation.md b/docs/guides/database-validation.md index 2844db231..cc95c9362 100644 --- a/docs/guides/database-validation.md +++ b/docs/guides/database-validation.md @@ -1,6 +1,6 @@ # Database service acceptance -This record separates public-module service/failure checks, positive plugin-DSO workflows, server-observed request cancellation and owned-server termination. Each scope has its own tested revisions and environment; a passing result applies only to that scope. +This record separates public-module service/failure checks, positive plugin-DSO workflows, server-observed request cancellation, owned-server termination and verified Redis TLS. Each scope has its own tested revisions and environment; a passing result applies only to that scope. ## Public modules against real services @@ -136,6 +136,32 @@ bash tests/database/owned_server_fault_actor_guards.sh `check-database` compiles the opt-in program without executing its service modes or signaling servers. Compile-only CI success is not another real-service run. +## Real plugin Redis TLS + +The three Redis TLS modes of `ark_database_public_plugin_service_tests` passed once each on **2026-10-26** at ark `6cf58973c0aad587b6eb88cede768a9283eb2147` and CI arknet pin `a0dfe09cd543e75cfb5996e537ce2fa49f2a758f`. The host was Mac16,11 / Apple M4 Pro / 14 cores / 48 GiB, macOS 27.0.1 (26A434) arm64, Apple Clang 21.0.0, C++20 Debug with Boost.Asio and OpenSSL 3.6.4. The isolated Redis 7.4.2 runtime was built separately from the verified official source archive with `BUILD_TLS=yes`, using that OpenSSL toolchain. + +The private loopback instance required a password, disabled the plaintext port and persistence, and used a fresh CA/server certificate with only a `127.0.0.1` IP SAN. Client-certificate authentication was disabled; mutual TLS was not tested. OpenSSL controls confirmed the trusted chain/IP, the unrelated CA rejection and the `localhost` hostname mismatch before the DSO modes. + +| Mode | Observed result | +| --- | --- | +| `redis-tls` | Authenticated binary ECHO command, then an ordered binary/empty ECHO and PING pipeline; owning command and pipeline replies survive later operations and host destruction. Independent server counters increased by three ECHOs and one PING. | +| `redis-tls-untrusted-ca` | Native `client_errc::connection_failed` with an OpenSSL certificate-verification transport error, `command_may_have_run=false` and no replies. PING/ECHO counters were unchanged across its observation window. | +| `redis-tls-wrong-host` | Same certificate-verification rejection using the trusted CA and `localhost` against the same IP-only certificate; PING/ECHO counters were unchanged across its observation window. | + +Each C++ mode exited `0`. Configuration remains owned after the caller mutates its host/password/CA values. Every continuation and the once-only root completion use the owner thread/shared executor. Accepted work finishes under ordinary `manager.stop()/update()` natural drain, followed by native retirement and idempotent stop. Owning replies/errors remain readable after module/manager/runtime destruction and native DSO close. The controlled test bounds do not add a hard or preemptive production shutdown deadline. + +Independent `INFO commandstats` observations cover only PING/ECHO in these isolated windows; neither the client error nor those counters proves that no RESP command reached a real server. The existing bounded protocol-peer TLS regression separately counts received RESP commands. The owned Redis was shut down through authenticated `SHUTDOWN NOSAVE` and reaped with exit `0`; its exact process was independently confirmed absent, and private data, credentials and keys were removed. + +Use the [standalone C++ Redis TLS guide](../../tests/database/redis_tls.md) for the SSL-enabled build, private endpoints/CA/hostname controls and counter observations. The existing target is compiled by `check-database`; service execution remains opt-in: + +```sh +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-untrusted-ca +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-wrong-host +``` + +This record does not cover mutual TLS, certificate rotation/revocation or TLS cancellation, server faults and recovery. The earlier authenticated plain-TCP and other database records retain their original tested identities and scopes. + ## Acceptance boundaries -The DSO workflows exercise normal plugin teardown and native close calls. Successful `FreeLibrary` or `dlclose` does not prove that the operating system physically removed the library image. Request cancellation and server-termination acceptance are limited to the recorded operations on this host. Restart/recovery, persistence and write effects, transaction cancellation, Redis TLS, other cancellation/fault races, OS image removal, other platforms/deployment environments and cross-machine capacity remain unverified. These records do not establish arbitrary hot-update safety or production capacity. +The DSO workflows exercise normal plugin teardown and native close calls. Successful `FreeLibrary` or `dlclose` does not prove that the operating system physically removed the library image. Request cancellation and server-termination acceptance are limited to the recorded operations on this host. Restart/recovery, persistence and write effects, transaction cancellation, mutual TLS and certificate rotation/revocation, Redis TLS cancellation/fault recovery, other cancellation/fault races, OS image removal, other platforms/deployment environments and cross-machine capacity remain unverified. These records do not establish arbitrary hot-update safety or production capacity. diff --git a/docs/guides/database-validation_CN.md b/docs/guides/database-validation_CN.md index e3ea46cd3..239dd5aab 100644 --- a/docs/guides/database-validation_CN.md +++ b/docs/guides/database-validation_CN.md @@ -1,6 +1,6 @@ # 数据库服务验收 -本页分别记录公开模块的真实服务正常/故障检查、插件 DSO 正常流程、经服务端观察确认的请求取消,以及自有服务终止。每类检查保留各自的测试版本与环境;通过结果仅适用于对应范围。 +本页分别记录公开模块的真实服务正常/故障检查、插件 DSO 正常流程、经服务端观察确认的请求取消、自有服务终止,以及可信 Redis TLS。每类检查保留各自的测试版本与环境;通过结果仅适用于对应范围。 ## 公开模块与真实服务 @@ -136,6 +136,32 @@ bash tests/database/owned_server_fault_actor_guards.sh `check-database` 仅编译该可选程序,不执行服务模式,也不向服务发送信号。仅编译通过的 CI 不代表另一次真实服务运行。 +## 真实插件 Redis TLS + +**2026-10-26**,`ark_database_public_plugin_service_tests` 的三个 Redis TLS 模式各执行一次,均通过。实际执行的 ark 为 `6cf58973c0aad587b6eb88cede768a9283eb2147`,CI arknet 固定版本为 `a0dfe09cd543e75cfb5996e537ce2fa49f2a758f`。机器为 Mac16,11/Apple M4 Pro/14 核/48 GiB,系统为 macOS 27.0.1(26A434)arm64,构建采用 Apple Clang 21.0.0、C++20 Debug、Boost.Asio 与 OpenSSL 3.6.4。隔离 Redis 7.4.2 运行时由已验证的官方源码归档单独构建,使用 `BUILD_TLS=yes` 及该 OpenSSL 工具链。 + +私有回环实例要求密码认证,禁用明文端口与持久化,使用全新 CA/服务证书,证书仅包含 `127.0.0.1` 的 IP SAN。客户端证书认证已关闭,本次不验证双向 TLS。执行 DSO 模式前,OpenSSL 对照分别确认可信证书链/IP、不相关 CA 拒绝,以及 `localhost` 主机名不匹配。 + +| 模式 | 观察结果 | +| --- | --- | +| `redis-tls` | 通过认证执行二进制 ECHO 命令,再验证二进制/空 ECHO 与 PING 流水线顺序;拥有型命令与流水线回复在后续操作及宿主销毁后仍可读取。独立服务计数增加三次 ECHO 与一次 PING。 | +| `redis-tls-untrusted-ca` | 保留原生 `client_errc::connection_failed`、OpenSSL 证书校验传输错误、`command_may_have_run=false` 及空回复。该观察窗口内的 PING/ECHO 计数不变。 | +| `redis-tls-wrong-host` | 使用可信 CA 与 `localhost` 访问同一仅含 IP 的服务证书,保留相同证书校验拒绝;该观察窗口内的 PING/ECHO 计数不变。 | + +三个 C++ 模式的退出码均为 `0`。调用方修改 host/password/CA 后,配置副本仍生效。每次继续执行及仅完成一次的根任务均位于所属线程/共享执行器。已接收工作通过普通 `manager.stop()/update()` 自然排空,再完成原生服务退出和可重复的 stop。模块/管理器/运行时销毁及原生 DSO 关闭后,拥有型回复/错误仍可读取。受控测试时限不增加强制或抢占式生产停机期限。 + +独立 `INFO commandstats` 观察仅覆盖隔离窗口内的 PING/ECHO;客户端错误与这些计数均不证明真实服务未收到任何 RESP 命令。现有有界协议端点 TLS 回归另行统计接收的 RESP 命令。自有 Redis 已通过认证的 `SHUTDOWN NOSAVE` 正常关闭并以退出码 `0` 回收;精确进程已独立确认不存在,私有数据、凭据与密钥已删除。 + +按[独立 C++ Redis TLS 指南](../../tests/database/redis_tls_CN.md)准备启用 SSL 的构建、私有端点/CA/主机名对照及计数观察。`check-database` 编译现有目标,但真实服务模式仍需显式执行: + +```sh +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-untrusted-ca +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-wrong-host +``` + +本记录不覆盖双向 TLS、证书轮换/撤销,以及 TLS 请求取消、服务故障与恢复。较早的认证明文 TCP 和其他数据库记录保留原有源码标识及验收范围。 + ## 验收边界 -DSO 流程覆盖正常插件销毁与原生关闭调用。`FreeLibrary` 或 `dlclose` 成功不证明操作系统已物理移除库映像。本机请求取消与服务端终止验收仅覆盖记录中的操作。重启恢复、持久化与写入效果、事务取消、Redis TLS、其他取消/故障竞争、操作系统库映像移除、其他平台/部署环境与跨机器容量仍未验收。这些记录均不证明任意热更新安全或生产容量。 +DSO 流程覆盖正常插件销毁与原生关闭调用。`FreeLibrary` 或 `dlclose` 成功不证明操作系统已物理移除库映像。本机请求取消与服务端终止验收仅覆盖记录中的操作。重启恢复、持久化与写入效果、事务取消、双向 TLS 与证书轮换/撤销、Redis TLS 取消/故障恢复、其他取消/故障竞争、操作系统库映像移除、其他平台/部署环境与跨机器容量仍未验收。这些记录均不证明任意热更新安全或生产容量。 diff --git a/docs/project/roadmap.md b/docs/project/roadmap.md index e082873b8..01fe118a7 100644 --- a/docs/project/roadmap.md +++ b/docs/project/roadmap.md @@ -8,13 +8,13 @@ This page distinguishes supported source behavior from remaining design and depl - Public [MySQL](../reference/mysql.md) and [Redis](../reference/redis.md) modules use arknet native clients through Asio coroutine entry points. Inputs and results own their storage; manager-owned task groups resume on the module owner thread. MySQL includes prepared statements and transactions; Redis includes commands and pipelines. Legacy database drivers are no longer the public plugin interface. - Startup callbacks propagate the first failure. The launcher reports the failed phase and exits with code `1` before the update loop. Shutdown stops managed task admission, drains accepted tasks and waits for module readiness. A configured cooperative wait timeout retains the process/resources and disables unloading. These behaviors do not provide partial-startup rollback or a hard process-stop deadline. See [application startup and shutdown](../guides/applications.md) and [managed tasks](../guides/tasks.md). -Public database service/failure checks, [real-plugin DSO positive workflows](../guides/database-validation.md#real-plugin-dsos-against-real-services), [bounded request cancellation](../guides/database-validation.md#real-plugin-request-cancellation) and [observed read-only server termination](../guides/database-validation.md#real-plugin-server-termination) passed against isolated Redis and MySQL servers on macOS. The [database service acceptance record](../guides/database-validation.md) keeps their distinct tested revisions, environments and scope; those results do not cover every deployment or source revision. +Public database service/failure checks, [real-plugin DSO positive workflows](../guides/database-validation.md#real-plugin-dsos-against-real-services), [bounded request cancellation](../guides/database-validation.md#real-plugin-request-cancellation), [observed read-only server termination](../guides/database-validation.md#real-plugin-server-termination) and [Redis TLS command/pipeline and certificate rejection](../guides/database-validation.md#real-plugin-redis-tls) passed against isolated Redis and MySQL servers on macOS. The [database service acceptance record](../guides/database-validation.md) keeps their distinct tested revisions, environments and scope; those results do not cover every deployment or source revision. ## Remaining acceptance and design | Area | Remaining work and acceptance boundary | | --- | --- | -| Database deployment | Restart/recovery, persistence and write effects, transaction cancellation, Redis TLS, other cancellation/fault races, other platforms and OS library image removal still need acceptance. Add target-environment and cross-machine capacity evidence before making deployment or capacity claims. Local protocol endpoints and construction checks do not replace these runs. | +| Database deployment | Restart/recovery, persistence and write effects, transaction cancellation, mutual TLS/certificate rotation, Redis TLS cancellation/fault recovery, other cancellation/fault races, other platforms and OS library image removal still need acceptance. Add target-environment and cross-machine capacity evidence before making deployment or capacity claims. Local protocol endpoints and construction checks do not replace these runs. | | Startup and shutdown | Partial-startup rollback is not provided. A common dependency-aware shutdown protocol remains design work; managed task drain alone does not prove that arbitrary callbacks, native workers or background cleanup have ended. Define ownership and admission phases before extending unload guarantees. | | Optional plugins | Kafka construction/export checks do not test broker delivery, flush behavior or service interoperability; its driver build needs independent platform acceptance. OSS SDK remains a scaffold without client/request functionality. Physic retains the explicit unsupported legacy PhysX 3 configuration failure. See [optional plugin boundaries](../reference/directory-structure.md). | | C++20 modernization | Review each remaining module and optional integration against its own API, build and runtime requirements. The language baseline, header layout and focused construction checks do not establish completion of the whole modernization. | diff --git a/docs/project/roadmap_CN.md b/docs/project/roadmap_CN.md index 9226ed471..acaf3eac7 100644 --- a/docs/project/roadmap_CN.md +++ b/docs/project/roadmap_CN.md @@ -8,13 +8,13 @@ - 公开 [MySQL](../reference/mysql_CN.md) 与 [Redis](../reference/redis_CN.md) 模块通过 Asio 协程入口使用 arknet 原生客户端。输入与结果拥有自身存储;管理器拥有的任务组在模块所属线程恢复。MySQL 提供预处理语句与事务,Redis 提供命令与流水线。旧数据库驱动已不再作为公开插件接口。 - 启动回调的首个失败会向上传递。启动器报告失败阶段,在进入更新循环前以退出码 `1` 终止。停止时关闭托管任务接收,排空已接收任务并等待模块就绪。配置的协作式等待超时会保留进程和资源,并禁止卸载。这些行为不提供部分启动回滚,也不构成强制进程退出期限。见[应用启动与停止](../guides/applications_CN.md)和[托管任务](../guides/tasks_CN.md)。 -公开数据库接口的正常/故障检查、[真实插件 DSO 正常流程](../guides/database-validation_CN.md#真实插件动态库与真实服务)、[有界请求取消](../guides/database-validation_CN.md#真实插件请求取消),以及[经观察确认的只读请求服务终止](../guides/database-validation_CN.md#真实插件服务端终止),已在 macOS 上通过隔离 Redis 与 MySQL 真实服务验收。[数据库服务验收记录](../guides/database-validation_CN.md)分别保留各自的测试版本、环境与范围;这些结果不覆盖所有部署环境或源码版本。 +公开数据库接口的正常/故障检查、[真实插件 DSO 正常流程](../guides/database-validation_CN.md#真实插件动态库与真实服务)、[有界请求取消](../guides/database-validation_CN.md#真实插件请求取消),[经观察确认的只读请求服务终止](../guides/database-validation_CN.md#真实插件服务端终止),以及 [Redis TLS 命令/流水线与证书拒绝](../guides/database-validation_CN.md#真实插件-redis-tls),已在 macOS 上通过隔离 Redis 与 MySQL 真实服务验收。[数据库服务验收记录](../guides/database-validation_CN.md)分别保留各自的测试版本、环境与范围;这些结果不覆盖所有部署环境或源码版本。 ## 剩余验收与设计 | 领域 | 剩余工作与验收边界 | | --- | --- | -| 数据库部署 | 重启恢复、持久化与写入效果、事务取消、Redis TLS、其他取消/故障竞争、其他平台与操作系统库映像移除仍需验收。发布部署或容量结论前,补充目标环境与跨机器容量证据。本机协议端点与构造检查不能替代这些验收。 | +| 数据库部署 | 重启恢复、持久化与写入效果、事务取消、双向 TLS/证书轮换、Redis TLS 取消/故障恢复、其他取消/故障竞争、其他平台与操作系统库映像移除仍需验收。发布部署或容量结论前,补充目标环境与跨机器容量证据。本机协议端点与构造检查不能替代这些验收。 | | 启动与停机 | 框架不提供部分启动回滚。通用的依赖感知停机协议仍需设计;托管任务排空不代表任意回调、原生工作线程或后台清理均已结束。扩展卸载保证前,先明确所有权与接收阶段。 | | 可选插件 | Kafka 构造/导出检查不验证 broker 投递、flush 行为或服务互通;驱动构建仍需独立平台验收。OSS SDK 保持骨架状态,不提供客户端/请求功能。Physic 保留旧版 PhysX 3 不受支持的显式配置失败。见[可选插件边界](../reference/directory-structure_CN.md)。 | | C++20 改造 | 按各模块与可选集成的 API、构建和运行要求继续审查。语言基线、头文件布局和局部构造检查不足以证明全部改造完成。 | diff --git a/tests/database/README.md b/tests/database/README.md index 87f13f2dd..3f0cc2b72 100644 --- a/tests/database/README.md +++ b/tests/database/README.md @@ -288,6 +288,8 @@ text. Building it and checking those failures do not establish successful real-service DSO acceptance; that requires explicitly running each selected mode against isolated services. +The additional opt-in `redis-tls`, `redis-tls-untrusted-ca` and `redis-tls-wrong-host` modes use a real authenticated TLS service. See the [Redis TLS DSO guide](redis_tls.md) for trusted-IP/hostname controls, private service requirements and independent PING/ECHO counter observation. They are compiled but not run by `check-database`. + ## Real plugin request cancellation `ark_database_public_plugin_cancellation_tests` is a separate opt-in runner for real Redis/MySQL plugin DSOs and owned services. It observes tagged server work before `task_group::cancel(id)`, retains native cancellation/unknown results, and checks owner-thread completion and natural retirement. The evidence covers the observed IDs, effects and bounded observation window; MySQL's final zero-or-one counter is not proof of rollback or every possible retry race. `check-database` compiles this runner without starting services. See the [cancellation guide](cancellation.md) for build commands, trusted TLS, exclusive write data and precise limits. diff --git a/tests/database/README_CN.md b/tests/database/README_CN.md index 52c73dcb5..ae302f999 100644 --- a/tests/database/README_CN.md +++ b/tests/database/README_CN.md @@ -227,6 +227,8 @@ MySQL 检查有符号整数、文本、NULL 查询字段,二进制预处理结 程序不启动服务,也不打印凭据或原生服务端文本。编译与这些失败检查不能证明真实服务 DSO 正向验收通过;需要显式选择模式,在隔离服务上实际运行。 +新增可选 `redis-tls`、`redis-tls-untrusted-ca` 与 `redis-tls-wrong-host` 模式使用真实认证 TLS 服务。[Redis TLS DSO 指南](redis_tls_CN.md)说明可信 IP/主机名对照、私有服务要求及独立 PING/ECHO 计数观察。`check-database` 编译但不执行这些模式。 + ## 真实插件请求取消 独立的 `ark_database_public_plugin_cancellation_tests` 手动启用模式使用真实 Redis/MySQL 插件 DSO 与自有服务,先观察带标记的服务端工作,再调用 `task_group::cancel(id)`,保留原生取消/未知结果,并检查所属线程完成和自然退出。证据仅覆盖观察到的 ID、影响及有限观察窗口;MySQL 最终计数为零或一,不证明回滚,也不覆盖全部重试竞争。`check-database` 只编译该程序,不启动服务。构建命令、可信 TLS、独占写入数据与准确边界见[取消验收指南](cancellation_CN.md)。 diff --git a/tests/database/public_plugin_service.cpp b/tests/database/public_plugin_service.cpp index a98f0977f..444765aa9 100644 --- a/tests/database/public_plugin_service.cpp +++ b/tests/database/public_plugin_service.cpp @@ -15,6 +15,11 @@ #include #include +#if defined(ARKNET_ENABLE_SSL) +#include +#include +#endif + #include "ark/plugin_manager.hpp" #include "ark/redis/redis_module.hpp" #include "ark/task.hpp" @@ -189,6 +194,89 @@ void check_redis() { } #if defined(ARKNET_ENABLE_SSL) +ark::task<> redis_tls_workflow(plugin_host& host, ark::redis_module& database, std::string binary, + std::optional& command, std::optional& pipeline) { + arknet::redis::command echo{"ECHO", binary}; + command = co_await database.command("service", std::move(echo), 5s); + host.check_executor(); + require(bool(*command) && command->replies.size() == 1 && command->replies[0].text == binary, + "Redis TLS DSO command lost its owning binary result"); + std::vector commands{{"ECHO", binary}, {"ECHO", ""}, {"PING"}}; + pipeline = co_await database.pipeline("service", std::move(commands), 5s); + host.check_executor(); + require(bool(*pipeline) && pipeline->replies.size() == 3 && pipeline->replies[0].text == binary && + pipeline->replies[1].text.empty() && pipeline->replies[2].text == "PONG" && + command->replies[0].text == binary, + "Redis TLS DSO pipeline lost ordered owning results or invalidated the command result"); +} + +bool certificate_verification_failed(const arknet::redis::result& result) { + return result.error == arknet::redis::client_errc::connection_failed && + result.transport_error.category() == asio::error::get_ssl_category() && + ERR_GET_REASON(static_cast(result.transport_error.value())) == + SSL_R_CERTIFICATE_VERIFY_FAILED && + !result.command_may_have_run && result.replies.empty(); +} + +ark::task<> redis_tls_rejection_workflow( + plugin_host& host, ark::redis_module& database, std::optional& retained) { + arknet::redis::command ping{"PING"}; + retained = co_await database.command("service", std::move(ping), 5s); + host.check_executor(); + require(certificate_verification_failed(*retained), + "Redis TLS DSO did not preserve certificate verification rejection before command submission"); +} + +void check_redis_tls(std::string_view mode) { + ark::redis_options options; + options.tls = true; + options.pool.connection.host = + environment(mode == "redis-tls-wrong-host" ? "ARK_DATABASE_REDIS_WRONG_HOST" : "ARK_DATABASE_REDIS_HOST"); + options.pool.connection.port = std::to_string(port("ARK_DATABASE_REDIS_PORT")); + options.pool.connection.password = environment("ARK_DATABASE_REDIS_PASSWORD"); + if (const auto* user = std::getenv("ARK_DATABASE_REDIS_USER")) + { + options.pool.connection.username = user; + } + options.pool.certificate_authority = + environment(mode == "redis-tls-untrusted-ca" ? "ARK_DATABASE_REDIS_UNTRUSTED_CA" : "ARK_DATABASE_REDIS_CA"); + options.pool.max_connections = 1; + options.pool.client_configuration.connect_timeout = 3s; + const std::string binary("tls\0redis\r\nvalue", 16); + std::optional command, pipeline, rejected; + { + plugin_host host("ark_database_redis_plugin"); + auto* database = host.manager.find_module(); + require(database != nullptr, "Real Redis TLS DSO did not register its public module"); + require(database->configure("service", options), "Redis TLS DSO configuration failed"); + options.pool.connection.host = "invalid.invalid"; + options.pool.connection.password = "changed"; + options.pool.certificate_authority = "missing-certificate.pem"; + if (mode == "redis-tls") + { + host.run(redis_tls_workflow(host, *database, binary, command, pipeline)); + } + else + { + host.run(redis_tls_rejection_workflow(host, *database, rejected)); + } + } + if (mode == "redis-tls") + { + require(command && pipeline && bool(*command) && bool(*pipeline) && command->replies[0].text == binary && + pipeline->replies[0].text == binary && pipeline->replies[1].text.empty() && + pipeline->replies[2].text == "PONG", + "Redis TLS owning command/pipeline results did not survive host destruction"); + } + else + { + require(rejected && certificate_verification_failed(*rejected), + "Redis TLS owning verification error did not survive host destruction"); + std::cout << "Native connection_failed / TLS certificate verification failed; command_may_have_run=false; " + "replies=0\n"; + } +} + ark::task<> mysql_workflow(plugin_host& host, ark::mysql_module& database, std::string binary, std::optional& text, std::optional& prepared) { text = co_await database.query("service", "SELECT -7, 'owned text', NULL", 5s); @@ -262,13 +350,18 @@ void check_mysql() { int main(int argc, char** argv) { try { - require(argc == 2, "Usage: ark_database_public_plugin_service_tests redis|mysql"); + require(argc == 2, "Usage: ark_database_public_plugin_service_tests " + "redis|mysql|redis-tls|redis-tls-untrusted-ca|redis-tls-wrong-host"); const std::string_view protocol(argv[1]); if (protocol == "redis") { check_redis(); } #if defined(ARKNET_ENABLE_SSL) + else if (protocol == "redis-tls" || protocol == "redis-tls-untrusted-ca" || protocol == "redis-tls-wrong-host") + { + check_redis_tls(protocol); + } else if (protocol == "mysql") { check_mysql(); diff --git a/tests/database/redis_tls.md b/tests/database/redis_tls.md new file mode 100644 index 000000000..7ccdfb71d --- /dev/null +++ b/tests/database/redis_tls.md @@ -0,0 +1,45 @@ +# Real Redis TLS plugin acceptance + +[简体中文](redis_tls_CN.md) + +This opt-in standalone C++ program loads the actual hidden-symbol Redis plugin DSO through `plugin_manager`, uses only public Redis/manager/task interfaces, and makes no key writes. It never starts or stops a server. `check-database` compiles the runner without executing these real-service modes; the existing `redis_service_tls.cpp` protocol-peer tests remain a separate automatically run regression. + +## Build and owned-service requirements + +Use the [database build](README.md#build) with `ARK_BUILD_DATABASE_TESTS=ON`, `ARKNET_ENABLE_SSL=ON` and the pinned local arknet source. Then build: + +```sh +cmake --build database-build --target ark_database_public_plugin_service_tests --parallel 1 +``` + +Prepare an exclusively owned disposable Redis instance with a fresh private data directory, a random loopback TLS port, authentication, persistence disabled and the plaintext port disabled (`port 0`). Build Redis with `BUILD_TLS=yes` and the same available OpenSSL toolchain. Do not change shared or installed services. Keep credentials, private keys and service configuration private; pass the password through the environment, never command arguments or logs. Verify the child PID, executable, private directory, PID file and PID-specific listener before use and before authenticated `SHUTDOWN NOSAVE`; reap the child and remove private data and secrets on every outcome. + +For the hostname control, issue the server certificate from the test CA with an **IP SAN for `127.0.0.1` only**, without a `localhost` DNS SAN or matching common name. Confirm its chain and IP with OpenSSL before the tests. `localhost` must resolve to that same loopback server; an ordinary connection refusal does not pass the rejection mode. Prepare a second valid but unrelated CA certificate for the untrusted-CA mode. Client certificate authentication is not tested (`tls-auth-clients no`); password authentication remains required over verified TLS. + +Set these variables in the private test environment: + +| Variable | Meaning | +| --- | --- | +| `ARK_DATABASE_REDIS_HOST` | `127.0.0.1`, matching the server certificate IP SAN | +| `ARK_DATABASE_REDIS_PORT` | Owned instance's TLS port | +| `ARK_DATABASE_REDIS_PASSWORD` | Required nonempty password | +| `ARK_DATABASE_REDIS_USER` | Optional ACL username | +| `ARK_DATABASE_REDIS_CA` | Trusted CA PEM file | +| `ARK_DATABASE_REDIS_UNTRUSTED_CA` | Valid unrelated CA PEM file | +| `ARK_DATABASE_REDIS_WRONG_HOST` | `localhost`, resolving to the same server but absent from its certificate | + +```sh +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-untrusted-ca +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-wrong-host +``` + +## Assertions and observation limits + +The positive mode requires an authenticated TLS command with an owning binary ECHO reply, followed by an ordered binary/empty ECHO and PING pipeline. Configuration is copied before the caller mutates its host/password/CA values; the earlier command result must remain readable after the pipeline. Both owning results remain readable after module/manager/runtime destruction and native DSO close. + +Each rejection mode attempts PING but requires native `client_errc::connection_failed`, an OpenSSL certificate-verification transport error, `command_may_have_run=false` and no replies. Connection refusal, timeout, SQL/Redis server errors or a coroutine exception cannot pass. The owning error is checked again after teardown. The host's task completes once on the owner thread/shared executor; normal `manager.stop()/update()` drains accepted work, retires native services and supports repeated stop. A test observation bound does not make cooperative stop a hard or preemptive production deadline. + +For independent business-command observation, take authenticated `redis-cli --tls --cacert` `INFO commandstats` snapshots immediately before and after each rejection, without issuing PING/ECHO or running other clients inside that window. Compare `cmdstat_ping` and `cmdstat_echo` call counts; INFO itself changes other statistics. Unchanged counts support only the observed PING/ECHO window. The C++ runner does not read server counters and does not by itself prove that no RESP command reached a real server. The bounded protocol-peer regression separately counts all received RESP commands. + +These modes do not verify mutual TLS, certificate rotation/revocation, TLS cancellation/server faults/recovery, persistence, cross-machine capacity or other platforms. Successful native close does not prove physical OS library image removal or arbitrary hot-update safety. diff --git a/tests/database/redis_tls_CN.md b/tests/database/redis_tls_CN.md new file mode 100644 index 000000000..acd9114c0 --- /dev/null +++ b/tests/database/redis_tls_CN.md @@ -0,0 +1,45 @@ +# 真实 Redis TLS 插件验收 + +[English](redis_tls.md) + +该可选独立 C++ 程序通过 `plugin_manager` 加载真实隐藏符号 Redis 插件 DSO,仅调用公开 Redis/管理器/任务接口,不写入键。程序不启动或停止服务。`check-database` 编译程序但不执行这些真实服务模式;现有 `redis_service_tls.cpp` 协议端点测试仍作为独立回归自动运行。 + +## 构建与自有服务要求 + +按[数据库构建指南](README_CN.md#构建)启用 `ARK_BUILD_DATABASE_TESTS=ON`、`ARKNET_ENABLE_SSL=ON`,使用固定版本的本地 arknet 源码,然后构建: + +```sh +cmake --build database-build --target ark_database_public_plugin_service_tests --parallel 1 +``` + +准备独占的一次性 Redis 实例:使用全新私有数据目录、随机回环 TLS 端口与认证,禁用持久化及明文端口(`port 0`)。使用现有 OpenSSL 工具链,以 `BUILD_TLS=yes` 构建 Redis。不修改共享或已安装的服务。凭据、私钥及服务配置保存在私有位置;密码仅通过环境传入,不放入参数或日志。使用前及认证执行 `SHUTDOWN NOSAVE` 前,核对自有子进程的 PID、可执行文件、私有目录、PID 文件及该 PID 的监听;所有结果下均应回收子进程并删除私有数据与秘密。 + +为主机名对照,由测试 CA 签发服务证书,**仅包含 `127.0.0.1` 的 IP SAN**,不包含 `localhost` 的 DNS SAN 或匹配的通用名称。测试前通过 OpenSSL 确认证书链与 IP。`localhost` 必须解析到同一回环服务;普通连接拒绝不能使该模式通过。另备一张有效但不相关的 CA 证书,用于不可信 CA 模式。本次不验证客户端证书认证(`tls-auth-clients no`),可信 TLS 上仍要求密码认证。 + +在私有测试环境中设置: + +| 变量 | 含义 | +| --- | --- | +| `ARK_DATABASE_REDIS_HOST` | `127.0.0.1`,与服务证书 IP SAN 匹配 | +| `ARK_DATABASE_REDIS_PORT` | 自有实例的 TLS 端口 | +| `ARK_DATABASE_REDIS_PASSWORD` | 必填非空密码 | +| `ARK_DATABASE_REDIS_USER` | 可选 ACL 用户名 | +| `ARK_DATABASE_REDIS_CA` | 可信 CA PEM 文件 | +| `ARK_DATABASE_REDIS_UNTRUSTED_CA` | 有效但不相关的 CA PEM 文件 | +| `ARK_DATABASE_REDIS_WRONG_HOST` | `localhost`,解析到同一服务,但不在证书中 | + +```sh +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-untrusted-ca +./database-build/tests/database/ark_database_public_plugin_service_tests redis-tls-wrong-host +``` + +## 断言与观察边界 + +正常模式通过认证 TLS 执行 ECHO 命令,保留拥有型二进制回复,再验证二进制/空 ECHO 与 PING 流水线的回复顺序。调用方修改 host/password/CA 后,配置副本仍生效;较早的命令结果在后续流水线完成后仍可读取。模块/管理器/运行时销毁及原生 DSO 关闭后,两份拥有型结果仍可读取。 + +两个拒绝模式尝试 PING,但必须保留原生 `client_errc::connection_failed`、OpenSSL 证书校验传输错误、`command_may_have_run=false` 及空回复。连接拒绝、超时、SQL/Redis 服务错误或协程异常均不能作为通过结果。销毁后再次检查拥有型错误。宿主任务在所属线程/共享执行器上完成一次;普通 `manager.stop()/update()` 排空已接收工作、完成原生服务退出,并支持重复 stop。测试观察时限不使协作式 stop 成为强制或抢占式生产期限。 + +独立观察业务命令时,在每个拒绝模式执行前后,立即通过认证的 `redis-cli --tls --cacert` 获取 `INFO commandstats`;该窗口内不发送 PING/ECHO,也不运行其他客户端。比较 `cmdstat_ping` 与 `cmdstat_echo` 的调用数;INFO 自身会改变其他统计。计数不变仅支持该 PING/ECHO 观察窗口。C++ 程序不读取服务计数,单独运行不能证明真实服务未收到任何 RESP 命令;有界协议端点回归另行统计所有接收到的 RESP 命令。 + +这些模式不验证双向 TLS、证书轮换/撤销、TLS 取消/服务故障/恢复、持久化、跨机器容量或其他平台。原生关闭成功不证明操作系统已物理移除库映像,也不证明任意热更新安全。