Skip to content

Commit e703cd6

Browse files
committed
[AKS] Implement platform-managed-keys (PMK) awared validation for KMS customer-managed-key (CMK)
1 parent c58fd97 commit e703cd6

File tree

5 files changed

+2038
-14
lines changed

5 files changed

+2038
-14
lines changed

src/aks-preview/azext_aks_preview/_validators.py

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
RequiredArgumentMissingError)
3232
from azure.cli.core.commands.validators import validate_tag
3333
from azure.cli.core.util import CLIError
34-
from azure.mgmt.core.tools import is_valid_resource_id
34+
from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id
3535
from knack.log import get_logger
3636

3737
logger = get_logger(__name__)
@@ -701,26 +701,94 @@ def validate_crg_id(namespace):
701701
def validate_azure_keyvault_kms_key_id(namespace):
702702
key_id = namespace.azure_keyvault_kms_key_id
703703
if key_id:
704-
err_msg = (
705-
"--azure-keyvault-kms-key-id is not a valid Key Vault key ID. "
706-
"See https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name" # pylint: disable=line-too-long
704+
# Check if PMK (Platform-Managed Keys) is enabled
705+
is_pmk_enabled = (
706+
hasattr(namespace, 'kms_infrastructure_encryption') and
707+
namespace.kms_infrastructure_encryption == "Enabled"
707708
)
708709

709710
https_prefix = "https://"
710711
if not key_id.startswith(https_prefix):
712+
err_msg = (
713+
"--azure-keyvault-kms-key-id is not a valid Key Vault key ID. "
714+
"See https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name" # pylint: disable=line-too-long
715+
)
711716
raise InvalidArgumentValueError(err_msg)
712717

713718
segments = key_id[len(https_prefix):].split("/")
714-
if len(segments) != 4 or segments[1] != "keys":
715-
raise InvalidArgumentValueError(err_msg)
719+
720+
if is_pmk_enabled:
721+
# PMK enabled (K2P): Only accept versionless key ID (3 segments: vault.net/keys/key-name)
722+
if len(segments) != 3 or segments[1] != "keys":
723+
err_msg = (
724+
"--azure-keyvault-kms-key-id is not a valid versionless Key Vault key ID for PMK. "
725+
"Valid format is https://{key-vault-url}/keys/{key-name}. "
726+
"See https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name" # pylint: disable=line-too-long
727+
)
728+
raise InvalidArgumentValueError(err_msg)
729+
else:
730+
# PMK disabled (KMS v2): Accept versioned key ID (4 segments)
731+
if len(segments) != 4 or segments[1] != "keys":
732+
err_msg = (
733+
"--azure-keyvault-kms-key-id is not a valid Key Vault key ID. "
734+
"See https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name" # pylint: disable=line-too-long
735+
)
736+
raise InvalidArgumentValueError(err_msg)
716737

717738

718739
def validate_azure_keyvault_kms_key_vault_resource_id(namespace):
719740
key_vault_resource_id = namespace.azure_keyvault_kms_key_vault_resource_id
741+
742+
# Check if PMK (Platform-Managed Keys) is enabled
743+
is_pmk_enabled = (
744+
hasattr(namespace, 'kms_infrastructure_encryption') and
745+
namespace.kms_infrastructure_encryption == "Enabled"
746+
)
747+
720748
if key_vault_resource_id is None or key_vault_resource_id == '':
721-
return
749+
if is_pmk_enabled:
750+
raise RequiredArgumentMissingError(
751+
"--azure-keyvault-kms-key-vault-resource-id is required when "
752+
"--kms-infrastructure-encryption is set to Enabled (PMK)."
753+
)
754+
else:
755+
return
756+
757+
is_cmk_enabled = (
758+
hasattr(namespace, 'enable_azure_keyvault_kms') and
759+
namespace.enable_azure_keyvault_kms
760+
)
761+
if not is_cmk_enabled:
762+
raise RequiredArgumentMissingError(
763+
'"--azure-keyvault-kms-key-vault-resource-id" requires "--enable-azure-keyvault-kms".'
764+
)
765+
722766
if not is_valid_resource_id(key_vault_resource_id):
723-
raise InvalidArgumentValueError("--azure-keyvault-kms-key-vault-resource-id is not a valid Azure resource ID.")
767+
raise InvalidArgumentValueError(
768+
"--azure-keyvault-kms-key-vault-resource-id is not a valid Azure resource ID."
769+
)
770+
771+
try:
772+
parsed = parse_resource_id(key_vault_resource_id)
773+
provider = parsed.get('namespace', '').lower()
774+
if provider != 'microsoft.keyvault':
775+
raise InvalidArgumentValueError(
776+
"--azure-keyvault-kms-key-vault-resource-id must reference a "
777+
"Microsoft.KeyVault resource."
778+
)
779+
resource_type = parsed.get('type', '').lower()
780+
if resource_type not in ['vaults', 'managedhsms']:
781+
raise InvalidArgumentValueError(
782+
"--azure-keyvault-kms-key-vault-resource-id must reference a Key Vault "
783+
"(vaults) or Managed HSM (managedHSMs)."
784+
)
785+
except InvalidArgumentValueError:
786+
# Re-raise our validation errors
787+
raise
788+
except Exception as ex:
789+
raise InvalidArgumentValueError(
790+
f"--azure-keyvault-kms-key-vault-resource-id parsing failed: {str(ex)}"
791+
)
724792

725793

726794
def validate_bootstrap_container_registry_resource_id(namespace):

src/aks-preview/azext_aks_preview/managed_cluster_decorator.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,29 @@ def get_kms_infrastructure_encryption(self) -> str:
12881288

12891289
return kms_infrastructure_encryption
12901290

1291+
def get_azure_keyvault_kms_key_vault_resource_id(self) -> Union[str, None]:
1292+
"""Obtain the value of azure_keyvault_kms_key_vault_resource_id.
1293+
1294+
:return: bool
1295+
"""
1296+
# read the original value passed by the command
1297+
azure_keyvault_kms_key_vault_resource_id = self.raw_param.get(
1298+
"azure_keyvault_kms_key_vault_resource_id"
1299+
)
1300+
if self.decorator_mode == DecoratorMode.CREATE:
1301+
if (
1302+
self.mc and
1303+
hasattr(self.mc, "security_profile") and # backward compatibility
1304+
self.mc.security_profile and
1305+
self.mc.security_profile.azure_key_vault_kms and
1306+
self.mc.security_profile.azure_key_vault_kms.key_vault_resource_id is not None
1307+
):
1308+
azure_keyvault_kms_key_vault_resource_id = (
1309+
self.mc.security_profile.azure_key_vault_kms.key_vault_resource_id
1310+
)
1311+
1312+
return azure_keyvault_kms_key_vault_resource_id
1313+
12911314
def get_cluster_snapshot_id(self) -> Union[str, None]:
12921315
"""Obtain the values of cluster_snapshot_id.
12931316
@@ -3313,8 +3336,8 @@ def set_up_image_integrity(self, mc: ManagedCluster) -> ManagedCluster:
33133336

33143337
return mc
33153338

3316-
def set_up_kms_infrastructure_encryption(self, mc: ManagedCluster) -> ManagedCluster:
3317-
"""Set up security profile KubernetesResourceObjectEncryptionProfile for the ManagedCluster object.
3339+
def set_up_kms_pmk_and_cmk(self, mc: ManagedCluster) -> ManagedCluster:
3340+
"""Set up security profile KubernetesResourceObjectEncryptionProfile and AzureKeyVaultKms for the ManagedCluster object.
33183341
33193342
:return: the ManagedCluster object
33203343
"""
@@ -3335,6 +3358,17 @@ def set_up_kms_infrastructure_encryption(self, mc: ManagedCluster) -> ManagedClu
33353358
# pylint: disable=line-too-long
33363359
mc.security_profile.kubernetes_resource_object_encryption_profile.infrastructure_encryption = kms_infrastructure_encryption
33373360

3361+
if self.context.get_enable_azure_keyvault_kms():
3362+
key_id = self.context.get_azure_keyvault_kms_key_id()
3363+
if key_id:
3364+
if mc.security_profile is None:
3365+
mc.security_profile = self.models.ManagedClusterSecurityProfile()
3366+
mc.security_profile.azure_key_vault_kms = self.models.AzureKeyVaultKms(
3367+
enabled=True,
3368+
key_id=key_id,
3369+
key_vault_resource_id=self.context.get_azure_keyvault_kms_key_vault_resource_id(),
3370+
)
3371+
33383372
return mc
33393373

33403374
def set_up_creationdata_of_cluster_snapshot(self, mc: ManagedCluster) -> ManagedCluster:
@@ -3906,7 +3940,7 @@ def construct_mc_profile_preview(self, bypass_restore_defaults: bool = False) ->
39063940
# set up image integrity
39073941
mc = self.set_up_image_integrity(mc)
39083942
# set up KMS infrastructure encryption
3909-
mc = self.set_up_kms_infrastructure_encryption(mc)
3943+
mc = self.set_up_kms_pmk_and_cmk(mc)
39103944
# set up cluster snapshot
39113945
mc = self.set_up_creationdata_of_cluster_snapshot(mc)
39123946
# set up app routing profile

0 commit comments

Comments
 (0)