Skip to content

Commit

Permalink
{AKS} support disabling Azure KeyVault KMS
Browse files Browse the repository at this point in the history
  • Loading branch information
bingosummer committed Jul 23, 2022
1 parent 8aaa983 commit 50eed11
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/aks-preview/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ To release a new version, please select a new version number (usually plus 1 to
Pending
+++++++

* Support disabling Azure KeyVault KMS.

0.5.91
++++++

Expand Down
3 changes: 3 additions & 0 deletions src/aks-preview/azext_aks_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,9 @@
- name: --enable-azure-keyvault-kms
type: bool
short-summary: Enable Azure KeyVault Key Management Service.
- name: --disable-azure-keyvault-kms
type: bool
short-summary: Disable Azure KeyVault Key Management Service.
- name: --azure-keyvault-kms-key-id
type: string
short-summary: Identifier of Azure Key Vault key.
Expand Down
1 change: 1 addition & 0 deletions src/aks-preview/azext_aks_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ def load_arguments(self, _):
c.argument('enable_workload_identity', arg_type=get_three_state_flag())
c.argument('enable_oidc_issuer', action='store_true', is_preview=True)
c.argument('enable_azure_keyvault_kms', action='store_true', is_preview=True)
c.argument('disable_azure_keyvault_kms', action='store_true', is_preview=True)
c.argument('azure_keyvault_kms_key_id', validator=validate_azure_keyvault_kms_key_id, is_preview=True)
c.argument('azure_keyvault_kms_key_vault_network_access', arg_type=get_enum_type(keyvault_network_access_types), is_preview=True)
c.argument('azure_keyvault_kms_key_vault_resource_id', validator=validate_azure_keyvault_kms_key_vault_resource_id, is_preview=True)
Expand Down
1 change: 1 addition & 0 deletions src/aks-preview/azext_aks_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ def aks_update(
enable_workload_identity=None,
enable_oidc_issuer=False,
enable_azure_keyvault_kms=False,
disable_azure_keyvault_kms=False,
azure_keyvault_kms_key_id=None,
azure_keyvault_kms_key_vault_network_access=None,
azure_keyvault_kms_key_vault_resource_id=None,
Expand Down
43 changes: 43 additions & 0 deletions src/aks-preview/azext_aks_preview/managed_cluster_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,37 @@ def get_enable_azure_keyvault_kms(self) -> bool:
"""
return self._get_enable_azure_keyvault_kms(enable_validation=True)

def _get_disable_azure_keyvault_kms(self, enable_validation: bool = False) -> bool:
"""Internal function to obtain the value of disable_azure_keyvault_kms.
This function supports the option of enable_validation. When enabled, if both enable_azure_keyvault_kms and disable_azure_keyvault_kms are
specified, raise a MutuallyExclusiveArgumentError.
:return: bool
"""
# Read the original value passed by the command.
disable_azure_keyvault_kms = self.raw_param.get("disable_azure_keyvault_kms")

# This option is not supported in create mode, hence we do not read the property value from the `mc` object.
# This parameter does not need dynamic completion.
if enable_validation:
if disable_azure_keyvault_kms and self._get_enable_azure_keyvault_kms(enable_validation=False):
raise MutuallyExclusiveArgumentError(
"Cannot specify --enable-azure-keyvault-kms and --disable-azure-keyvault-kms at the same time."
)

return disable_azure_keyvault_kms

def get_disable_azure_keyvault_kms(self) -> bool:
"""Obtain the value of disable_azure_keyvault_kms.
This function will verify the parameter by default. If both enable_azure_keyvault_kms and disable_azure_keyvault_kms are specified, raise a
MutuallyExclusiveArgumentError.
:return: bool
"""
return self._get_disable_azure_keyvault_kms(enable_validation=True)

def _get_azure_keyvault_kms_key_id(self, enable_validation: bool = False) -> Union[str, None]:
"""Internal function to obtain the value of azure_keyvault_kms_key_id according to the context.
Expand Down Expand Up @@ -1964,6 +1995,18 @@ def update_azure_keyvault_kms(self, mc: ManagedCluster) -> ManagedCluster:
self.context.get_azure_keyvault_kms_key_vault_resource_id()
)

if self.context.get_disable_azure_keyvault_kms():
# get kms profile
if mc.security_profile is None:
mc.security_profile = self.models.ManagedClusterSecurityProfile()
azure_key_vault_kms_profile = mc.security_profile.azure_key_vault_kms
if azure_key_vault_kms_profile is None:
azure_key_vault_kms_profile = self.models.AzureKeyVaultKms()
mc.security_profile.azure_key_vault_kms = azure_key_vault_kms_profile

# set enabled to False
azure_key_vault_kms_profile.enabled = False

return mc

def update_storage_profile(self, mc: ManagedCluster) -> ManagedCluster:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4291,6 +4291,80 @@ def test_aks_update_with_azurekeyvaultkms_private_key_vault(self, resource_group
self.is_empty(),
])

