Skip to content

<fix>[kvm]: backport TLS reconnect PID fix#4465

Closed
MatheMatrix wants to merge 8 commits into
5.4.10from
sync/yingzhe.hu/cherry-pick/ZSTAC-84446-5.4.10-full
Closed

<fix>[kvm]: backport TLS reconnect PID fix#4465
MatheMatrix wants to merge 8 commits into
5.4.10from
sync/yingzhe.hu/cherry-pick/ZSTAC-84446-5.4.10-full

Conversation

@MatheMatrix

Copy link
Copy Markdown
Owner

Summary

Backport the ZSTAC-84446 TLS reconnect PID fix chain to 5.4.10.

  • Unify TLS cert IP collection/check/deploy behavior to avoid reconnect-triggered full ansible redeploy.
  • Decouple TLS cert mismatch detection from the reconnect libvirtd restart toggle so normal reconnect keeps the kvmagent PID stable.
  • Exclude br_conn_all_ns host-internal IP from TLS SAN expectations.
  • Include the required libvirt TLS prerequisite commits because 5.4.10 does not yet have the TLS config/deploy/migration test foundation used by this fix.
  • Adjust 5.4.10 gray version metadata for the backport.

Validation

  • git diff --check upstream/5.4.10..HEAD
  • mvn compile -pl plugin/kvm -am -Dmaven.test.skip
  • mvn install -pl plugin/kvm -am -Dmaven.test.skip
  • mvn -f test/pom.xml test -Dtest=org.zstack.test.kvm.KVMHostUtilsTest -DfailIfNoTests=false -DskipJacoco=true

Resolves: ZSTAC-84446

sync from gitlab !10427

huhu0316 added 8 commits July 6, 2026 17:09
Add libvirt.tls.enabled GlobalConfig and useTls field in MigrateVmCmd to support TLS-encrypted libvirt connections for migration and V2V.

Resolves: ZSTAC-81343

Change-Id: I391fa36c0dd63c25c5d85d102bc3579c8eb3d685
(cherry picked from commit 3feb9e9)
Resolves: ZSTAC-83696

Change-Id: I368cc5af8fb3d553bedc3be5d031015719e68ddc
(cherry picked from commit c85fab3)
…TTP for TLS cert update

Resolves: ZSTAC-83696

Change-Id: I4a5f404e51f12487b61bdf8f990bbc490cafeeee
(cherry picked from commit 659c296)
… redeploy

Resolves: ZSTAC-84446

Change-Id: I8af5e3887a5bad286b43dda00c874c9de999e1cb
(cherry picked from commit 2697ef2)
Resolves: ZSTAC-84446

Change-Id: I9bed31c0cefddd6ed11f59cd13e36eb1c2abc029
(cherry picked from commit ad8a4c5)
br_conn_all_ns (169.254.64.1, mevoco.py CONNECT_ALL_NETNS_BR_OUTER_IP) is
created on the host only when the first flat L3 network lands, AFTER the
add-host ansible playbook has generated the TLS cert. The cert SAN
therefore lacks this IP, and on every subsequent reconnect the
check-tls-certs flow detects the gap and triggers an ansible redeploy
plus libvirtd/kvmagent restart, breaking PID stability.

This IP is host-internal (used only for VM <-> root-netns userdata /
pushgateway / kvmagent HTTP) and has no business in a libvirtd TLS cert.
Add EXCLUDED_INTERNAL_IPS as a single source of truth used by both the
check flow (buildIpList) and the deploy flow (unionTlsCertIps) so the two
flows can never disagree, regardless of when the bridge appears.
host_plugin.fact() on the agent side is unchanged.

Resolves: ZSTAC-84446

Change-Id: I10821d8c68190f2cbc8a0679d19ed053916d9184
(cherry picked from commit a544065)
Adjust the TLS migration command gray version for the 5.4.10 backport so the new fields are sent to 5.4.x agents.

Resolves: ZSTAC-84446

Change-Id: I0ca3210bd735649bf9d6b8e38e5ddee4f4cd63cb
Normalize the cherry-picked Groovy test to LF so git diff --check passes before creating the 4.8.38 backport MR.

Resolves: ZSTAC-81344

Change-Id: I2ba5c110e21cf613c209a9ac49883b843fc26514
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Could not fetch remote config from http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml: TimeoutError: The operation was aborted due to timeout
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

本次变更为KVM主机迁移引入Libvirt TLS支持:新增全局配置开关、迁移命令的TLS与源主机管理IP字段、部署参数中的证书IP字段、连接流程中的证书检测与部署装配逻辑、宿主机IP采集/合并工具方法、TLS CA初始化流程,并补充对应的单元测试与集成测试。

Changes

Libvirt TLS迁移支持

