diff --git a/compute/src/main/java/org/zstack/compute/vm/devices/DummyTpmEncryptedResourceKeyBackend.java b/compute/src/main/java/org/zstack/compute/vm/devices/DummyTpmEncryptedResourceKeyBackend.java
index e221c03bb4f..4ee23a2a02d 100644
--- a/compute/src/main/java/org/zstack/compute/vm/devices/DummyTpmEncryptedResourceKeyBackend.java
+++ b/compute/src/main/java/org/zstack/compute/vm/devices/DummyTpmEncryptedResourceKeyBackend.java
@@ -80,4 +80,9 @@ public void cleanEncryptedResourceKey(String vmHostBackupFileUuid) {
// do nothing
logger.debug("ignore cleanup encrypted resource key request for VmHostBackupFileVO: " + vmHostBackupFileUuid);
}
+
+ @Override
+ public void cleanTpmKeyBackupEncryptedResourceKey(String tpmKeyBackupUuid) {
+ logger.debug("ignore cleanup encrypted resource key request for TpmKeyBackupVO: " + tpmKeyBackupUuid);
+ }
}
diff --git a/compute/src/main/java/org/zstack/compute/vm/devices/TpmEncryptedResourceKeyBackend.java b/compute/src/main/java/org/zstack/compute/vm/devices/TpmEncryptedResourceKeyBackend.java
index b5d1c766249..0db19ee4525 100644
--- a/compute/src/main/java/org/zstack/compute/vm/devices/TpmEncryptedResourceKeyBackend.java
+++ b/compute/src/main/java/org/zstack/compute/vm/devices/TpmEncryptedResourceKeyBackend.java
@@ -97,4 +97,9 @@ static class RestoreEncryptedResourceKeyContext {
void restoreEncryptedResourceKey(RestoreEncryptedResourceKeyContext context);
void cleanEncryptedResourceKey(String vmHostBackupFileUuid);
+
+ /**
+ * Remove encryption key material stored for a {@link org.zstack.header.tpm.entity.TpmKeyBackupVO}.
+ */
+ void cleanTpmKeyBackupEncryptedResourceKey(String tpmKeyBackupUuid);
}
diff --git a/conf/db/zsv/V5.1.0__schema.sql b/conf/db/zsv/V5.1.0__schema.sql
index 243651492d9..0c2187e60bb 100644
--- a/conf/db/zsv/V5.1.0__schema.sql
+++ b/conf/db/zsv/V5.1.0__schema.sql
@@ -1,3 +1,10 @@
+CREATE TABLE IF NOT EXISTS `zstack`.`TpmKeyBackupVO` (
+ `uuid` char(32) NOT NULL UNIQUE,
+ `lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59',
+ PRIMARY KEY (`uuid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
DELETE FROM `EncryptedResourceKeyRefVO`
WHERE `resourceUuid` NOT IN (SELECT `uuid` FROM `ResourceVO`);
ALTER TABLE `EncryptedResourceKeyRefVO`
diff --git a/conf/persistence.xml b/conf/persistence.xml
index e0b9cccdb32..97fa6edff1c 100755
--- a/conf/persistence.xml
+++ b/conf/persistence.xml
@@ -19,6 +19,7 @@
org.zstack.header.managementnode.ManagementNodeVO
org.zstack.header.managementnode.ManagementNodeContextVO
org.zstack.header.tpm.entity.TpmVO
+ org.zstack.header.tpm.entity.TpmKeyBackupVO
org.zstack.header.vm.additions.VmHostFileVO
org.zstack.header.vm.additions.VmHostBackupFileVO
org.zstack.header.vm.additions.VmHostFileContentVO
diff --git a/header/src/main/java/org/zstack/header/tpm/entity/TpmKeyBackupVO.java b/header/src/main/java/org/zstack/header/tpm/entity/TpmKeyBackupVO.java
new file mode 100644
index 00000000000..30efe258aa4
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/tpm/entity/TpmKeyBackupVO.java
@@ -0,0 +1,37 @@
+package org.zstack.header.tpm.entity;
+
+import org.zstack.header.vo.ResourceVO;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import java.sql.Timestamp;
+
+/**
+ * Internal holder for a TPM encryption resource key snapshot during snapshot-group revert.
+ * The key material is stored in {@code EncryptedResourceKeyRefVO} with {@code resourceUuid} = this VO's uuid.
+ */
+@Entity
+@Table
+public class TpmKeyBackupVO extends ResourceVO {
+ @Column
+ private Timestamp createDate;
+ @Column
+ private Timestamp lastOpDate;
+
+ public Timestamp getCreateDate() {
+ return createDate;
+ }
+
+ public void setCreateDate(Timestamp createDate) {
+ this.createDate = createDate;
+ }
+
+ public Timestamp getLastOpDate() {
+ return lastOpDate;
+ }
+
+ public void setLastOpDate(Timestamp lastOpDate) {
+ this.lastOpDate = lastOpDate;
+ }
+}
diff --git a/header/src/main/java/org/zstack/header/tpm/entity/TpmKeyBackupVO_.java b/header/src/main/java/org/zstack/header/tpm/entity/TpmKeyBackupVO_.java
new file mode 100644
index 00000000000..f2bdc161fcd
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/tpm/entity/TpmKeyBackupVO_.java
@@ -0,0 +1,13 @@
+package org.zstack.header.tpm.entity;
+
+import org.zstack.header.vo.ResourceVO_;
+
+import javax.persistence.metamodel.SingularAttribute;
+import javax.persistence.metamodel.StaticMetamodel;
+import java.sql.Timestamp;
+
+@StaticMetamodel(TpmKeyBackupVO.class)
+public class TpmKeyBackupVO_ extends ResourceVO_ {
+ public static volatile SingularAttribute createDate;
+ public static volatile SingularAttribute lastOpDate;
+}
diff --git a/header/src/main/java/org/zstack/header/tpm/message/DeleteTpmKeyBackupMsg.java b/header/src/main/java/org/zstack/header/tpm/message/DeleteTpmKeyBackupMsg.java
new file mode 100644
index 00000000000..dd512143235
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/tpm/message/DeleteTpmKeyBackupMsg.java
@@ -0,0 +1,24 @@
+package org.zstack.header.tpm.message;
+
+import org.zstack.header.message.NeedReplyMessage;
+
+public class DeleteTpmKeyBackupMsg extends NeedReplyMessage {
+ private String tpmUuid;
+ private String tpmKeyBackupUuid;
+
+ public String getTpmUuid() {
+ return tpmUuid;
+ }
+
+ public void setTpmUuid(String tpmUuid) {
+ this.tpmUuid = tpmUuid;
+ }
+
+ public String getTpmKeyBackupUuid() {
+ return tpmKeyBackupUuid;
+ }
+
+ public void setTpmKeyBackupUuid(String tpmKeyBackupUuid) {
+ this.tpmKeyBackupUuid = tpmKeyBackupUuid;
+ }
+}
diff --git a/header/src/main/java/org/zstack/header/tpm/message/DeleteTpmKeyBackupReply.java b/header/src/main/java/org/zstack/header/tpm/message/DeleteTpmKeyBackupReply.java
new file mode 100644
index 00000000000..aa49fd35dc0
--- /dev/null
+++ b/header/src/main/java/org/zstack/header/tpm/message/DeleteTpmKeyBackupReply.java
@@ -0,0 +1,6 @@
+package org.zstack.header.tpm.message;
+
+import org.zstack.header.message.MessageReply;
+
+public class DeleteTpmKeyBackupReply extends MessageReply {
+}
diff --git a/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyMsg.java b/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyMsg.java
index 0b244be60fe..15021ac881e 100644
--- a/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyMsg.java
+++ b/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyMsg.java
@@ -5,6 +5,11 @@
public class RestoreTpmEncryptionKeyMsg extends NeedReplyMessage {
private String srcResourceUuid;
private String dstResourceUuid;
+ /**
+ * When true, the current encryption key on {@link #dstResourceUuid} (TPM) is copied to a
+ * {@link org.zstack.header.tpm.entity.TpmKeyBackupVO} before restoring from {@link #srcResourceUuid}.
+ */
+ private boolean backupCurrentKey = true;
public String getSrcResourceUuid() {
return srcResourceUuid;
@@ -21,4 +26,12 @@ public String getDstResourceUuid() {
public void setDstResourceUuid(String dstResourceUuid) {
this.dstResourceUuid = dstResourceUuid;
}
+
+ public boolean isBackupCurrentKey() {
+ return backupCurrentKey;
+ }
+
+ public void setBackupCurrentKey(boolean backupCurrentKey) {
+ this.backupCurrentKey = backupCurrentKey;
+ }
}
diff --git a/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyReply.java b/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyReply.java
index 0a6e1fcae74..9cb2d539af0 100644
--- a/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyReply.java
+++ b/header/src/main/java/org/zstack/header/tpm/message/RestoreTpmEncryptionKeyReply.java
@@ -3,4 +3,13 @@
import org.zstack.header.message.MessageReply;
public class RestoreTpmEncryptionKeyReply extends MessageReply {
+ private String tpmKeyBackupUuid;
+
+ public String getTpmKeyBackupUuid() {
+ return tpmKeyBackupUuid;
+ }
+
+ public void setTpmKeyBackupUuid(String tpmKeyBackupUuid) {
+ this.tpmKeyBackupUuid = tpmKeyBackupUuid;
+ }
}
diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/tpm/KvmTpmManager.java b/plugin/kvm/src/main/java/org/zstack/kvm/tpm/KvmTpmManager.java
index fc1dca93c8a..6ab93c7f9df 100644
--- a/plugin/kvm/src/main/java/org/zstack/kvm/tpm/KvmTpmManager.java
+++ b/plugin/kvm/src/main/java/org/zstack/kvm/tpm/KvmTpmManager.java
@@ -4,16 +4,19 @@
import org.zstack.compute.vm.VmGlobalConfig;
import org.zstack.compute.vm.devices.TpmEncryptedResourceKeyBackend;
import org.zstack.compute.vm.devices.VmTpmManager;
+import org.zstack.core.Platform;
import org.zstack.core.asyncbatch.While;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.cloudbus.MessageSafe;
+import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.core.db.SQL;
import org.zstack.core.db.SQLBatch;
import org.zstack.core.thread.ChainTask;
import org.zstack.core.thread.SyncTaskChain;
import org.zstack.core.thread.ThreadFacade;
+import org.zstack.core.timeout.TimeHelper;
import org.zstack.core.workflow.SimpleFlowChain;
import org.zstack.header.AbstractService;
import org.zstack.header.core.Completion;
@@ -36,10 +39,13 @@
import org.zstack.header.tpm.api.APIUpdateTpmMsg;
import org.zstack.header.tpm.entity.TpmCapabilityView;
import org.zstack.header.tpm.entity.TpmInventory;
+import org.zstack.header.tpm.entity.TpmKeyBackupVO;
import org.zstack.header.tpm.entity.TpmVO;
import org.zstack.header.tpm.entity.TpmVO_;
import org.zstack.header.tpm.message.AddTpmMsg;
import org.zstack.header.tpm.message.AddTpmReply;
+import org.zstack.header.tpm.message.DeleteTpmKeyBackupMsg;
+import org.zstack.header.tpm.message.DeleteTpmKeyBackupReply;
import org.zstack.header.tpm.message.RestoreTpmEncryptionKeyMsg;
import org.zstack.header.tpm.message.RestoreTpmEncryptionKeyReply;
import org.zstack.header.tpm.message.TpmDeletionMsg;
@@ -74,6 +80,7 @@
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
@@ -113,6 +120,10 @@ public class KvmTpmManager extends AbstractService {
private EncryptedResourceKeyManager resourceKeyManager;
@Autowired
private KvmSecureBootExtensions secureBootExtensions;
+ @Autowired
+ private DatabaseFacade databaseFacade;
+ @Autowired
+ private TimeHelper timeHelper;
@Override
public boolean start() {
@@ -153,6 +164,8 @@ private void handleLocalMessage(Message msg) {
handle((BackupTpmEncryptionKeyMsg) msg);
} else if (msg instanceof RestoreTpmEncryptionKeyMsg) {
handle((RestoreTpmEncryptionKeyMsg) msg);
+ } else if (msg instanceof DeleteTpmKeyBackupMsg) {
+ handle((DeleteTpmKeyBackupMsg) msg);
} else if (msg instanceof ResetVmTpmMsg) {
handle((ResetVmTpmMsg) msg);
} else {
@@ -648,11 +661,47 @@ private void handle(BackupTpmEncryptionKeyMsg msg) {
}
private void handle(RestoreTpmEncryptionKeyMsg msg) {
- RestoreEncryptedResourceKeyContext content = new RestoreEncryptedResourceKeyContext();
- content.srcResourceUuid = msg.getSrcResourceUuid();
- content.dstResourceUuid = msg.getDstResourceUuid();
- tpmKeyBackend.restoreEncryptedResourceKey(content);
- bus.reply(msg, new RestoreTpmEncryptionKeyReply());
+ RestoreTpmEncryptionKeyReply reply = new RestoreTpmEncryptionKeyReply();
+ String tpmKeyBackupUuid = null;
+ try {
+ if (msg.isBackupCurrentKey()
+ && msg.getDstResourceUuid() != null
+ && tpmKeyBackend.checkTpmKeyProviderAttached(msg.getDstResourceUuid())) {
+ tpmKeyBackupUuid = Platform.getUuid();
+ TpmKeyBackupVO backupVo = new TpmKeyBackupVO();
+ backupVo.setUuid(tpmKeyBackupUuid);
+ Timestamp now = new Timestamp(timeHelper.getCurrentTimeMillis());
+ backupVo.setCreateDate(now);
+ backupVo.setLastOpDate(now);
+ databaseFacade.persist(backupVo);
+ BackupEncryptedResourceKeyContext backupCtx = new BackupEncryptedResourceKeyContext();
+ backupCtx.srcResourceUuid = msg.getDstResourceUuid();
+ backupCtx.dstResourceUuid = tpmKeyBackupUuid;
+ tpmKeyBackend.backupEncryptedResourceKey(backupCtx);
+ reply.setTpmKeyBackupUuid(tpmKeyBackupUuid);
+ }
+
+ RestoreEncryptedResourceKeyContext content = new RestoreEncryptedResourceKeyContext();
+ content.srcResourceUuid = msg.getSrcResourceUuid();
+ content.dstResourceUuid = msg.getDstResourceUuid();
+ tpmKeyBackend.restoreEncryptedResourceKey(content);
+ bus.reply(msg, reply);
+ } catch (Exception t) {
+ if (tpmKeyBackupUuid != null) {
+ tpmKeyBackend.cleanTpmKeyBackupEncryptedResourceKey(tpmKeyBackupUuid);
+ databaseFacade.removeByPrimaryKey(tpmKeyBackupUuid, TpmKeyBackupVO.class);
+ }
+ throw t;
+ }
+ }
+
+ private void handle(DeleteTpmKeyBackupMsg msg) {
+ DeleteTpmKeyBackupReply reply = new DeleteTpmKeyBackupReply();
+ if (msg.getTpmKeyBackupUuid() != null) {
+ tpmKeyBackend.cleanTpmKeyBackupEncryptedResourceKey(msg.getTpmKeyBackupUuid());
+ databaseFacade.removeByPrimaryKey(msg.getTpmKeyBackupUuid(), TpmKeyBackupVO.class);
+ }
+ bus.reply(msg, reply);
}
static class ResetVmTpmContext {
diff --git a/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java b/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java
index b99849cdb7a..fcb6907ca9e 100644
--- a/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java
+++ b/storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupBase.java
@@ -32,7 +32,9 @@
import org.zstack.header.tpm.entity.TpmVO;
import org.zstack.header.tpm.entity.TpmVO_;
import org.zstack.header.tpm.message.AddTpmMsg;
+import org.zstack.header.tpm.message.DeleteTpmKeyBackupMsg;
import org.zstack.header.tpm.message.RestoreTpmEncryptionKeyMsg;
+import org.zstack.header.tpm.message.RestoreTpmEncryptionKeyReply;
import org.zstack.header.tpm.message.TpmDeletionMsg;
import org.zstack.header.vm.additions.RestoreVmHostFileMsg;
import org.zstack.header.vm.RestoreVmInstanceMsg;
@@ -389,6 +391,7 @@ class Context {
VolumeSnapshotGroupVO newGroup;
boolean snapshotGroupHasTpm;
String tpmUuid, newCreateTpmUuid;
+ String tpmKeyBackupUuid;
}
Context context = new Context();
context.snapshotGroupHasTpm = VolumeSnapshotGroupSystemTags.WITH_TPM.hasTag(msg.getUuid());
@@ -457,11 +460,15 @@ public void run(MessageReply reply) {
RestoreTpmEncryptionKeyMsg restoreMsg = new RestoreTpmEncryptionKeyMsg();
restoreMsg.setSrcResourceUuid(msg.getUuid());
restoreMsg.setDstResourceUuid(context.tpmUuid);
+ restoreMsg.setBackupCurrentKey(true);
bus.makeTargetServiceIdByResourceUuid(restoreMsg, SERVICE_ID, context.tpmUuid);
bus.send(restoreMsg, new CloudBusCallBack(msg) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
+ if (reply instanceof RestoreTpmEncryptionKeyReply) {
+ context.tpmKeyBackupUuid = ((RestoreTpmEncryptionKeyReply) reply).getTpmKeyBackupUuid();
+ }
logger.debug(String.format(
"restore resource key of Tpm[uuid:%s] for VM[uuid:%s] for snapshotGroup[uuid:%s]",
context.tpmUuid, vmUuid, msg.getUuid()));
@@ -472,7 +479,43 @@ public void run(MessageReply reply) {
}
});
})
- // TODO: It should has rollback
+ .rollback(trigger -> {
+ if (context.tpmKeyBackupUuid == null) {
+ trigger.rollback();
+ return;
+ }
+ RestoreTpmEncryptionKeyMsg rollbackMsg = new RestoreTpmEncryptionKeyMsg();
+ rollbackMsg.setBackupCurrentKey(false);
+ rollbackMsg.setSrcResourceUuid(context.tpmKeyBackupUuid);
+ rollbackMsg.setDstResourceUuid(context.tpmUuid);
+ bus.makeTargetServiceIdByResourceUuid(rollbackMsg, SERVICE_ID, context.tpmUuid);
+ bus.send(rollbackMsg, new CloudBusCallBack(trigger) {
+ @Override
+ public void run(MessageReply reply) {
+ if (!reply.isSuccess()) {
+ logger.debug(String.format(
+ "failed to rollback TPM encryption key from TpmKeyBackupVO[uuid:%s] to Tpm[uuid:%s]",
+ context.tpmKeyBackupUuid, context.tpmUuid));
+ }
+ DeleteTpmKeyBackupMsg delMsg = new DeleteTpmKeyBackupMsg();
+ delMsg.setTpmUuid(context.tpmUuid);
+ delMsg.setTpmKeyBackupUuid(context.tpmKeyBackupUuid);
+ bus.makeTargetServiceIdByResourceUuid(delMsg, SERVICE_ID, context.tpmUuid);
+ bus.send(delMsg, new CloudBusCallBack(trigger) {
+ @Override
+ public void run(MessageReply reply2) {
+ if (!reply2.isSuccess()) {
+ logger.debug(String.format(
+ "failed to delete TpmKeyBackupVO[uuid:%s] after TPM key rollback",
+ context.tpmKeyBackupUuid));
+ }
+ context.tpmKeyBackupUuid = null;
+ trigger.rollback();
+ }
+ });
+ }
+ });
+ })
.build())
.then(Flow.of("remove-tpm-if-needed")
.runIf(data -> !context.snapshotGroupHasTpm && context.tpmUuid != null)
@@ -549,6 +592,26 @@ public void done(ErrorCodeList errorCodeList) {
});
})
.build())
+ .then(Flow.of("delete-tpm-key-backup")
+ .runIf(data -> context.tpmKeyBackupUuid != null)
+ .handle(trigger -> {
+ DeleteTpmKeyBackupMsg delMsg = new DeleteTpmKeyBackupMsg();
+ delMsg.setTpmUuid(context.tpmUuid);
+ delMsg.setTpmKeyBackupUuid(context.tpmKeyBackupUuid);
+ bus.makeTargetServiceIdByResourceUuid(delMsg, SERVICE_ID, context.tpmUuid);
+ bus.send(delMsg, new CloudBusCallBack(trigger) {
+ @Override
+ public void run(MessageReply r) {
+ if (r.isSuccess()) {
+ context.tpmKeyBackupUuid = null;
+ trigger.next();
+ } else {
+ trigger.fail(r.getError());
+ }
+ }
+ });
+ })
+ .build())
.propagateExceptionTo(msg)
.done(() -> bus.reply(msg, reply))
.error(errorCode -> {