forked from zstackio/zstack
-
Notifications
You must be signed in to change notification settings - Fork 0
<feature>[storage]: support encrypted scsi lun #4309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ZStack-Robot
wants to merge
1
commit into
feature-zsv-5.1.0-encryption
Choose a base branch
from
sync/zstackio/codex/encrypted-rdm
base: feature-zsv-5.1.0-encryption
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,26 +82,31 @@ public class VolumeEncryptedSecretHelper { | |
| private PluginRegistry pluginRegistry; | ||
|
|
||
| public EncryptedResourceKeyManager.ResourceKeyResult materializeDek(String volUuid, String kpUuid) { | ||
| return materializeResourceDek(volUuid, VolumeVO.class.getSimpleName(), kpUuid, "instantiate-volume"); | ||
| } | ||
|
|
||
| public EncryptedResourceKeyManager.ResourceKeyResult materializeResourceDek(String resourceUuid, String resourceType, | ||
| String kpUuid, String purpose) { | ||
| EncryptedResourceKeyManager.GetOrCreateResourceKeyContext ctx = | ||
| new EncryptedResourceKeyManager.GetOrCreateResourceKeyContext(); | ||
| ctx.setResourceUuid(volUuid); | ||
| ctx.setResourceType(VolumeVO.class.getSimpleName()); | ||
| ctx.setResourceUuid(resourceUuid); | ||
| ctx.setResourceType(resourceType); | ||
| ctx.setKeyProviderUuid(kpUuid); | ||
| ctx.setPurpose("instantiate-volume"); | ||
| ctx.setPurpose(purpose); | ||
|
|
||
| FutureReturnValueCompletion completion = new FutureReturnValueCompletion(null); | ||
| encryptedResourceKeyManager.getOrCreateKey(ctx, completion); | ||
| completion.await(TimeUnit.MINUTES.toMillis(5)); | ||
| if (!completion.isSuccess()) { | ||
| throw new OperationFailureException(operr( | ||
| "failed to materialize encryption key for volume[uuid:%s]", volUuid) | ||
| "failed to materialize encryption key for resource[type:%s, uuid:%s]", resourceType, resourceUuid) | ||
| .withCause(completion.getErrorCode())); | ||
| } | ||
|
|
||
| EncryptedResourceKeyManager.ResourceKeyResult result = completion.getResult(); | ||
| if (result == null || StringUtils.isBlank(result.getDekBase64())) { | ||
| throw new OperationFailureException(operr( | ||
| "key manager returned empty DEK for encrypted volume[uuid:%s]", volUuid)); | ||
| "key manager returned empty DEK for encrypted resource[type:%s, uuid:%s]", resourceType, resourceUuid)); | ||
| } | ||
| return result; | ||
| } | ||
|
|
@@ -257,55 +262,67 @@ public String defineLibvirtSecretOnHost(String hostUuid, String vmUuid, String v | |
|
|
||
| public String defineLibvirtSecretOnHost(String hostUuid, String vmUuid, String volUuid, | ||
| String dekBase64, Integer keyVersion, String secretUuid) { | ||
| if (StringUtils.isBlank(hostUuid) || StringUtils.isBlank(volUuid) || | ||
| StringUtils.isBlank(dekBase64) || keyVersion == null) { | ||
| String description = String.format("LUKS DEK for volume %s", volUuid); | ||
| String usageInstance = KVMConstant.volumeSecretUsageInstance(volUuid); | ||
| String result = defineLibvirtSecretForResourceOnHost(hostUuid, vmUuid, volUuid, dekBase64, | ||
| keyVersion, secretUuid, "volume", usageInstance, description); | ||
|
|
||
| // Remember which host now owns this volume's libvirt secret so that | ||
| // expunge can clean it up later, even if the owning VM is gone by then. | ||
| // recreate=true overwrites any stale tag from a previous host. | ||
| try { | ||
| SystemTagCreator tc = VolumeSystemTags.VOLUME_LIBVIRT_SECRET_HOST.newSystemTagCreator(volUuid); | ||
| tc.setTagByTokens(Collections.singletonMap( | ||
| VolumeSystemTags.VOLUME_LIBVIRT_SECRET_HOST_TOKEN, hostUuid)); | ||
| tc.inherent = false; | ||
| tc.recreate = true; | ||
| tc.create(); | ||
| } catch (RuntimeException tagEx) { | ||
| // Tag write failure must not break the actual secret define -- the | ||
| // define already succeeded, the tag is for cleanup bookkeeping only. | ||
| logger.warn(String.format( | ||
| "failed to stamp VOLUME_LIBVIRT_SECRET_HOST tag on volume[uuid:%s] for host[uuid:%s]: %s", | ||
| volUuid, hostUuid, tagEx.getMessage())); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public String defineLibvirtSecretForResourceOnHost(String hostUuid, String vmUuid, String resourceUuid, | ||
| String dekBase64, Integer keyVersion, String secretUuid, | ||
| String purpose, String usageInstance, String description) { | ||
| if (StringUtils.isBlank(hostUuid) || StringUtils.isBlank(resourceUuid) || | ||
| StringUtils.isBlank(dekBase64) || keyVersion == null || | ||
| StringUtils.isBlank(purpose) || StringUtils.isBlank(usageInstance)) { | ||
| throw new OperationFailureException(operr( | ||
| "defineLibvirtSecretOnHost requires non-blank hostUuid, volUuid, dekBase64 and a non-null keyVersion")); | ||
| "defineLibvirtSecretForResourceOnHost requires non-blank hostUuid, resourceUuid, dekBase64, purpose, usageInstance and a non-null keyVersion")); | ||
| } | ||
| SecretHostDefineMsg defineMsg = new SecretHostDefineMsg(); | ||
| defineMsg.setHostUuid(hostUuid); | ||
| defineMsg.setVmUuid(vmUuid); | ||
| defineMsg.setDekBase64(dekBase64); | ||
| defineMsg.setPurpose("volume"); | ||
| defineMsg.setPurpose(purpose); | ||
| defineMsg.setKeyVersion(keyVersion); | ||
| defineMsg.setUsageInstance(KVMConstant.volumeSecretUsageInstance(volUuid)); | ||
| defineMsg.setUsageInstance(usageInstance); | ||
| if (StringUtils.isNotBlank(secretUuid)) { | ||
| defineMsg.setSecretUuid(secretUuid); | ||
| } | ||
| defineMsg.setDescription(String.format("LUKS DEK for volume %s", volUuid)); | ||
| defineMsg.setDescription(description); | ||
| bus.makeTargetServiceIdByResourceUuid(defineMsg, HostConstant.SERVICE_ID, hostUuid); | ||
|
|
||
| MessageReply reply = bus.call(defineMsg); | ||
| if (!reply.isSuccess()) { | ||
| throw new OperationFailureException(operr( | ||
| "failed to ensure libvirt secret for encrypted volume[uuid:%s] on host[uuid:%s]", | ||
| volUuid, hostUuid).withCause(reply.getError())); | ||
| "failed to ensure libvirt secret for encrypted resource[uuid:%s] on host[uuid:%s]", | ||
| resourceUuid, hostUuid).withCause(reply.getError())); | ||
| } | ||
| SecretHostDefineReply r = reply.castReply(); | ||
| if (StringUtils.isBlank(r.getSecretUuid())) { | ||
| throw new OperationFailureException(operr( | ||
| "ensure volume LUKS secret on host succeeded but secretUuid is empty, host[uuid:%s]", | ||
| "ensure resource LUKS secret on host succeeded but secretUuid is empty, host[uuid:%s]", | ||
| hostUuid)); | ||
| } | ||
|
|
||
| // Remember which host now owns this volume's libvirt secret so that | ||
| // expunge can clean it up later, even if the owning VM is gone by then. | ||
| // recreate=true overwrites any stale tag from a previous host. | ||
| try { | ||
| SystemTagCreator tc = VolumeSystemTags.VOLUME_LIBVIRT_SECRET_HOST.newSystemTagCreator(volUuid); | ||
| tc.setTagByTokens(Collections.singletonMap( | ||
| VolumeSystemTags.VOLUME_LIBVIRT_SECRET_HOST_TOKEN, hostUuid)); | ||
| tc.inherent = false; | ||
| tc.recreate = true; | ||
| tc.create(); | ||
| } catch (RuntimeException tagEx) { | ||
| // Tag write failure must not break the actual secret define -- the | ||
| // define already succeeded, the tag is for cleanup bookkeeping only. | ||
| logger.warn(String.format( | ||
| "failed to stamp VOLUME_LIBVIRT_SECRET_HOST tag on volume[uuid:%s] for host[uuid:%s]: %s", | ||
| volUuid, hostUuid, tagEx.getMessage())); | ||
| } | ||
|
|
||
| return r.getSecretUuid(); | ||
| } | ||
|
|
||
|
|
@@ -327,19 +344,29 @@ public String defineSecretFromBinding(String hostUuid, String vmUuid, String vol | |
| } | ||
|
|
||
| public String defineSecretFromBinding(String hostUuid, String vmUuid, String volUuid, String kpUuid, String secretUuid) { | ||
| return defineSecretFromBindingForResource(hostUuid, vmUuid, volUuid, VolumeVO.class.getSimpleName(), kpUuid, | ||
| "volume", KVMConstant.volumeSecretUsageInstance(volUuid), secretUuid); | ||
| } | ||
|
|
||
| public String defineSecretFromBindingForResource(String hostUuid, String vmUuid, String resourceUuid, | ||
| String resourceType, String kpUuid, String secretPurpose, | ||
| String usageInstance, String secretUuid) { | ||
| if (StringUtils.isBlank(kpUuid)) { | ||
| throw new OperationFailureException(operr( | ||
| "encrypted volume[uuid:%s] has no key provider binding; cannot define libvirt secret on host[uuid:%s]", | ||
| volUuid, hostUuid)); | ||
| "encrypted resource[type:%s, uuid:%s] has no key provider binding; cannot define libvirt secret on host[uuid:%s]", | ||
| resourceType, resourceUuid, hostUuid)); | ||
| } | ||
| EncryptedResourceKeyManager.ResourceKeyResult keyResult = materializeDek(volUuid, kpUuid); | ||
| EncryptedResourceKeyManager.ResourceKeyResult keyResult = materializeResourceDek(resourceUuid, resourceType, | ||
| kpUuid, "define-" + secretPurpose + "-secret"); | ||
| String dekBase64 = keyResult.getDekBase64(); | ||
| if (StringUtils.isBlank(dekBase64)) { | ||
| throw new OperationFailureException(operr( | ||
| "encrypted volume[uuid:%s]: key manager returned empty DEK for libvirt secret", | ||
| volUuid)); | ||
| "encrypted resource[type:%s, uuid:%s]: key manager returned empty DEK for libvirt secret", | ||
| resourceType, resourceUuid)); | ||
| } | ||
| return defineLibvirtSecretOnHost(hostUuid, vmUuid, volUuid, dekBase64, keyResult.getKeyVersion(), secretUuid); | ||
| String description = String.format("LUKS DEK for %s %s", resourceType, resourceUuid); | ||
| return defineLibvirtSecretForResourceOnHost(hostUuid, vmUuid, resourceUuid, dekBase64, | ||
| keyResult.getKeyVersion(), secretUuid, secretPurpose, usageInstance, description); | ||
| } | ||
|
Comment on lines
+351
to
370
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 在参数合法性未确认前就物化 DEK,存在副作用顺序问题 Line 359 在校验 🔧 建议修复 public String defineSecretFromBindingForResource(String hostUuid, String vmUuid, String resourceUuid,
String resourceType, String kpUuid, String secretPurpose,
String usageInstance, String secretUuid) {
+ if (StringUtils.isBlank(hostUuid) || StringUtils.isBlank(resourceUuid)
+ || StringUtils.isBlank(resourceType)
+ || StringUtils.isBlank(secretPurpose)
+ || StringUtils.isBlank(usageInstance)) {
+ throw new OperationFailureException(operr(
+ "defineSecretFromBindingForResource requires non-blank hostUuid, resourceUuid, resourceType, secretPurpose and usageInstance"));
+ }
if (StringUtils.isBlank(kpUuid)) {
throw new OperationFailureException(operr(
"encrypted resource[type:%s, uuid:%s] has no key provider binding; cannot define libvirt secret on host[uuid:%s]",
resourceType, resourceUuid, hostUuid));
}🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
|
|
@@ -350,12 +377,18 @@ public String defineSecretFromBinding(String hostUuid, String vmUuid, String vol | |
| * contract, but key-agent does not include it in volume secret usage names. | ||
| */ | ||
| public String getSecretOnHost(String hostUuid, String vmUuid, String volUuid, Integer keyVersion) { | ||
| return getSecretForResourceOnHost(hostUuid, vmUuid, volUuid, keyVersion, | ||
| "volume", KVMConstant.volumeSecretUsageInstance(volUuid)); | ||
| } | ||
|
|
||
| public String getSecretForResourceOnHost(String hostUuid, String vmUuid, String resourceUuid, Integer keyVersion, | ||
| String purpose, String usageInstance) { | ||
| SecretHostGetMsg msg = new SecretHostGetMsg(); | ||
| msg.setHostUuid(hostUuid); | ||
| msg.setVmUuid(vmUuid); | ||
| msg.setPurpose("volume"); | ||
| msg.setPurpose(purpose); | ||
| msg.setKeyVersion(keyVersion); | ||
| msg.setUsageInstance(KVMConstant.volumeSecretUsageInstance(volUuid)); | ||
| msg.setUsageInstance(usageInstance); | ||
| bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, hostUuid); | ||
|
|
||
| MessageReply reply = bus.call(msg); | ||
|
|
@@ -368,28 +401,35 @@ public String getSecretOnHost(String hostUuid, String vmUuid, String volUuid, In | |
| return null; | ||
| } | ||
| throw new OperationFailureException(operr( | ||
| "failed to get libvirt LUKS secret on host[uuid:%s] vm[uuid:%s] volume[uuid:%s] keyVersion[%s]: %s", | ||
| hostUuid, vmUuid, volUuid, keyVersion, err)); | ||
| "failed to get libvirt LUKS secret on host[uuid:%s] vm[uuid:%s] resource[uuid:%s] keyVersion[%s]: %s", | ||
| hostUuid, vmUuid, resourceUuid, keyVersion, err)); | ||
| } | ||
|
|
||
| public void deleteSecretOnHostBestEffort(String hostUuid, String vmUuid, String volUuid, Integer keyVersion) { | ||
| deleteSecretForResourceOnHostBestEffort(hostUuid, vmUuid, volUuid, keyVersion, | ||
| "volume", KVMConstant.volumeSecretUsageInstance(volUuid)); | ||
| } | ||
|
|
||
| public void deleteSecretForResourceOnHostBestEffort(String hostUuid, String vmUuid, String resourceUuid, | ||
| Integer keyVersion, String purpose, String usageInstance) { | ||
| if (StringUtils.isBlank(hostUuid) || StringUtils.isBlank(vmUuid) | ||
| || StringUtils.isBlank(volUuid) || keyVersion == null) { | ||
| || StringUtils.isBlank(resourceUuid) || keyVersion == null | ||
| || StringUtils.isBlank(purpose) || StringUtils.isBlank(usageInstance)) { | ||
| return; | ||
| } | ||
| SecretHostDeleteMsg msg = new SecretHostDeleteMsg(); | ||
| msg.setHostUuid(hostUuid); | ||
| msg.setVmUuid(vmUuid); | ||
| msg.setPurpose("volume"); | ||
| msg.setPurpose(purpose); | ||
| msg.setKeyVersion(keyVersion); | ||
| msg.setUsageInstance(KVMConstant.volumeSecretUsageInstance(volUuid)); | ||
| msg.setUsageInstance(usageInstance); | ||
| bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, hostUuid); | ||
|
|
||
| MessageReply reply = bus.call(msg); | ||
| if (!reply.isSuccess()) { | ||
| logger.warn(String.format( | ||
| "best-effort delete libvirt LUKS secret failed for volume[uuid:%s] on host[uuid:%s] vm[uuid:%s]: %s", | ||
| volUuid, hostUuid, vmUuid, reply.getError())); | ||
| "best-effort delete libvirt LUKS secret failed for resource[uuid:%s] on host[uuid:%s] vm[uuid:%s]: %s", | ||
| resourceUuid, hostUuid, vmUuid, reply.getError())); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -414,6 +454,23 @@ public String resolveOrDefineSecretForVolume(String hostUuid, String vmUuid, Str | |
| return defineSecretFromBinding(hostUuid, vmUuid, volUuid, kpUuid); | ||
| } | ||
|
|
||
| public String resolveOrDefineSecretForResource(String hostUuid, String vmUuid, String resourceUuid, | ||
| String resourceType, String kpUuid, Integer keyVersion, | ||
| String secretPurpose, String usageInstance) { | ||
| if (keyVersion == null) { | ||
| throw new OperationFailureException(operr( | ||
| "encrypted resource[type:%s, uuid:%s] has no key version bound; cannot resolve libvirt LUKS secret on host[uuid:%s] for vm[uuid:%s]", | ||
| resourceType, resourceUuid, hostUuid, vmUuid)); | ||
| } | ||
| String secretUuid = getSecretForResourceOnHost(hostUuid, vmUuid, resourceUuid, keyVersion, | ||
| secretPurpose, usageInstance); | ||
| if (StringUtils.isNotBlank(secretUuid)) { | ||
| return secretUuid; | ||
| } | ||
| return defineSecretFromBindingForResource(hostUuid, vmUuid, resourceUuid, resourceType, kpUuid, | ||
| secretPurpose, usageInstance, null); | ||
| } | ||
|
|
||
| private String resolveVolumeLibvirtSecretUuidFromDomainXml(String hostUuid, String vmUuid, String volUuid) { | ||
| KVMAgentCommands.ResolveVolumeLibvirtSecretCmd cmd = new KVMAgentCommands.ResolveVolumeLibvirtSecretCmd(); | ||
| cmd.setVmUuid(vmUuid); | ||
|
|
@@ -484,4 +541,37 @@ public String resolveOrDefineSecretForVolumeMigration(String srcHostUuid, String | |
| String kpUuid = volumeEncryptedResourceKeyBackend.findKeyProviderUuidByVolume(volUuid); | ||
| return defineSecretFromBinding(dstHostUuid, vmUuid, volUuid, kpUuid, sourceSecretUuid); | ||
| } | ||
|
|
||
| public String resolveOrDefineSecretForResourceMigration(String srcHostUuid, String dstHostUuid, String vmUuid, | ||
| String resourceUuid, String resourceType, String kpUuid, | ||
| Integer keyVersion, String secretPurpose, | ||
| String usageInstance) { | ||
| if (StringUtils.isBlank(srcHostUuid) || StringUtils.isBlank(dstHostUuid) | ||
| || StringUtils.isBlank(vmUuid) || StringUtils.isBlank(resourceUuid)) { | ||
| throw new OperationFailureException(operr( | ||
| "resolve migration LUKS secret requires non-blank srcHostUuid, dstHostUuid, vmUuid and resourceUuid")); | ||
| } | ||
|
|
||
| if (keyVersion == null) { | ||
| throw new OperationFailureException(operr( | ||
| "encrypted resource[type:%s, uuid:%s] has no key version bound; cannot resolve migration LUKS secret for vm[uuid:%s]", | ||
| resourceType, resourceUuid, vmUuid)); | ||
| } | ||
|
|
||
| String sourceSecretUuid = getSecretForResourceOnHost(srcHostUuid, vmUuid, resourceUuid, keyVersion, | ||
| secretPurpose, usageInstance); | ||
| if (StringUtils.isBlank(sourceSecretUuid)) { | ||
| sourceSecretUuid = defineSecretFromBindingForResource(srcHostUuid, vmUuid, resourceUuid, resourceType, | ||
| kpUuid, secretPurpose, usageInstance, null); | ||
| } | ||
|
|
||
| String destSecretUuid = getSecretForResourceOnHost(dstHostUuid, vmUuid, resourceUuid, keyVersion, | ||
| secretPurpose, usageInstance); | ||
| if (StringUtils.equals(destSecretUuid, sourceSecretUuid)) { | ||
| return destSecretUuid; | ||
| } | ||
|
|
||
| return defineSecretFromBindingForResource(dstHostUuid, vmUuid, resourceUuid, resourceType, | ||
| kpUuid, secretPurpose, usageInstance, sourceSecretUuid); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOT NULL列缺少显式历史数据处理步骤(升级风险)Line 8 和 Line 9 直接新增
NOT NULL列,但当前迁移未体现“先处理历史数据再收紧约束”的步骤。按仓库 SQL 规范,这里需要通过存储过程或函数(或等价回填步骤)显式处理历史数据,再改为NOT NULL。🔧 建议迁移方式(示例)
As per coding guidelines: "When
NOT NULLexists, must usestored procedureor other functions to process historical data, this is very very important".🤖 Prompt for AI Agents
Source: Coding guidelines