Layer / File(s) Summary
迁移命令与部署参数TLS字段
KVMAgentCommands.java, KVMGlobalConfig.java, KVMHostDeployArguments.java, KVMConstant.java
新增MigrateVmCmd的useTls/srcHostManagementIp字段及访问器,新增LIBVIRT_TLS_ENABLED全局配置,新增KVMHostDeployArguments的tlsCertIps字段及访问器。
主机IP采集与合并工具方法
KVMHostUtils.java
新增EXCLUDED_INTERNAL_IPS常量、collectHostIps、unionTlsCertIps、unionIps、filterIps、buildIpList、shouldForceTlsRedeploy及newSsh方法。
连接流程TLS证书检测与部署装配
KVMHost.java
连接流程新增SAN证书检测步骤,ansible部署阶段设置tlsCertIps与强制重跑标记,迁移命令设置useTls与源主机管理IP,新增parseSanIps工具方法。
Libvirt TLS CA初始化
KVMHostFactory.java
start()流程新增initLibvirtTlsCA(),生成/读取本地CA证书与私钥并通过JsonLabel持久化到数据库。
单元测试与集成测试
LibvirtTlsMigrateCase.groovy, KVMHostUtilsTest.java
新增集成测试验证TLS开关与迁移命令传播、全局配置校验、SAN解析;新增单元测试覆盖buildIpList、unionIps与shouldForceTlsRedeploy的多种场景。

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant KVMHost
  participant SrcHost as 源宿主机(SSH)
  participant KVMHostUtils
  participant DeployArguments as KVMHostDeployArguments
  participant MigrateVmCmd

  KVMHost->>SrcHost: openssl x509 读取servercert.pem SAN
  SrcHost-->>KVMHost: subjectAltName输出
  KVMHost->>KVMHost: parseSanIps(输出)
  KVMHost->>KVMHostUtils: collectHostIps(hostUuid, managementIp)
  KVMHostUtils-->>KVMHost: 宿主IP列表
  KVMHost->>KVMHost: 比对SAN与宿主IP,标记NEED_DEPLOY_TLS_CERT/TLS_DETECTED_IPS
  KVMHost->>KVMHostUtils: unionTlsCertIps(检测IP,管理IP)
  KVMHostUtils-->>KVMHost: tlsCertIps
  KVMHost->>DeployArguments: setTlsCertIps(tlsCertIps)
  KVMHost->>KVMHostUtils: shouldForceTlsRedeploy(...)
  KVMHostUtils-->>KVMHost: 是否强制重跑
  KVMHost->>MigrateVmCmd: setUseTls(...), setSrcHostManagementIp(...)
Loading

Poem

兔子敲键盘,证书亮闪闪,
TLS护迁移,IP排排站,
SAN比对不慌乱,CA落库真周全,
测试用例齐上阵,代码稳稳向前跳~ 🐇🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了本次 KVM TLS reconnect PID 修复回移的核心变更。
Description check ✅ Passed 描述与变更集一致,明确说明了回移目标、TLS 证书处理和验证步骤。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/yingzhe.hu/cherry-pick/ZSTAC-84446-5.4.10-full

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.1)
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java

ast-grep timed out on this file

plugin/kvm/src/main/java/org/zstack/kvm/KVMHostDeployArguments.java

ast-grep retry budget exhausted before isolating this batch

plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java

ast-grep retry budget exhausted before isolating this batch

  • 2 others

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
test/src/test/java/org/zstack/test/kvm/KVMHostUtilsTest.java (2)

89-99: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

zstac84446_checkAndFirstDeployUseSameSource 未真正验证 check/deploy 一致性。

该测试两次调用同一个 buildIpList 方法且参数完全相同,属于同义反复(tautology),必然相等,并未验证 PR 目标中 "check-flow 与 deploy-flow IP 列表保持一致" 的真实场景(即 buildIpList 的检测结果与 unionIps 的部署结果是否一致)。建议改为分别调用 buildIpList(模拟 check 流程)与 unionIps(模拟 deploy 流程,将 buildIpList 结果作为 detectedIpsCsv 传入)并断言两者等价,才能覆盖这条关键的一致性契约。

