Skip to content

Commit f15be32

Browse files
committed
Merge branch 'ZSTAC-81343@@2' into '5.5.12'
<feature>[kvm]: add libvirt TLS config See merge request zstackio/zstack!9317
2 parents e0a8246 + 3feb9e9 commit f15be32

6 files changed

Lines changed: 371 additions & 0 deletions

File tree

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3805,6 +3805,26 @@ public static class MigrateVmCmd extends AgentCommand implements HasThreadContex
38053805
private boolean reload;
38063806
@GrayVersion(value = "5.0.0")
38073807
private long bandwidth;
3808+
@GrayVersion(value = "5.5.12")
3809+
private boolean useTls;
3810+
@GrayVersion(value = "5.5.12")
3811+
private String srcHostManagementIp;
3812+
3813+
public String getSrcHostManagementIp() {
3814+
return srcHostManagementIp;
3815+
}
3816+
3817+
public void setSrcHostManagementIp(String srcHostManagementIp) {
3818+
this.srcHostManagementIp = srcHostManagementIp;
3819+
}
3820+
3821+
public boolean isUseTls() {
3822+
return useTls;
3823+
}
3824+
3825+
public void setUseTls(boolean useTls) {
3826+
this.useTls = useTls;
3827+
}
38083828

38093829
public Integer getDownTime() {
38103830
return downTime;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ public class KVMGlobalConfig {
139139
@BindResourceConfig({HostVO.class, ClusterVO.class})
140140
public static GlobalConfig RECONNECT_HOST_RESTART_LIBVIRTD_SERVICE = new GlobalConfig(CATEGORY, "reconnect.host.restart.libvirtd.service");
141141

142+
@GlobalConfigValidation(validValues = {"true", "false"})
143+
@GlobalConfigDef(defaultValue = "true", type = Boolean.class, description = "enable TLS encryption for libvirt remote connections (migration)")
144+
public static GlobalConfig LIBVIRT_TLS_ENABLED = new GlobalConfig(CATEGORY, "libvirt.tls.enabled");
145+
142146
@GlobalConfigValidation
143147
public static GlobalConfig KVMAGENT_PHYSICAL_MEMORY_USAGE_ALARM_THRESHOLD = new GlobalConfig(CATEGORY, "kvmagent.physicalmemory.usage.alarm.threshold");
144148

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,6 +3163,7 @@ public void run(final FlowTrigger trigger, Map data) {
31633163
cmd.setDestHostIp(dstHostMigrateIp);
31643164
cmd.setSrcHostIp(srcHostMigrateIp);
31653165
cmd.setDestHostManagementIp(dstHostMnIp);
3166+
cmd.setSrcHostManagementIp(srcHostMnIp);
31663167
cmd.setMigrateFromDestination(migrateFromDestination);
31673168
cmd.setStorageMigrationPolicy(storageMigrationPolicy == null ? null : storageMigrationPolicy.toString());
31683169
cmd.setVmUuid(vmUuid);
@@ -3174,6 +3175,8 @@ public void run(final FlowTrigger trigger, Map data) {
31743175
cmd.setDownTime(s.downTime);
31753176
cmd.setBandwidth(s.bandwidth);
31763177
cmd.setNics(nicTos);
3178+
cmd.setUseTls(KVMGlobalConfig.LIBVIRT_TLS_ENABLED.value(Boolean.class)
3179+
&& rcf.getResourceConfigValue(KVMGlobalConfig.RECONNECT_HOST_RESTART_LIBVIRTD_SERVICE, self.getUuid(), Boolean.class));
31773180

31783181
if (s.diskMigrationMap != null) {
31793182
Map<String, VolumeTO> diskMigrationMap = new HashMap<>();
@@ -5815,6 +5818,16 @@ public void run(final FlowTrigger trigger, Map data) {
58155818
deployArguments.setSkipPackages(info.getSkipPackages());
58165819
deployArguments.setUpdatePackages(String.valueOf(CoreGlobalProperty.UPDATE_PKG_WHEN_CONNECT));
58175820

5821+
// Build TLS cert IP list: management IP + extra IPs (migration network etc.)
5822+
String managementIp = getSelf().getManagementIp();
5823+
String extraIps = HostSystemTags.EXTRA_IPS.getTokenByResourceUuid(
5824+
self.getUuid(), HostSystemTags.EXTRA_IPS_TOKEN);
5825+
if (extraIps != null && !extraIps.isEmpty()) {
5826+
deployArguments.setTlsCertIps(managementIp + "," + extraIps);
5827+
} else {
5828+
deployArguments.setTlsCertIps(managementIp);
5829+
}
5830+
58185831
if (deployArguments.isForceRun()) {
58195832
runner.setForceRun(true);
58205833
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public class KVMHostDeployArguments extends SyncTimeRequestedDeployArguments {
3939
private String restartLibvirtd;
4040
@SerializedName("extra_packages")
4141
private String extraPackages;
42+
@SerializedName("tls_cert_ips")
43+
private String tlsCertIps;
4244

4345
private transient boolean forceRun = false;
4446

@@ -135,6 +137,14 @@ public void setExtraPackages(String extraPackages) {
135137
this.extraPackages = extraPackages;
136138
}
137139

140+
public String getTlsCertIps() {
141+
return tlsCertIps;
142+
}
143+
144+
public void setTlsCertIps(String tlsCertIps) {
145+
this.tlsCertIps = tlsCertIps;
146+
}
147+
138148
public String getEnableSpiceTls() {
139149
return enableSpiceTls;
140150
}

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.zstack.kvm;
22

3+
import org.apache.commons.io.FileUtils;
34
import org.apache.commons.lang.StringUtils;
45
import org.springframework.beans.factory.annotation.Autowired;
56
import org.springframework.web.util.UriComponentsBuilder;
@@ -9,6 +10,8 @@
910
import org.zstack.compute.vm.VmNicManager;
1011
import org.zstack.core.CoreGlobalProperty;
1112
import org.zstack.core.ansible.AnsibleFacade;
13+
import org.zstack.core.jsonlabel.JsonLabel;
14+
import org.zstack.core.jsonlabel.JsonLabelInventory;
1215
import org.zstack.core.cloudbus.CloudBus;
1316
import org.zstack.core.cloudbus.CloudBusListCallBack;
1417
import org.zstack.core.cloudbus.CloudBusSteppingCallback;
@@ -75,6 +78,7 @@
7578
import org.zstack.resourceconfig.ResourceConfigFacade;
7679
import org.zstack.utils.CollectionUtils;
7780
import org.zstack.utils.IpRangeSet;
81+
import org.zstack.utils.ShellUtils;
7882
import org.zstack.utils.SizeUtils;
7983
import org.zstack.utils.Utils;
8084
import org.zstack.utils.data.SizeUnit;
@@ -85,6 +89,7 @@
8589
import org.zstack.utils.logging.CLogger;
8690

8791
import javax.persistence.Tuple;
92+
import java.io.File;
8893
import java.io.IOException;
8994
import java.net.InetSocketAddress;
9095
import java.net.SocketAddress;
@@ -122,6 +127,10 @@ public class KVMHostFactory extends AbstractService implements HypervisorFactory
122127
HypervisorMessageFactory {
123128
private static final CLogger logger = Utils.getLogger(KVMHostFactory.class);
124129

130+
private static final String LIBVIRT_TLS_CA_KEY = "libvirtTLSCA";
131+
private static final String LIBVIRT_TLS_PRIVATE_KEY = "libvirtTLSPrivateKey";
132+
private static final String CA_DIR = "/var/lib/zstack/pki/CA";
133+
125134
public static final HypervisorType hypervisorType = new HypervisorType(KVMConstant.KVM_HYPERVISOR_TYPE);
126135
public static final VolumeFormat QCOW2_FORMAT = new VolumeFormat(VolumeConstant.VOLUME_FORMAT_QCOW2, hypervisorType);
127136
public static final VolumeFormat RAW_FORMAT = new VolumeFormat(VolumeConstant.VOLUME_FORMAT_RAW, hypervisorType);
@@ -458,8 +467,56 @@ private void processKvmagentPhysicalMemUsageAbnormal(HostProcessPhysicalMemoryUs
458467
bus.send(restartKvmAgentMsg);
459468
}
460469

470+
private void initLibvirtTlsCA() {
471+
if (CoreGlobalProperty.UNIT_TEST_ON) {
472+
return;
473+
}
474+
475+
try {
476+
ShellUtils.run(String.format("mkdir -p %s", CA_DIR));
477+
ShellUtils.run("chown -R zstack:zstack /var/lib/zstack/pki");
478+
479+
File caFile = new File(CA_DIR + "/cacert.pem");
480+
File keyFile = new File(CA_DIR + "/cakey.pem");
481+
482+
// Local CA missing — generate with openssl
483+
// NOTE: ShellUtils.run() prepends sudo only to the first command in &&-chains,
484+
// so each command must be a separate call.
485+
if (!caFile.exists() || !keyFile.exists()) {
486+
ShellUtils.run(String.format(
487+
"openssl genrsa -out %s/cakey.pem 4096", CA_DIR));
488+
ShellUtils.run(String.format(
489+
"openssl req -new -x509 -days 3650 -key %s/cakey.pem " +
490+
"-out %s/cacert.pem -subj '/O=ZStack/CN=ZStack Libvirt CA'",
491+
CA_DIR, CA_DIR));
492+
ShellUtils.run(String.format("chown zstack:zstack %s/cakey.pem %s/cacert.pem",
493+
CA_DIR, CA_DIR));
494+
ShellUtils.run(String.format("chmod 600 %s/cakey.pem", CA_DIR));
495+
ShellUtils.run(String.format("chmod 644 %s/cacert.pem", CA_DIR));
496+
}
497+
498+
String ca = FileUtils.readFileToString(caFile).trim();
499+
String key = FileUtils.readFileToString(keyFile).trim();
500+
501+
// createIfAbsent: DB has no record → write; DB has record → return DB value
502+
JsonLabelInventory caInv = new JsonLabel().createIfAbsent(LIBVIRT_TLS_CA_KEY, ca);
503+
JsonLabelInventory keyInv = new JsonLabel().createIfAbsent(LIBVIRT_TLS_PRIVATE_KEY, key);
504+
505+
// Use DB as source of truth — overwrite local files (HA: MN2 uses MN1's CA from DB)
506+
FileUtils.writeStringToFile(caFile, caInv.getLabelValue());
507+
FileUtils.writeStringToFile(keyFile, keyInv.getLabelValue());
508+
ShellUtils.run(String.format("chmod 600 %s/cakey.pem", CA_DIR));
509+
ShellUtils.run(String.format("chmod 644 %s/cacert.pem", CA_DIR));
510+
511+
logger.info("Libvirt TLS CA initialized and persisted to database");
512+
} catch (Exception e) {
513+
logger.warn("Failed to initialize libvirt TLS CA", e);
514+
}
515+
}
516+
461517
@Override
462518
public boolean start() {
519+
initLibvirtTlsCA();
463520
deployAnsibleModule();
464521
populateExtensions();
465522
configKVMDeviceType();

0 commit comments

Comments
 (0)