@live_only()
@AllowLargeResponse()
@AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='centraluseuap')
def test_aks_disable_azurekeyvaultkms(self, resource_group, resource_group_location):
aks_name = self.create_random_name('cliakstest', 16)
kv_name = self.create_random_name('cliakstestkv', 16)
identity_name = self.create_random_name('cliakstestidentity', 24)
self.kwargs.update({
'resource_group': resource_group,
'name': aks_name,
"kv_name": kv_name,
"identity_name": identity_name,
'ssh_key_value': self.generate_ssh_keys()
})

# create user-assigned identity
create_identity = 'identity create --resource-group={resource_group} --name={identity_name} -o json'
identity = self.cmd(create_identity).get_output_in_json()
identity_id = identity['id']
identity_object_id = identity['principalId']
assert identity_id is not None
assert identity_object_id is not None
self.kwargs.update({
'identity_id': identity_id,
'identity_object_id': identity_object_id,
})

# create key vault and key
create_keyvault = 'keyvault create --resource-group={resource_group} --name={kv_name} -o json'
kv = self.cmd(create_keyvault, checks=[
self.check('properties.provisioningState', 'Succeeded')
]).get_output_in_json()

create_key = 'keyvault key create -n kms --vault-name {kv_name} -o json'
key = self.cmd(create_key, checks=[
self.check('attributes.enabled', True)
]).get_output_in_json()
key_id = key['key']['kid']
assert key_id is not None
self.kwargs.update({
'key_id': key_id,
})

# assign access policy
set_policy = 'keyvault set-policy --resource-group={resource_group} --name={kv_name} ' \
'--object-id {identity_object_id} --key-permissions encrypt decrypt -o json'
policy = self.cmd(set_policy, checks=[
self.check('properties.provisioningState', 'Succeeded')
]).get_output_in_json()

create_cmd = 'aks create --resource-group={resource_group} --name={name} ' \
'--assign-identity {identity_id} ' \
'--enable-azure-keyvault-kms --azure-keyvault-kms-key-id={key_id} --aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureKeyVaultKmsPreview ' \
'--ssh-key-value={ssh_key_value} -o json'
self.cmd(create_cmd, checks=[
self.check('provisioningState', 'Succeeded'),
self.check('securityProfile.azureKeyVaultKms.enabled', True),
self.check('securityProfile.azureKeyVaultKms.keyId', key_id)
])

update_cmd = 'aks update --resource-group={resource_group} --name={name} ' \
'--disable-azure-keyvault-kms --aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureKeyVaultKmsPreview ' \
'-o json'
self.cmd(update_cmd, checks=[
self.check('provisioningState', 'Succeeded'),
self.check('securityProfile.azureKeyVaultKms.enabled', False),
])

# delete
cmd = 'aks delete --resource-group={resource_group} --name={name} --yes --no-wait'
self.cmd(cmd, checks=[
self.is_empty(),
])

@AllowLargeResponse()
@AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='westcentralus', preserve_default_location=True)
def test_aks_create_and_update_with_csi_drivers_extensibility(self, resource_group, resource_group_location):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,53 @@ def test_get_enable_azure_keyvault_kms(self):
with self.assertRaises(RequiredArgumentMissingError):
ctx_5.get_enable_azure_keyvault_kms()

def test_get_disable_azure_keyvault_kms(self):
ctx_0 = AKSPreviewManagedClusterContext(
self.cmd,
AKSManagedClusterParamDict({}),
self.models,
decorator_mode=DecoratorMode.UPDATE,
)
self.assertIsNone(ctx_0.get_enable_azure_keyvault_kms())