♻️ 建议的修复方向
-        String firstDeploy = KVMHostUtils.buildIpList("172.24.250.175", out, null);
-        String onCheck = KVMHostUtils.buildIpList("172.24.250.175", out, null);
-        Assert.assertEquals(firstDeploy, onCheck);
+        String onCheck = KVMHostUtils.buildIpList("172.24.250.175", out, null);
+        String onDeploy = KVMHostUtils.unionIps(onCheck, "172.24.250.175", null, null,
+                KVMHostUtils.EXCLUDED_INTERNAL_IPS);
+        Assert.assertEquals(onCheck, onDeploy);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/src/test/java/org/zstack/test/kvm/KVMHostUtilsTest.java` around lines 89
- 99, zstac84446_checkAndFirstDeployUseSameSource is currently a tautology
because it calls buildIpList twice with identical inputs, so it does not verify
check-flow versus deploy-flow consistency. Update the test to compare the check
result from KVMHostUtils.buildIpList against the deploy result produced by
KVMHostUtils.unionIps, passing the detected IP list from buildIpList into
unionIps as the detectedIpsCsv input, and assert the two outputs are equivalent.
Keep the existing assertions that filter expected and excluded IPs, but make
sure the test exercises both buildIpList and unionIps to validate the real
contract.

28-206: 📐 Maintainability & Code Quality | 🔵 Trivial

测试方法命名未遵循 lowerCamelCase 规范。

新增的多个测试方法(如 filtersZsSuffixIface_consistentWithHostFactzstac84446_checkAndFirstDeployUseSameSourceunionIps_detectedIsBaseAndExtraAppendedshouldForceTlsRedeploy_noNeedNeverForces 等)使用下划线分隔而非纯 lowerCamelCase。该文件中已有的旧方法(如 zstac86349_...)也是类似风格,属于沿用既有约定,非本次新引入的孤立问题,故仅作为可选建议。

As per path instructions, "方法名、参数名、成员变量和局部变量:使用 lowerCamelCase 风格。"

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/src/test/java/org/zstack/test/kvm/KVMHostUtilsTest.java` around lines 28
- 206, The newly added test methods in KVMHostUtilsTest use underscore-separated
names instead of lowerCamelCase. Rename the affected tests to follow the project
naming convention, updating the method identifiers such as
filtersZsSuffixIface_consistentWithHostFact,
zstac84446_checkAndFirstDeployUseSameSource,
unionIps_detectedIsBaseAndExtraAppended, and
shouldForceTlsRedeploy_noNeedNeverForces to pure lowerCamelCase while keeping
the test behavior unchanged.

Source: Path instructions

plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java (1)

497-506: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

使用已弃用的 FileUtils 无 charset 重载。

FileUtils.readFileToString(File)FileUtils.writeStringToFile(File, String) 在 commons-io 中均已弃用,且依赖平台默认字符集,跨环境可能产生编码不确定性。建议显式传入 StandardCharsets.UTF_8

♻️ 建议修改
-            String ca = FileUtils.readFileToString(caFile).trim();
-            String key = FileUtils.readFileToString(keyFile).trim();
+            String ca = FileUtils.readFileToString(caFile, StandardCharsets.UTF_8).trim();
+            String key = FileUtils.readFileToString(keyFile, StandardCharsets.UTF_8).trim();

             // createIfAbsent: DB has no record -> write; DB has record -> return DB value
             JsonLabelInventory caInv = new JsonLabel().createIfAbsent(LIBVIRT_TLS_CA_KEY, ca);
             JsonLabelInventory keyInv = new JsonLabel().createIfAbsent(LIBVIRT_TLS_PRIVATE_KEY, key);

             // Use DB as source of truth - overwrite local files (HA: MN2 uses MN1's CA from DB)
-            FileUtils.writeStringToFile(caFile, caInv.getLabelValue());
-            FileUtils.writeStringToFile(keyFile, keyInv.getLabelValue());
+            FileUtils.writeStringToFile(caFile, caInv.getLabelValue(), StandardCharsets.UTF_8);
+            FileUtils.writeStringToFile(keyFile, keyInv.getLabelValue(), StandardCharsets.UTF_8);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java` around lines 497
- 506, The KVMHostFactory TLS file read/write block uses deprecated FileUtils
overloads that rely on the platform default charset, so update the calls around
readFileToString and writeStringToFile to use an explicit UTF-8 charset. Keep
the existing createIfAbsent flow and DB-as-source-of-truth logic intact, but
ensure the caFile and keyFile handling is done through the charset-aware
FileUtils APIs so encoding is deterministic across environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java`:
- Around line 497-506: The KVMHostFactory TLS sync logic is persisting the CA
private key in plain text through JsonLabel.createIfAbsent() and
JsonLabelVO.labelValue; update this flow to avoid storing the raw key directly.
In the block that reads cakey.pem and writes via LIBVIRT_TLS_PRIVATE_KEY, switch
to encrypted storage or a controlled secret-distribution mechanism, and only
write/decrypt the key when restoring local files. Ensure the handling around
createIfAbsent(), caInv, and keyInv no longer exposes the private key in the
database.

---

