|
12 | 12 | import org.zstack.compute.cluster.arch.ClusterResourceConfigInitializer; |
13 | 13 | import org.zstack.compute.host.*; |
14 | 14 | import org.zstack.compute.vm.*; |
| 15 | +import org.zstack.compute.vm.devices.TpmEncryptedResourceKeyBackend; |
| 16 | +import org.zstack.compute.vm.devices.VmTpmManager; |
15 | 17 | import org.zstack.core.asyncbatch.While; |
16 | 18 | import org.zstack.core.cloudbus.ResourceDestinationMaker; |
17 | 19 | import org.zstack.core.timeout.TimeHelper; |
|
55 | 57 | import org.zstack.header.exception.CloudRuntimeException; |
56 | 58 | import org.zstack.header.host.*; |
57 | 59 | import org.zstack.header.host.MigrateVmOnHypervisorMsg.StorageMigrationPolicy; |
| 60 | +import org.zstack.header.keyprovider.EncryptedResourceKeyManager; |
| 61 | +import org.zstack.header.keyprovider.EncryptedResourceKeyManager.GetOrCreateResourceKeyContext; |
| 62 | +import org.zstack.header.keyprovider.EncryptedResourceKeyManager.ResourceKeyResult; |
58 | 63 | import org.zstack.header.secret.SecretHostDefineMsg; |
59 | 64 | import org.zstack.header.secret.SecretHostDefineReply; |
60 | 65 | import org.zstack.header.secret.SecretHostDeleteMsg; |
|
67 | 72 | import org.zstack.header.message.NeedReplyMessage; |
68 | 73 | import org.zstack.header.network.l2.*; |
69 | 74 | import org.zstack.header.network.l3.L3NetworkInventory; |
| 75 | +import org.zstack.header.tpm.entity.TpmVO; |
70 | 76 | import org.zstack.header.network.l3.L3NetworkVO; |
71 | 77 | import org.zstack.header.rest.JsonAsyncRESTCallback; |
72 | 78 | import org.zstack.header.rest.RESTFacade; |
@@ -168,6 +174,10 @@ public class KVMHost extends HostBase implements Host { |
168 | 174 | private AccountManager accountMgr; |
169 | 175 | @Autowired |
170 | 176 | private ResourceDestinationMaker destMaker; |
| 177 | + @Autowired |
| 178 | + private TpmEncryptedResourceKeyBackend tpmKeyBackend; |
| 179 | + @Autowired |
| 180 | + private EncryptedResourceKeyManager resourceKeyManager; |
171 | 181 |
|
172 | 182 | private KVMHostContext context; |
173 | 183 |
|
@@ -3170,6 +3180,146 @@ public void fail(ErrorCode errorCode) { |
3170 | 3180 | } |
3171 | 3181 | }); |
3172 | 3182 |
|
| 3183 | + flow(new NoRollbackFlow() { |
| 3184 | + String __name__ = "ensure-secret-on-dst-before-migrate"; |
| 3185 | + |
| 3186 | + @Override |
| 3187 | + public void run(FlowTrigger trigger, Map data) { |
| 3188 | + String tpmUuid = VmTpmManager.findTpmUuidForVmOrNull(vmUuid); |
| 3189 | + if (StringUtils.isBlank(tpmUuid)) { |
| 3190 | + trigger.next(); |
| 3191 | + return; |
| 3192 | + } |
| 3193 | + String providerUuid = tpmKeyBackend.findKeyProviderUuidByTpm(tpmUuid); |
| 3194 | + String providerName = tpmKeyBackend.findKeyProviderNameByTpm(tpmUuid); |
| 3195 | + if (StringUtils.isBlank(providerUuid) && StringUtils.isBlank(providerName)) { |
| 3196 | + trigger.fail(operr("missing TPM resource key binding for tpm[uuid:%s] before migrate", tpmUuid)); |
| 3197 | + return; |
| 3198 | + } |
| 3199 | + |
| 3200 | + GetOrCreateResourceKeyContext keyCtx = new GetOrCreateResourceKeyContext(); |
| 3201 | + keyCtx.setResourceUuid(tpmUuid); |
| 3202 | + keyCtx.setResourceType(TpmVO.class.getSimpleName()); |
| 3203 | + keyCtx.setKeyProviderUuid(providerUuid); |
| 3204 | + keyCtx.setKeyProviderName(providerName); |
| 3205 | + keyCtx.setPurpose("vtpm"); |
| 3206 | + resourceKeyManager.getOrCreateKey(keyCtx, new ReturnValueCompletion<ResourceKeyResult>(trigger) { |
| 3207 | + @Override |
| 3208 | + public void success(ResourceKeyResult result) { |
| 3209 | + String effectiveProviderName = result.getKeyProviderName(); |
| 3210 | + if (StringUtils.isBlank(effectiveProviderName)) { |
| 3211 | + effectiveProviderName = providerName; |
| 3212 | + } |
| 3213 | + if (StringUtils.isBlank(effectiveProviderName)) { |
| 3214 | + trigger.fail(operr("missing effective key provider name for tpm[uuid:%s] before migrate", tpmUuid)); |
| 3215 | + return; |
| 3216 | + } |
| 3217 | + |
| 3218 | + final String usageInstance = "tpm0"; |
| 3219 | + final Integer[] keyVersionHolder = new Integer[1]; |
| 3220 | + final String[] sourceSecretUuidHolder = new String[1]; |
| 3221 | + |
| 3222 | + FlowChain chain = FlowChainBuilder.newSimpleFlowChain(); |
| 3223 | + chain.setName(String.format("get-and-ensure-vtpm-secret-vm-%s", vmUuid)); |
| 3224 | + chain.then(new NoRollbackFlow() { |
| 3225 | + String __name__ = "get-source-secret-uuid"; |
| 3226 | + |
| 3227 | + @Override |
| 3228 | + public void run(FlowTrigger innerTrigger, Map data) { |
| 3229 | + Object keyVersionObj = SQL.New("select keyVersion from EncryptedResourceKeyRefVO " + |
| 3230 | + "where resourceType = :resourceType and resourceUuid = :resourceUuid") |
| 3231 | + .param("resourceType", TpmVO.class.getSimpleName()) |
| 3232 | + .param("resourceUuid", tpmUuid) |
| 3233 | + .find(); |
| 3234 | + if (keyVersionObj == null) { |
| 3235 | + innerTrigger.fail(operr("cannot find keyVersion in EncryptedResourceKeyRefVO for tpm[uuid:%s] before migrate", tpmUuid)); |
| 3236 | + return; |
| 3237 | + } |
| 3238 | + if (!(keyVersionObj instanceof Number)) { |
| 3239 | + innerTrigger.fail(operr("invalid keyVersion type[%s] in EncryptedResourceKeyRefVO for tpm[uuid:%s] before migrate", |
| 3240 | + keyVersionObj.getClass().getName(), tpmUuid)); |
| 3241 | + return; |
| 3242 | + } |
| 3243 | + |
| 3244 | + keyVersionHolder[0] = ((Number) keyVersionObj).intValue(); |
| 3245 | + KVMAgentCommands.SecretHostGetCmd getCmd = new KVMAgentCommands.SecretHostGetCmd(); |
| 3246 | + getCmd.setVmUuid(vmUuid); |
| 3247 | + getCmd.setPurpose("vtpm"); |
| 3248 | + getCmd.setKeyVersion(0); |
| 3249 | + getCmd.setUsageInstance(usageInstance); |
| 3250 | + restf.asyncJsonPost(buildUrl(KVMConstant.KVM_GET_SECRET_PATH), getCmd, |
| 3251 | + new JsonAsyncRESTCallback<KVMAgentCommands.SecretHostGetResponse>(innerTrigger) { |
| 3252 | + @Override |
| 3253 | + public void fail(ErrorCode err) { |
| 3254 | + innerTrigger.fail(err != null ? err : operr("get secret on source host failed before migrate")); |
| 3255 | + } |
| 3256 | + |
| 3257 | + @Override |
| 3258 | + public void success(KVMAgentCommands.SecretHostGetResponse getRsp) { |
| 3259 | + if (getRsp == null || !getRsp.isSuccess() || StringUtils.isBlank(getRsp.getSecretUuid())) { |
| 3260 | + String details = getRsp == null ? "empty response" : getRsp.getError(); |
| 3261 | + innerTrigger.fail(operr("failed to get source secret uuid before migrate: %s", details)); |
| 3262 | + return; |
| 3263 | + } |
| 3264 | + |
| 3265 | + sourceSecretUuidHolder[0] = getRsp.getSecretUuid(); |
| 3266 | + innerTrigger.next(); |
| 3267 | + } |
| 3268 | + |
| 3269 | + @Override |
| 3270 | + public Class<KVMAgentCommands.SecretHostGetResponse> getReturnClass() { |
| 3271 | + return KVMAgentCommands.SecretHostGetResponse.class; |
| 3272 | + } |
| 3273 | + }, TimeUnit.SECONDS, KVMConstant.ENVELOPE_KEY_HTTP_TIMEOUT_SEC); |
| 3274 | + } |
| 3275 | + }).then(new NoRollbackFlow() { |
| 3276 | + String __name__ = "ensure-secret-on-dst"; |
| 3277 | + |
| 3278 | + @Override |
| 3279 | + public void run(FlowTrigger innerTrigger, Map data) { |
| 3280 | + SecretHostDefineMsg innerMsg = new SecretHostDefineMsg(); |
| 3281 | + innerMsg.setHostUuid(dstHostUuid); |
| 3282 | + innerMsg.setVmUuid(vmUuid); |
| 3283 | + innerMsg.setDekBase64(result.getDekBase64()); |
| 3284 | + innerMsg.setPurpose("vtpm"); |
| 3285 | + innerMsg.setKeyVersion(0); |
| 3286 | + innerMsg.setUsageInstance(usageInstance); |
| 3287 | + innerMsg.setSecretUuid(sourceSecretUuidHolder[0]); |
| 3288 | + innerMsg.setDescription(String.format("Define secret for VM %s before live migration", vmUuid)); |
| 3289 | + bus.makeTargetServiceIdByResourceUuid(innerMsg, HostConstant.SERVICE_ID, innerMsg.getHostUuid()); |
| 3290 | + bus.send(innerMsg, new CloudBusCallBack(innerTrigger) { |
| 3291 | + @Override |
| 3292 | + public void run(MessageReply reply) { |
| 3293 | + if (reply.isSuccess()) { |
| 3294 | + innerTrigger.next(); |
| 3295 | + } else { |
| 3296 | + innerTrigger.fail(reply.getError()); |
| 3297 | + } |
| 3298 | + } |
| 3299 | + }); |
| 3300 | + } |
| 3301 | + }).error(new FlowErrorHandler(trigger) { |
| 3302 | + @Override |
| 3303 | + public void handle(ErrorCode errCode, Map data) { |
| 3304 | + trigger.fail(errCode); |
| 3305 | + } |
| 3306 | + }).done(new FlowDoneHandler(trigger) { |
| 3307 | + @Override |
| 3308 | + public void handle(Map data) { |
| 3309 | + trigger.next(); |
| 3310 | + } |
| 3311 | + }).start(); |
| 3312 | + } |
| 3313 | + |
| 3314 | + @Override |
| 3315 | + public void fail(ErrorCode errorCode) { |
| 3316 | + trigger.fail(errorCode); |
| 3317 | + } |
| 3318 | + }); |
| 3319 | + } |
| 3320 | + |
| 3321 | + }); |
| 3322 | + |
3173 | 3323 | flow(new NoRollbackFlow() { |
3174 | 3324 | String __name__ = "migrate-vm"; |
3175 | 3325 |
|
@@ -5372,8 +5522,9 @@ private void handle(SecretHostDefineMsg msg) { |
5372 | 5522 | bus.reply(msg, reply); |
5373 | 5523 | return; |
5374 | 5524 | } |
5375 | | - if (StringUtils.isBlank(msg.getVmUuid()) || StringUtils.isBlank(msg.getPurpose()) || msg.getKeyVersion() == null) { |
5376 | | - reply.setError(operr("vmUuid, purpose and keyVersion are required for ensure secret")); |
| 5525 | + if (StringUtils.isBlank(msg.getVmUuid()) || StringUtils.isBlank(msg.getPurpose()) |
| 5526 | + || msg.getKeyVersion() == null || StringUtils.isBlank(msg.getUsageInstance())) { |
| 5527 | + reply.setError(operr("vmUuid, purpose, keyVersion and usageInstance are required for ensure secret")); reply.setError(operr("vmUuid, purpose and keyVersion are required for ensure secret")); |
5377 | 5528 | bus.reply(msg, reply); |
5378 | 5529 | return; |
5379 | 5530 | } |
@@ -5452,6 +5603,7 @@ private void handle(SecretHostDefineMsg msg) { |
5452 | 5603 | cmd.setVmUuid(msg.getVmUuid()); |
5453 | 5604 | cmd.setPurpose(msg.getPurpose()); |
5454 | 5605 | cmd.setKeyVersion(msg.getKeyVersion()); |
| 5606 | + cmd.setSecretUuid(msg.getSecretUuid()); |
5455 | 5607 | cmd.setDescription(msg.getDescription() != null ? msg.getDescription() : ""); |
5456 | 5608 | cmd.setUsageInstance(KVMConstant.HOST_SECRET_USAGE_INSTANCE_VTPM); |
5457 | 5609 | Map<String, String> headers = new HashMap<>(); |
|
0 commit comments