Skip to content

feat(byok): enable customer-managed key for nodePool OS disk encryption - #6647

Open
Caden Marchese (cadenmarchese) wants to merge 1 commit into
Azure:mainfrom
cadenmarchese:cadenmarchese/pass-des-to-cluster-service
Open

feat(byok): enable customer-managed key for nodePool OS disk encryption#6647
Caden Marchese (cadenmarchese) wants to merge 1 commit into
Azure:mainfrom
cadenmarchese:cadenmarchese/pass-des-to-cluster-service

Conversation

@cadenmarchese

Copy link
Copy Markdown
Member

https://redhat.atlassian.net/browse/ARO-27738

What

  • This is a re-take of feat: enable customer-managed key for nodePool OS disk encryption #5682, which was merged and later reverted due to issues with the e2e test. The issue with the e2e test was that the explicit role assignment for CAPZ with scope of disk encryption set was missing, causing the machine creation to fail.
  • This change allows BYOK for nodePool OS disk encryption. Currently, since we don't pass the diskEncryptionSet ID to cluster service, all nodePool OS disks fall back to platform-managed keys, even if the intent was to configure it with a diskEncryptionSet.
  • This also unblocks mHSM support for nodePool OS Disk encryption, which functions in the same way.
  • This requires service managed identity as well as capz (or the mocks) to have the Reader role over the disk encryption set, otherwise e2e fails with the following:
: Nodepool OS Disk Encryption should create a nodepool with customer-managed disk encryption via DES expand_less23m40s{fail [github.com/Azure/ARO-HCP/test/e2e/nodepool_osdisk_encryption.go:134]: failed to create nodepool des-np with DES Unexpected error:     <*fmt.wrapError \| 0xc0015ba6c0>:      failed to create NodePool des-np: failed waiting for nodepool="des-np" for cluster "des-encrypt" in resourcegroup="des-encrypt-6jl8pq" to finish creating: GET https://rp.j6608000.hcpsvc.osadev.cloud/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/providers/Microsoft.RedHatOpenShift/locations/westus3/hcpOperationStatuses/c915d1eb-59bc-41f2-800e-47d6179fae28     --------------------------------------------------------------------------------     RESPONSE 200: 200 OK     ERROR CODE: InternalServerError     --------------------------------------------------------------------------------     {       "id": "/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/providers/Microsoft.RedHatOpenShift/locations/westus3/hcpOperationStatuses/c915d1eb-59bc-41f2-800e-47d6179fae28",       "name": "c915d1eb-59bc-41f2-800e-47d6179fae28",       "status": "Failed",       "startTime": "2026-07-16T22:00:30.305420763Z",       "endTime": "2026-07-16T22:00:45.224060261Z",       "error": {         "code": "InternalServerError",         "message": "[clusterServiceNodePoolStatus] GET https://management.azure.com/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/des-encrypt-6jl8pq/providers/Microsoft.Compute/diskEncryptionSets/des-encrypt-des-o3nbqnh7nwvje\n--------------------------------------------------------------------------------\nRESPONSE 403: 403 Forbidden\nERROR CODE: AuthorizationFailed\n--------------------------------------------------------------------------------\n{\n  \"error\": {\n    \"code\": \"AuthorizationFailed\",\n    \"message\": \"The client '33e65307-a696-491e-831f-42341ce3a9b5' with object id 'b26c2343-0863-4a55-bb7e-dfb2544999c1' does not have authorization to perform action 'Microsoft.Compute/diskEncryptionSets/read' over scope '/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/des-encrypt-6jl8pq/providers/Microsoft.Compute/diskEncryptionSets/des-encrypt-des-o3nbqnh7nwvje' or the scope is invalid. If access was recently granted, please refresh your credentials.\"\n  }\n}\n--------------------------------------------------------------------------------\n"       }     }     -------------------------------------------------------------------------------- | : Nodepool OS Disk Encryption should create a nodepool with customer-managed disk encryption via DES expand_less | 23m40s | {fail [github.com/Azure/ARO-HCP/test/e2e/nodepool_osdisk_encryption.go:134]: failed to create nodepool des-np with DES Unexpected error:     <*fmt.wrapError \| 0xc0015ba6c0>:      failed to create NodePool des-np: failed waiting for nodepool="des-np" for cluster "des-encrypt" in resourcegroup="des-encrypt-6jl8pq" to finish creating: GET https://rp.j6608000.hcpsvc.osadev.cloud/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/providers/Microsoft.RedHatOpenShift/locations/westus3/hcpOperationStatuses/c915d1eb-59bc-41f2-800e-47d6179fae28     --------------------------------------------------------------------------------     RESPONSE 200: 200 OK     ERROR CODE: InternalServerError     --------------------------------------------------------------------------------     {       "id": "/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/providers/Microsoft.RedHatOpenShift/locations/westus3/hcpOperationStatuses/c915d1eb-59bc-41f2-800e-47d6179fae28",       "name": "c915d1eb-59bc-41f2-800e-47d6179fae28",       "status": "Failed",       "startTime": "2026-07-16T22:00:30.305420763Z",       "endTime": "2026-07-16T22:00:45.224060261Z",       "error": {         "code": "InternalServerError",         "message": "[clusterServiceNodePoolStatus] GET https://management.azure.com/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/des-encrypt-6jl8pq/providers/Microsoft.Compute/diskEncryptionSets/des-encrypt-des-o3nbqnh7nwvje\n--------------------------------------------------------------------------------\nRESPONSE 403: 403 Forbidden\nERROR CODE: AuthorizationFailed\n--------------------------------------------------------------------------------\n{\n  \"error\": {\n    \"code\": \"AuthorizationFailed\",\n    \"message\": \"The client '33e65307-a696-491e-831f-42341ce3a9b5' with object id 'b26c2343-0863-4a55-bb7e-dfb2544999c1' does not have authorization to perform action 'Microsoft.Compute/diskEncryptionSets/read' over scope '/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/des-encrypt-6jl8pq/providers/Microsoft.Compute/diskEncryptionSets/des-encrypt-des-o3nbqnh7nwvje' or the scope is invalid. If access was recently granted, please refresh your credentials.\"\n  }\n}\n--------------------------------------------------------------------------------\n"       }     }     --------------------------------------------------------------------------------
: Nodepool OS Disk Encryption should create a nodepool with customer-managed disk encryption via DES expand_less | 23m40s
{fail [github.com/Azure/ARO-HCP/test/e2e/nodepool_osdisk_encryption.go:134]: failed to create nodepool des-np with DES Unexpected error:     <*fmt.wrapError \| 0xc0015ba6c0>:      failed to create NodePool des-np: failed waiting for nodepool="des-np" for cluster "des-encrypt" in resourcegroup="des-encrypt-6jl8pq" to finish creating: GET https://rp.j6608000.hcpsvc.osadev.cloud/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/providers/Microsoft.RedHatOpenShift/locations/westus3/hcpOperationStatuses/c915d1eb-59bc-41f2-800e-47d6179fae28     --------------------------------------------------------------------------------     RESPONSE 200: 200 OK     ERROR CODE: InternalServerError     --------------------------------------------------------------------------------     {       "id": "/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/providers/Microsoft.RedHatOpenShift/locations/westus3/hcpOperationStatuses/c915d1eb-59bc-41f2-800e-47d6179fae28",       "name": "c915d1eb-59bc-41f2-800e-47d6179fae28",       "status": "Failed",       "startTime": "2026-07-16T22:00:30.305420763Z",       "endTime": "2026-07-16T22:00:45.224060261Z",       "error": {         "code": "InternalServerError",         "message": "[clusterServiceNodePoolStatus] GET https://management.azure.com/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/des-encrypt-6jl8pq/providers/Microsoft.Compute/diskEncryptionSets/des-encrypt-des-o3nbqnh7nwvje\n--------------------------------------------------------------------------------\nRESPONSE 403: 403 Forbidden\nERROR CODE: AuthorizationFailed\n--------------------------------------------------------------------------------\n{\n  \"error\": {\n    \"code\": \"AuthorizationFailed\",\n    \"message\": \"The client '33e65307-a696-491e-831f-42341ce3a9b5' with object id 'b26c2343-0863-4a55-bb7e-dfb2544999c1' does not have authorization to perform action 'Microsoft.Compute/diskEncryptionSets/read' over scope '/subscriptions/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/resourceGroups/des-encrypt-6jl8pq/providers/Microsoft.Compute/diskEncryptionSets/des-encrypt-des-o3nbqnh7nwvje' or the scope is invalid. If access was recently granted, please refresh your credentials.\"\n  }\n}\n--------------------------------------------------------------------------------\n"       }     }     --------------------------------------------------------------------------------
  • Additionally, BYOK in Azure requires the keyvault to have soft delete and purge protection enabled. This means that the e2e test needs to accommodate that, and the purge/sweep logic will not attempt to clean the keyvault for this specific e2e test up. Instead, it will be cleaned up automatically by Azure in 7 days, which is the minimum interval for purge protected keyvaults.

Why

This is required to get BYOK for nodePool OS Disk encryption working, both for regular keyvaults, and mHSM keyvaults. Today the HCP API will accept a disk encryption set but won't use it for the actual encryption, which is unexpected behavior.

Testing

Special notes for your reviewer

PR Checklist

  • PR is scoped to a single task (no mixed concerns)
  • Title follows Conventional Commits format
  • Summary explains the "Why" behind the change
  • Linked to relevant ticket/issue
  • Screenshots included (if graph/UI/metrics changes)
  • Self-reviewed the diff
  • CI/CD checks are passing (ignore Tide)
  • Draft PR used for WIP (if applicable)
  • Commit history is clean (rebased/squashed)
  • Tricky code blocks are commented
  • Specific reviewers tagged
  • All comment threads resolved before merge

feat: add e2e test for BYOK OS Disks

use Go SDK rather than bicep templates for des e2e test

revert cleanup removals, switch to errors.join approach

fix(e2e): skip purge protected keyvaults on test cleanup

also, delete role assignments before cleaning up the resource
group. This is required because the BYOK OS Disk e2e test requires
a non-purgeable keyvault already configured to automatically
delete in 7 days

assign reader role to both CAPZ and SMI with scope of DES
Copilot AI lite review requested due to automatic review settings August 21, 2026 13:35
@openshift-ci

openshift-ci Bot commented Aug 21, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: cadenmarchese
Once this PR has been reviewed and has the lgtm label, please assign deads2k for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables customer-managed keys (BYOK) for NodePool OS disk encryption by wiring the disk encryption set (DES) resource ID through the RP→Cluster Service conversion path, and adds an E2E test that provisions a Key Vault–backed DES and verifies worker OS disks are encrypted with it.

Changes:

  • Pass NodePool OS disk encryptionSetId through internal/ocm conversion into the Cluster Service node pool model.
  • Add E2E coverage that creates a Key Vault + DES, assigns required Reader/KV roles, creates an encrypted nodepool, and verifies disk encryption via ARM.
  • Update test framework cleanup to handle DES-related role assignments and to skip purging purge-protected soft-deleted Key Vaults.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/util/verifiers/osdisk_encryption.go New verifier that checks VM OS disks use customer-managed encryption and match the expected DES ID.
test/util/framework/per_test_framework.go Cleanup enhancements: delete tracked role assignments before RG deletion; skip purging purge-protected deleted Key Vaults.
test/util/framework/per_test_framework_test.go Unit tests for the new keyVaultPurgeProtected helper.
test/util/framework/helpers_v20260901preview.go Adds EncryptionSetID param and sets nodepool OSDisk.EncryptionSetID when provided.
test/util/framework/helpers_v20260630preview.go Same as above for the 20260630 preview helpers.
test/util/framework/helpers_v20251223preview.go Same as above for the 20251223 preview helpers.
test/util/framework/helpers_v20240610preview.go Same as above for the 20240610 preview helpers.
test/util/framework/disk_encryption_set_helper.go New helper to create a Key Vault–backed DES and assign KV crypto + DES Reader roles to required principals.
test/e2e/nodepool_osdisk_encryption.go New E2E test validating nodepool OS disk encryption via DES (BYOK).
test/e2e-setup/bicep/modules/customer-infra.bicep Adds Key Vault soft delete + purge protection configuration parameters for BYOK scenarios.
internal/ocm/convert.go Sets Cluster Service SseEncryptionSetResourceId from RP nodepool OSDisk.EncryptionSetID.
internal/ocm/convert_test.go Adds unit coverage for passing DES ID (and not setting it when nil).
test/testdata/zz_fixture_TestMainListSuitesForEachSuite_stage_parallelstage_parallel.txt Registers the new E2E in the stage parallel suite fixture.
test/testdata/zz_fixture_TestMainListSuitesForEachSuite_rp_api_compat_all_parallelrp_api_compat_all_parallel.txt Registers the new E2E in rp_api_compat_all_parallel suite fixture.
test/testdata/zz_fixture_TestMainListSuitesForEachSuite_rp_api_compat_all_parallel_01rp_api_compat_all_parallel_development.txt Registers the new E2E in rp_api_compat_all_parallel development suite fixture.
test/testdata/zz_fixture_TestMainListSuitesForEachSuite_prod_parallelprod_parallel.txt Registers the new E2E in the prod parallel suite fixture.
test/testdata/zz_fixture_TestMainListSuitesForEachSuite_integration_parallelintegration_parallel.txt Registers the new E2E in the integration parallel suite fixture.
test/testdata/zz_fixture_TestMainListSuitesForEachSuite_dev_cd_check_paralleldev_cd_check_parallel.txt Registers the new E2E in the dev CD check parallel suite fixture.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +111 to +113
enableSoftDelete: enableKeyVaultSoftDelete ? true : null
enablePurgeProtection: enableKeyVaultPurgeProtection ? true : null
softDeleteRetentionInDays: enableKeyVaultSoftDelete ? keyVaultSoftDeleteRetentionInDays : null
@cadenmarchese

Copy link
Copy Markdown
Member Author

/test integration-e2e-parallel

Comment on lines +38 to +39
labels.AroRpApiCompatible,
labels.MIContainers(1),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
labels.AroRpApiCompatible,
labels.MIContainers(1),
labels.AroRpApiCompatible,
labels.IntegrationOnly,
labels.MIContainers(1),

This should still fail in Staging and Production, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should pass in all envs, including stage and prod, but I'm not able to confirm in INT because cluster identities in INT use the aro-hcp-int-msi-mock service principal, which grants everything diskEncryptionSets/read over the entire subscription (see role assignments, and the role definition, which gets permission over the whole sub)

In stage/prod, each identity gets its built-in role with minimally scoped permissions. Is there a way to run this e2e test in prod before merge to validate the fix?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mvacula02 I don't believe /test stage-e2e-parallel prod-e2e-parallel will work, since the e2e test here depends on the ocm/internal changes being deployed there. Steve suggested these steps:

  1. add code to the RP to handle the new feature, behind an AFEC flag, such that it can be deployed to all envs without impacting production
  2. deploy RP
  3. write a test to use it, since the test subs (and only the test subs) have the AFEC
  4. once validated, remove requirement on afec and allow users

We may already have an existing AFEC flag we can use to deploy the RP changes safely, and then repurpose this PR to just be for the test later. I'll look into it, if that all sounds OK.

@mvacula02

Copy link
Copy Markdown
Collaborator

/test stage-e2e-parallel prod-e2e-parallel

@cadenmarchese

Copy link
Copy Markdown
Member Author

/retest-required

@openshift-ci

openshift-ci Bot commented Aug 21, 2026

Copy link
Copy Markdown

Caden Marchese (@cadenmarchese): The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/integration-e2e-parallel 265e81c link false /test integration-e2e-parallel
ci/prow/stage-e2e-parallel 265e81c link false /test stage-e2e-parallel
ci/prow/prod-e2e-parallel 265e81c link false /test prod-e2e-parallel
ci/prow/e2e-parallel 265e81c link true /test e2e-parallel

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@cadenmarchese

Copy link
Copy Markdown
Member Author

/hold

Per conversations about the e2e test, and since it previously caused issues during release, I've been recommended a phased approach here:

  1. Merge just the feature, behind the AFEC flag used by CI (feat(byok): pass diskEncryptionSet ID to CS if AFEC is registered #6662)
  2. Once merged in all envs, scope this PR to the e2e test only, and run /test stage-e2e-parallel prod-e2e-parallel to confirm the e2e test passes in all envs

This is required since workload identities are mocked in dev, CI and INT, making the e2e test (which scopes disk encryption set role assignments to specific identities) not useful in dev, CI and INT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants