Skip to content

Commit

Permalink
support AzureKeyVaultKms
Browse files Browse the repository at this point in the history
  • Loading branch information
bingosummer committed Mar 21, 2022
1 parent 0310284 commit 23433a9
Show file tree
Hide file tree
Showing 10 changed files with 590 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/aks-preview/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
Release History
===============

0.5.58
++++++
* Add support for enabling Azure KeyVault KMS with `--enable-azure-keyvault-kms` flag.

0.5.57
++++++
* Add support for updating HTTP proxy configuration via `az aks update --http-proxy-config file.json`
Expand Down
9 changes: 9 additions & 0 deletions src/aks-preview/azext_aks_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,12 @@
- name: --message-of-the-day
type: string
short-summary: Path to a file containing the desired message of the day. Only valid for linux nodes. Will be written to /etc/motd.
- name: --enable-azure-keyvault-kms
type: bool
short-summary: (PREVIEW) Enable Azure KeyVault Key Management Service.
- name: --azure-keyvault-kms-key-id
type: string
short-summary: (PREVIEW) Identifier of Azure Key Vault key.
examples:
- name: Create a Kubernetes cluster with an existing SSH public key.
text: az aks create -g MyResourceGroup -n MyManagedCluster --ssh-key-value /path/to/publickey
Expand Down Expand Up @@ -677,6 +683,9 @@
- name: --http-proxy-config
type: string
short-summary: HTTP Proxy configuration for this cluster.
- name: --enable-azure-keyvault-kms
type: bool
short-summary: (PREVIEW) Enable Azure KeyVault Key Management Service.
examples:
- name: Enable cluster-autoscaler within node count range [1,5]
text: az aks update --enable-cluster-autoscaler --min-count 1 --max-count 5 -g MyResourceGroup -n MyManagedCluster
Expand Down
6 changes: 5 additions & 1 deletion src/aks-preview/azext_aks_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
validate_load_balancer_outbound_ports, validate_load_balancer_idle_timeout, validate_nat_gateway_idle_timeout, validate_nodepool_tags, validate_addon,
validate_nodepool_labels, validate_vnet_subnet_id, validate_pod_subnet_id, validate_max_surge, validate_assign_identity, validate_addons,
validate_pod_identity_pod_labels, validate_pod_identity_resource_name, validate_pod_identity_resource_namespace, validate_assign_kubelet_identity,
validate_host_group_id, validate_message_of_the_day)
validate_host_group_id, validate_message_of_the_day, validate_azure_keyvault_kms_key_id)
from ._consts import CONST_OUTBOUND_TYPE_LOAD_BALANCER, CONST_OUTBOUND_TYPE_USER_DEFINED_ROUTING, CONST_OUTBOUND_TYPE_MANAGED_NAT_GATEWAY, \
CONST_OUTBOUND_TYPE_USER_ASSIGNED_NAT_GATEWAY, CONST_SCALE_SET_PRIORITY_REGULAR, CONST_SCALE_SET_PRIORITY_SPOT, \
CONST_SPOT_EVICTION_POLICY_DELETE, CONST_SPOT_EVICTION_POLICY_DEALLOCATE, \
Expand Down Expand Up @@ -207,6 +207,8 @@ def load_arguments(self, _):
validator=validate_host_group_id, is_preview=True)
c.argument('crg_id', validator=validate_crg_id, is_preview=True)
c.argument('message_of_the_day', type=str) # no validation for aks create because it already only supports Linux.
c.argument('enable_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)

with self.argument_context('aks update') as c:
c.argument('enable_cluster_autoscaler', options_list=[
Expand Down Expand Up @@ -268,6 +270,8 @@ def load_arguments(self, _):
help='space-separated labels: key[=value] [key[=value] ...]. See https://aka.ms/node-labels for syntax of labels.')
c.argument('enable_oidc_issuer', action='store_true', is_preview=True)
c.argument('http_proxy_config', type=str)
c.argument('enable_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)

with self.argument_context('aks scale') as c:
c.argument('nodepool_name', type=str,
Expand Down
17 changes: 16 additions & 1 deletion src/aks-preview/azext_aks_preview/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ def validate_label(label):


def validate_max_surge(namespace):
"""validates parameters like max surge are postive integers or percents. less strict than RP"""
"""validates parameters like max surge are positive integers or percents. less strict than RP"""
if namespace.max_surge is None:
return
int_or_percent = namespace.max_surge
Expand Down Expand Up @@ -507,3 +507,18 @@ def validate_crg_id(namespace):
from msrestazure.tools import is_valid_resource_id
if not is_valid_resource_id(namespace.crg_id):
raise InvalidArgumentValueError("--crg-id is not a valid Azure resource ID.")


def validate_azure_keyvault_kms_key_id(namespace):
key_id = namespace.azure_keyvault_kms_key_id
if key_id:
err_msg = '--azure-keyvault-kms-key-id is not a valid Key Vault key ID. ' \
'See https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name'

https_prefix = "https://"
if not key_id.startswith(https_prefix):
raise InvalidArgumentValueError(err_msg)

segments = key_id[len(https_prefix):].split("/")
if len(segments) != 4 or segments[1] != "keys":
raise InvalidArgumentValueError(err_msg)
6 changes: 5 additions & 1 deletion src/aks-preview/azext_aks_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ def aks_create(cmd,
host_group_id=None,
crg_id=None,
message_of_the_day=None,
enable_azure_keyvault_kms=False,
azure_keyvault_kms_key_id=None,
yes=False):
# DO NOT MOVE: get all the original parameters and save them as a dictionary
raw_parameters = locals()
Expand Down Expand Up @@ -840,7 +842,9 @@ def aks_update(cmd, # pylint: disable=too-many-statements,too-many-branches,
gmsa_dns_server=None,
gmsa_root_domain_name=None,
enable_oidc_issuer=False,
http_proxy_config=None):
http_proxy_config=None,
enable_azure_keyvault_kms=False,
azure_keyvault_kms_key_id=None):
# DO NOT MOVE: get all the original parameters and save them as a dictionary
raw_parameters = locals()

Expand Down
129 changes: 128 additions & 1 deletion src/aks-preview/azext_aks_preview/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
ManagedClusterAddonProfile = TypeVar("ManagedClusterAddonProfile")
ManagedClusterOIDCIssuerProfile = TypeVar('ManagedClusterOIDCIssuerProfile')
Snapshot = TypeVar("Snapshot")
AzureKeyVaultKms = TypeVar('AzureKeyVaultKms')


# pylint: disable=too-many-instance-attributes,too-few-public-methods
Expand Down Expand Up @@ -119,6 +120,16 @@ def __init__(self, cmd: AzCommandsLoader, resource_type: ResourceType):
resource_type=self.resource_type,
operation_group="managed_clusters",
)
self.ManagedClusterSecurityProfile = self.__cmd.get_models(
"ManagedClusterSecurityProfile",
resource_type=self.resource_type,
operation_group="managed_clusters",
)
self.AzureKeyVaultKms = self.__cmd.get_models(
"AzureKeyVaultKms",
resource_type=self.resource_type,
operation_group="managed_clusters",
)
# holder for nat gateway related models
self.__nat_gateway_models = None
# holder for pod identity related models
Expand Down Expand Up @@ -1577,6 +1588,83 @@ def get_crg_id(self) -> str:
crg_id = self.raw_param.get("crg_id")
return crg_id

def _get_enable_azure_keyvault_kms(self, enable_validation: bool = False) -> bool:
"""Internal function to obtain the value of enable_azure_keyvault_kms.
This function supports the option of enable_validation. When enabled, if azure_keyvault_kms_key_id is empty,
raise a RequiredArgumentMissingError.
:return: bool
"""
# read the original value passed by the command
enable_azure_keyvault_kms = self.raw_param.get("enable_azure_keyvault_kms")
# In create mode, try to read the property value corresponding to the parameter from the `mc` object.
if self.decorator_mode == DecoratorMode.CREATE:
if (
self.mc and
self.mc.security_profile and
self.mc.security_profile.azure_key_vault_kms
):
enable_azure_keyvault_kms = self.mc.security_profile.azure_key_vault_kms.enabled

# this parameter does not need dynamic completion
# validation
if enable_validation:
if enable_azure_keyvault_kms and self._get_azure_keyvault_kms_key_id(enable_validation=False) is None:
raise RequiredArgumentMissingError('"--enable-azure-keyvault-kms" requires "--azure-keyvault-kms-key-id".')

return enable_azure_keyvault_kms

def get_enable_azure_keyvault_kms(self) -> bool:
"""Obtain the value of enable_azure_keyvault_kms.
This function will verify the parameter by default. When enabled, if azure_keyvault_kms_key_id is empty,
raise a RequiredArgumentMissingError.
:return: bool
"""
return self._get_enable_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.
This function supports the option of enable_validation. When enabled, it will check if azure_keyvault_kms_key_id is
assigned but enable_azure_keyvault_kms is not specified, if so, raise a RequiredArgumentMissingError.
:return: string or None
"""
# read the original value passed by the command
azure_keyvault_kms_key_id = self.raw_param.get("azure_keyvault_kms_key_id")
# In create mode, try to read the property value corresponding to the parameter from the `mc` object.
if self.decorator_mode == DecoratorMode.CREATE:
if (
self.mc and
self.mc.security_profile and
self.mc.security_profile.azure_key_vault_kms and
self.mc.security_profile.azure_key_vault_kms.key_id is not None
):
azure_keyvault_kms_key_id = self.mc.security_profile.azure_key_vault_kms.key_id

if enable_validation:
enable_azure_keyvault_kms = self._get_enable_azure_keyvault_kms(enable_validation=False)
if (azure_keyvault_kms_key_id and
(enable_azure_keyvault_kms is None or
enable_azure_keyvault_kms is False)
):
raise RequiredArgumentMissingError('"--azure-keyvault-kms-key-id" requires "--enable-azure-keyvault-kms".')

return azure_keyvault_kms_key_id

def get_azure_keyvault_kms_key_id(self) -> Union[str, None]:
"""Obtain the value of azure_keyvault_kms_key_id.
This function will verify the parameter by default. When enabled, if enable_azure_keyvault_kms is False,
raise a RequiredArgumentMissingError.
:return: bool
"""
return self._get_azure_keyvault_kms_key_id(enable_validation=True)


class AKSPreviewCreateDecorator(AKSCreateDecorator):
# pylint: disable=super-init-not-called
Expand Down Expand Up @@ -1898,6 +1986,23 @@ def set_up_oidc_issuer_profile(self, mc: ManagedCluster) -> ManagedCluster:

return mc

def set_up_azure_keyvault_kms(self, mc: ManagedCluster) -> ManagedCluster:
"""Set up security profile azureKeyVaultKms for the ManagedCluster object.
:return: the ManagedCluster object
"""
if self.context.get_enable_azure_keyvault_kms():
key_id = self.context.get_azure_keyvault_kms_key_id()
if key_id:
if mc.security_profile is None:
mc.security_profile = self.models.ManagedClusterSecurityProfile()
mc.security_profile.azure_key_vault_kms = self.models.AzureKeyVaultKms(
enabled=True,
key_id=key_id,
)

return mc

def construct_mc_preview_profile(self) -> ManagedCluster:
"""The overall controller used to construct the preview ManagedCluster profile.
Expand All @@ -1917,6 +2022,7 @@ def construct_mc_preview_profile(self) -> ManagedCluster:
# set up pod identity profile
mc = self.set_up_pod_identity_profile(mc)
mc = self.set_up_oidc_issuer_profile(mc)
mc = self.set_up_azure_keyvault_kms(mc)
return mc

def create_mc_preview(self, mc: ManagedCluster) -> ManagedCluster:
Expand Down Expand Up @@ -2068,7 +2174,8 @@ def check_raw_parameters(self):
'"--enble-windows-gmsa" or '
'"--nodepool-labels" or '
'"--enable-oidc-issuer" or '
'"--http-proxy-config".'
'"--http-proxy-config" or '
'"--enable-azure-keyvault-kms".'
)

def update_load_balancer_profile(self, mc: ManagedCluster) -> ManagedCluster:
Expand Down Expand Up @@ -2204,6 +2311,25 @@ def update_oidc_issuer_profile(self, mc: ManagedCluster) -> ManagedCluster:

return mc

def update_azure_keyvault_kms(self, mc: ManagedCluster) -> ManagedCluster:
"""Update security profile azureKeyvaultKms for the ManagedCluster object.
:return: the ManagedCluster object
"""
self._ensure_mc(mc)

if self.context.get_enable_azure_keyvault_kms():
key_id = self.context.get_azure_keyvault_kms_key_id()
if key_id:
if mc.security_profile is None:
mc.security_profile = self.models.ManagedClusterSecurityProfile()
mc.security_profile.azure_key_vault_kms = self.models.AzureKeyVaultKms(
enabled=True,
key_id=key_id,
)

return mc

def patch_mc(self, mc: ManagedCluster) -> ManagedCluster:
"""Helper function to patch the ManagedCluster object.
Expand Down Expand Up @@ -2237,6 +2363,7 @@ def update_mc_preview_profile(self) -> ManagedCluster:
mc = self.update_pod_identity_profile(mc)
mc = self.update_oidc_issuer_profile(mc)
mc = self.update_http_proxy_config(mc)
mc = self.update_azure_keyvault_kms(mc)
return mc

def update_mc_preview(self, mc: ManagedCluster) -> ManagedCluster:
Expand Down
Loading

0 comments on commit 23433a9

Please sign in to comment.