ctx_1 = AKSPreviewManagedClusterContext(
self.cmd,
AKSManagedClusterParamDict(
{
"disable_azure_keyvault_kms": True,
}
),
self.models,
decorator_mode=DecoratorMode.UPDATE,
)
self.assertEqual(ctx_1.get_disable_azure_keyvault_kms(), True)

ctx_2 = AKSPreviewManagedClusterContext(
self.cmd,
AKSManagedClusterParamDict(
{
"disable_azure_keyvault_kms": False,
}
),
self.models,
decorator_mode=DecoratorMode.UPDATE,
)
self.assertEqual(ctx_2.get_disable_azure_keyvault_kms(), False)

ctx_3 = AKSPreviewManagedClusterContext(
self.cmd,
AKSManagedClusterParamDict(
{
"enable_azure_keyvault_kms": True,
"disable_azure_keyvault_kms": True,
}
),
self.models,
decorator_mode=DecoratorMode.UPDATE,
)
with self.assertRaises(MutuallyExclusiveArgumentError):
ctx_3.get_disable_azure_keyvault_kms()

def test_get_azure_keyvault_kms_key_id(self):
ctx_0 = AKSPreviewManagedClusterContext(
self.cmd,
Expand Down Expand Up @@ -3959,6 +4006,67 @@ def test_update_azure_keyvault_kms(self):
)
self.assertEqual(dec_mc_4, ground_truth_mc_4)

dec_5 = AKSPreviewManagedClusterUpdateDecorator(
self.cmd,
self.client,
{
"disable_azure_keyvault_kms": True,
},
CUSTOM_MGMT_AKS_PREVIEW,
)
azure_keyvault_kms_profile_5 = self.models.AzureKeyVaultKms(
enabled=True,
key_id=key_id_1,
key_vault_network_access="Public",
)
security_profile_5 = self.models.ManagedClusterSecurityProfile(
azure_key_vault_kms=azure_keyvault_kms_profile_5,
)
mc_5 = self.models.ManagedCluster(
location="test_location",
security_profile=security_profile_5,
)
dec_5.context.attach_mc(mc_5)
dec_mc_5 = dec_5.update_azure_keyvault_kms(mc_5)

ground_truth_azure_keyvault_kms_profile_5 = self.models.AzureKeyVaultKms(
enabled=False,
key_id=key_id_1,
key_vault_network_access="Public",
)
ground_truth_security_profile_5 = self.models.ManagedClusterSecurityProfile(
azure_key_vault_kms=ground_truth_azure_keyvault_kms_profile_5,
)
ground_truth_mc_5 = self.models.ManagedCluster(
location="test_location",
security_profile=ground_truth_security_profile_5,
)
self.assertEqual(dec_mc_5, ground_truth_mc_5)

dec_6 = AKSPreviewManagedClusterUpdateDecorator(
self.cmd,
self.client,
{
"disable_azure_keyvault_kms": True,
},
CUSTOM_MGMT_AKS_PREVIEW,
)
mc_6 = self.models.ManagedCluster(
location="test_location",
)
dec_6.context.attach_mc(mc_6)
dec_mc_6 = dec_6.update_azure_keyvault_kms(mc_6)

ground_truth_azure_keyvault_kms_profile_6 = self.models.AzureKeyVaultKms()
ground_truth_azure_keyvault_kms_profile_6.enabled=False
ground_truth_security_profile_6 = self.models.ManagedClusterSecurityProfile()
ground_truth_security_profile_6.azure_key_vault_kms=ground_truth_azure_keyvault_kms_profile_6
ground_truth_mc_6 = self.models.ManagedCluster(
location="test_location",
security_profile=ground_truth_security_profile_6,
)
self.assertEqual(dec_mc_6, ground_truth_mc_6)


def test_update_storage_profile(self):
dec_1 = AKSPreviewManagedClusterUpdateDecorator(
Expand Down
3 changes: 3 additions & 0 deletions src/aks-preview/linter_exclusions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ aks update:
enable_azure_keyvault_kms:
rule_exclusions:
- option_length_too_long
disable_azure_keyvault_kms:
rule_exclusions:
- option_length_too_long
azure_keyvault_kms_key_id:
rule_exclusions:
- option_length_too_long
Expand Down

0 comments on commit 50eed11

Please sign in to comment.