Nitpick comments:
In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java`:
- Around line 497-506: The KVMHostFactory TLS file read/write block uses
deprecated FileUtils overloads that rely on the platform default charset, so
update the calls around readFileToString and writeStringToFile to use an
explicit UTF-8 charset. Keep the existing createIfAbsent flow and
DB-as-source-of-truth logic intact, but ensure the caFile and keyFile handling
is done through the charset-aware FileUtils APIs so encoding is deterministic
across environments.

In `@test/src/test/java/org/zstack/test/kvm/KVMHostUtilsTest.java`:
- Around line 89-99: zstac84446_checkAndFirstDeployUseSameSource is currently a
tautology because it calls buildIpList twice with identical inputs, so it does
not verify check-flow versus deploy-flow consistency. Update the test to compare
the check result from KVMHostUtils.buildIpList against the deploy result
produced by KVMHostUtils.unionIps, passing the detected IP list from buildIpList
into unionIps as the detectedIpsCsv input, and assert the two outputs are
equivalent. Keep the existing assertions that filter expected and excluded IPs,
but make sure the test exercises both buildIpList and unionIps to validate the
real contract.
- Around line 28-206: The newly added test methods in KVMHostUtilsTest use
underscore-separated names instead of lowerCamelCase. Rename the affected tests
to follow the project naming convention, updating the method identifiers such as
filtersZsSuffixIface_consistentWithHostFact,
zstac84446_checkAndFirstDeployUseSameSource,
unionIps_detectedIsBaseAndExtraAppended, and
shouldForceTlsRedeploy_noNeedNeverForces to pure lowerCamelCase while keeping
the test behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d8853faf-bea5-4e1a-93ad-592b64d1489f

📥 Commits

Reviewing files that changed from the base of the PR and between e7037c6 and 59bf199.

📒 Files selected for processing (9)
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMGlobalConfig.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHostDeployArguments.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java
  • plugin/kvm/src/main/java/org/zstack/kvm/KVMHostUtils.java
  • test/src/test/groovy/org/zstack/test/integration/kvm/vm/migrate/LibvirtTlsMigrateCase.groovy
  • test/src/test/java/org/zstack/test/kvm/KVMHostUtilsTest.java

Comment on lines +497 to +506
String ca = FileUtils.readFileToString(caFile).trim();
String key = FileUtils.readFileToString(keyFile).trim();

// createIfAbsent: DB has no record -> write; DB has record -> return DB value
JsonLabelInventory caInv = new JsonLabel().createIfAbsent(LIBVIRT_TLS_CA_KEY, ca);
JsonLabelInventory keyInv = new JsonLabel().createIfAbsent(LIBVIRT_TLS_PRIVATE_KEY, key);

// Use DB as source of truth - overwrite local files (HA: MN2 uses MN1's CA from DB)
FileUtils.writeStringToFile(caFile, caInv.getLabelValue());
FileUtils.writeStringToFile(keyFile, keyInv.getLabelValue());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant factory code and nearby context
sed -n '470,540p' plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java

# Find JsonLabel usage and implementation
rg -n "class JsonLabel|createIfAbsent\(|LIBVIRT_TLS_PRIVATE_KEY|LIBVIRT_TLS_CA_KEY|JsonLabel" -S .

# Inspect the JsonLabel class and related storage paths if present
files=$(git ls-files | rg 'JsonLabel|jsonlabel|Label')
printf '%s\n' "$files"

Repository: MatheMatrix/zstack

Length of output: 20622


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect JsonLabel storage semantics
sed -n '1,180p' core/src/main/java/org/zstack/core/jsonlabel/JsonLabel.java

# Inspect JsonLabelVO persistence fields
sed -n '1,140p' core/src/main/java/org/zstack/core/jsonlabel/JsonLabelVO.java

# Inspect the schema definition for JsonLabelVO
sed -n '120,170p' conf/db/upgrade/V1.7__schema.sql

# Inspect any later schema changes affecting labelValue
sed -n '1,40p' conf/db/upgrade/V3.9.0__schema.sql

Repository: MatheMatrix/zstack

Length of output: 9348


CA 私钥不要明文落库
cakey.pem 通过 JsonLabel.createIfAbsent() 直接写入 JsonLabelVO.labelValue,该字段只是普通 text/MEDIUMTEXT,没有加密或脱敏;数据库访问面一旦暴露,CA 私钥就会泄露。若需要跨管理节点共享,改为加密存储或受控密钥分发。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java` around lines 497
- 506, The KVMHostFactory TLS sync logic is persisting the CA private key in
plain text through JsonLabel.createIfAbsent() and JsonLabelVO.labelValue; update
this flow to avoid storing the raw key directly. In the block that reads
cakey.pem and writes via LIBVIRT_TLS_PRIVATE_KEY, switch to encrypted storage or
a controlled secret-distribution mechanism, and only write/decrypt the key when
restoring local files. Ensure the handling around createIfAbsent(), caInv, and
keyInv no longer exposes the private key in the database.

@zstack-robot-2 zstack-robot-2 deleted the sync/yingzhe.hu/cherry-pick/ZSTAC-84446-5.4.10-full branch July 9, 2026 03:30
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.

3 participants