From d2b5dc409d5bb497624ae3030d1540c52fb835a7 Mon Sep 17 00:00:00 2001 From: "yingzhe.hu" Date: Tue, 7 Apr 2026 17:12:29 +0800 Subject: [PATCH] [kvm]: update TLS certs via kvmagent on host reconnect Resolves: ZSTAC-83696 Change-Id: I368cc5af8fb3d553bedc3be5d031015719e68ddc --- .../java/org/zstack/kvm/KVMAgentCommands.java | 17 +++++ .../main/java/org/zstack/kvm/KVMConstant.java | 1 + .../src/main/java/org/zstack/kvm/KVMHost.java | 70 +++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java index 90e576cad7f..f4c4ae1a48a 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMAgentCommands.java @@ -455,6 +455,23 @@ public void setFailedInterfaceNames(List failedInterfaceNames) { public static class HostFactCmd extends AgentCommand { } + public static class UpdateTlsCertCmd extends AgentCommand { + private String caCert; + @NoLogging + private String caKey; + private String certIps; + + public String getCaCert() { return caCert; } + public void setCaCert(String caCert) { this.caCert = caCert; } + public String getCaKey() { return caKey; } + public void setCaKey(String caKey) { this.caKey = caKey; } + public String getCertIps() { return certIps; } + public void setCertIps(String certIps) { this.certIps = certIps; } + } + + public static class UpdateTlsCertResponse extends AgentResponse { + } + public static class HostFactResponse extends AgentResponse { @GrayVersion(value = "5.0.0") private String osDistribution; diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java index 1b2df9f8f2a..99fa4196b70 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java @@ -81,6 +81,7 @@ public interface KVMConstant { String KVM_DELETE_CONSOLE_FIREWALL_PATH = "/vm/console/deletefirewall"; String KVM_UPDATE_HOST_OS_PATH = "/host/updateos"; String KVM_HOST_UPDATE_DEPENDENCY_PATH = "/host/updatedependency"; + String KVM_UPDATE_TLS_CERT_PATH = "/host/updateTlsCert"; String HOST_SHUTDOWN = "/host/shutdown"; String HOST_REBOOT = "/host/reboot"; String HOST_UPDATE_SPICE_CHANNEL_CONFIG_PATH = "/host/updateSpiceChannelConfig"; diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java index 27ba23476ed..10a4a216485 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java @@ -33,6 +33,7 @@ import org.zstack.core.db.SQLBatch; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; +import org.zstack.core.jsonlabel.JsonLabel; import org.zstack.core.thread.*; import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.core.timeout.TimeHelper; @@ -195,6 +196,7 @@ public class KVMHost extends HostBase implements Host { private String checkSnapshotPath; private String mergeSnapshotPath; private String hostFactPath; + private String updateTlsCertPath; private String hostCheckFilePath; private String attachIsoPath; private String detachIsoPath; @@ -328,6 +330,10 @@ public KVMHost(KVMHostVO self, KVMHostContext context) { ub.path(KVMConstant.KVM_HOST_FACT_PATH); hostFactPath = ub.build().toString(); + ub = UriComponentsBuilder.fromHttpUrl(baseUrl); + ub.path(KVMConstant.KVM_UPDATE_TLS_CERT_PATH); + updateTlsCertPath = ub.build().toString(); + ub = UriComponentsBuilder.fromHttpUrl(baseUrl); ub.path(KVMConstant.KVM_HOST_CHECK_FILE_PATH); hostCheckFilePath = ub.build().toString(); @@ -6025,6 +6031,70 @@ public void fail(ErrorCode errorCode) { flow(createCollectHostFactsFlow(info)); + flow(new NoRollbackFlow() { + String __name__ = "update-tls-certs-if-needed"; + + @Override + public boolean skip(Map data) { + return CoreGlobalProperty.UNIT_TEST_ON + || !KVMGlobalConfig.LIBVIRT_TLS_ENABLED.value(Boolean.class); + } + + @Override + public void run(FlowTrigger trigger, Map data) { + String managementIp = getSelf().getManagementIp(); + String extraIps = HostSystemTags.EXTRA_IPS.getTokenByResourceUuid( + self.getUuid(), HostSystemTags.EXTRA_IPS_TOKEN); + + List allIps = new ArrayList<>(); + allIps.add(managementIp); + if (extraIps != null && !extraIps.isEmpty()) { + for (String ip : extraIps.split(",")) { + String trimmed = ip.trim(); + if (!trimmed.isEmpty() && !allIps.contains(trimmed)) { + allIps.add(trimmed); + } + } + } + + String certIps = String.join(",", allIps); + + String caCert = new JsonLabel().get("libvirtTLSCA", String.class); + String caKey = new JsonLabel().get("libvirtTLSPrivateKey", String.class); + if (caCert == null || caKey == null) { + logger.warn("TLS CA cert/key not found in database, skipping cert update"); + trigger.next(); + return; + } + + UpdateTlsCertCmd cmd = new UpdateTlsCertCmd(); + cmd.setCaCert(caCert); + cmd.setCaKey(caKey); + cmd.setCertIps(certIps); + + new Http<>(updateTlsCertPath, cmd, UpdateTlsCertResponse.class) + .call(new ReturnValueCompletion(trigger) { + @Override + public void success(UpdateTlsCertResponse ret) { + if (!ret.isSuccess()) { + logger.warn(String.format("Failed to update TLS certs on host[uuid:%s]: %s", + self.getUuid(), ret.getError())); + } + // cert update failure should not block reconnect + trigger.next(); + } + + @Override + public void fail(ErrorCode errorCode) { + logger.warn(String.format("Failed to update TLS certs on host[uuid:%s]: %s", + self.getUuid(), errorCode)); + // cert update failure should not block reconnect + trigger.next(); + } + }); + } + }); + if (info.isNewAdded()) { flow(new NoRollbackFlow() { String __name__ = "check-qemu-libvirt-version";