Skip to content
8 changes: 8 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acs/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,10 @@
- name: --k8s-support-plan
type: string
short-summary: Choose from "KubernetesOfficial" or "AKSLongTermSupport", with "AKSLongTermSupport" you get 1 extra year of CVE patchs.
- name: --ca-certs --custom-ca-trust-certificates
type: string
short-summary: Path to a file containing up to 10 blank line separated certificates. Only valid for Linux nodes.
long-summary: These certificates are used by Custom CA Trust feature and will be added to trust stores of nodes.
- name: --enable-defender
type: bool
short-summary: Enable Microsoft Defender security profile.
Expand Down Expand Up @@ -865,6 +869,10 @@
- name: --disable-defender
type: bool
short-summary: Disable defender profile.
- name: --ca-certs --custom-ca-trust-certificates
type: string
short-summary: Path to a file containing up to 10 blank line separated certificates. Only valid for Linux nodes.
long-summary: These certificates are used by Custom CA Trust feature and will be added to trust stores of nodes.
- name: --defender-config
type: string
short-summary: Path to JSON file containing Microsoft Defender profile configurations.
Expand Down
3 changes: 3 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acs/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
validate_crg_id,
validate_azure_service_mesh_revision,
validate_message_of_the_day,
validate_custom_ca_trust_certificates,
validate_bootstrap_container_registry_resource_id)
from azure.cli.core.commands.parameters import (
edge_zone_type, file_type, get_enum_type,
Expand Down Expand Up @@ -374,6 +375,7 @@ def load_arguments(self, _):
c.argument('enable_image_cleaner', action='store_true')
c.argument('image_cleaner_interval_hours', type=int)
c.argument('http_proxy_config')
c.argument('custom_ca_trust_certificates', options_list=["--custom-ca-trust-certificates", "--ca-certs"], help="path to file containing list of new line separated CAs")
c.argument('enable_keda', action='store_true')
c.argument('enable_vpa', action='store_true', help='enable vertical pod autoscaler for cluster')
c.argument('enable_azure_service_mesh',
Expand Down Expand Up @@ -573,6 +575,7 @@ def load_arguments(self, _):
c.argument('disable_image_cleaner', action='store_true', validator=validate_image_cleaner_enable_disable_mutually_exclusive)
c.argument('image_cleaner_interval_hours', type=int)
c.argument('http_proxy_config')
c.argument('custom_ca_trust_certificates', options_list=["--custom-ca-trust-certificates", "--ca-certs"], validator=validate_custom_ca_trust_certificates, help="path to file containing list of new line separated CAs")
c.argument('enable_keda', action='store_true')
c.argument('disable_keda', action='store_true')
c.argument('enable_vpa', action='store_true', help='enable vertical pod autoscaler for cluster')
Expand Down
8 changes: 8 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acs/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,11 @@ def validate_bootstrap_container_registry_resource_id(namespace):
from msrestazure.tools import is_valid_resource_id
if not is_valid_resource_id(container_registry_resource_id):
raise InvalidArgumentValueError("--bootstrap-container-registry-resource-id is not a valid Azure resource ID.")


def validate_custom_ca_trust_certificates(namespace):
"""Validates Custom CA Trust Certificates can only be used on Linux."""
if namespace.custom_ca_trust_certificates is not None and namespace.custom_ca_trust_certificates != "":
if hasattr(namespace, 'os_type') and namespace.os_type != "Linux":
raise ArgumentUsageError(
'--custom-ca-trust-certificates can only be set for linux nodepools')
2 changes: 2 additions & 0 deletions src/azure-cli/azure/cli/command_modules/acs/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ def aks_create(
image_cleaner_interval_hours=None,
enable_keda=False,
enable_vpa=False,
custom_ca_trust_certificates=None,
# advanced networking
enable_acns=None,
disable_acns_observability=None,
Expand Down Expand Up @@ -781,6 +782,7 @@ def aks_update(
enable_force_upgrade=False,
disable_force_upgrade=False,
upgrade_override_until=None,
custom_ca_trust_certificates=None,
# advanced networking
disable_acns=None,
enable_acns=None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
from azure.cli.core.commands import AzCliCommand, LongRunningOperation
from azure.cli.core.keys import is_valid_ssh_rsa_public_key
from azure.cli.core.profiles import ResourceType
from azure.cli.core.util import sdk_no_wait, truncate_text, get_file_json
from azure.cli.core.util import sdk_no_wait, truncate_text, get_file_json, read_file_content
from azure.core.exceptions import HttpResponseError
from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id
from knack.log import get_logger
Expand Down Expand Up @@ -884,6 +884,30 @@ def _get_disable_keda(self, enable_validation: bool = False) -> bool:

return disable_keda

def get_custom_ca_trust_certificates(self) -> Union[List[bytes], None]:
"""Obtain the value of custom ca trust certificates.
:return: List[str] or None
"""
custom_ca_certs_file_path = self.raw_param.get("custom_ca_trust_certificates")
if not custom_ca_certs_file_path:
return None
if not os.path.isfile(custom_ca_certs_file_path):
raise InvalidArgumentValueError(
"{} is not valid file, or not accessible.".format(
custom_ca_certs_file_path
)
)
# CAs are supposed to be separated with a new line, we filter out empty strings (e.g. some stray new line). We only allow up to 10 CAs
file_content = read_file_content(custom_ca_certs_file_path).split(os.linesep + os.linesep)
certs = [str.encode(x) for x in file_content if len(x) > 1]
if len(certs) > 10:
raise InvalidArgumentValueError(
"Only up to 10 new-line separated CAs can be passed, got {} instead.".format(
len(certs)
)
)
return certs

def get_snapshot_controller(self) -> Optional[ManagedClusterStorageProfileSnapshotController]:
"""Obtain the value of storage_profile.snapshot_controller

Expand Down Expand Up @@ -6150,6 +6174,22 @@ def set_up_workload_auto_scaler_profile(self, mc: ManagedCluster) -> ManagedClus
mc.workload_auto_scaler_profile.vertical_pod_autoscaler.enabled = True
return mc

def set_up_custom_ca_trust_certificates(self, mc: ManagedCluster) -> ManagedCluster:
"""Set up Custom CA Trust Certificates for the ManagedCluster object.

:return: the ManagedCluster object
"""
self._ensure_mc(mc)

ca_certs = self.context.get_custom_ca_trust_certificates()
if ca_certs:
if mc.security_profile is None:
mc.security_profile = self.models.ManagedClusterSecurityProfile() # pylint: disable=no-member

mc.security_profile.custom_ca_trust_certificates = ca_certs

return mc

def set_up_api_server_access_profile(self, mc: ManagedCluster) -> ManagedCluster:
"""Set up api server access profile and fqdn subdomain for the ManagedCluster object.

Expand Down Expand Up @@ -6607,6 +6647,8 @@ def construct_mc_profile_default(self, bypass_restore_defaults: bool = False) ->
mc = self.set_up_workload_auto_scaler_profile(mc)
# set up app routing profile
mc = self.set_up_ingress_web_app_routing(mc)
# set up custom ca trust certificates
mc = self.set_up_custom_ca_trust_certificates(mc)

# setup k8s support plan
mc = self.set_up_k8s_support_plan(mc)
Expand Down Expand Up @@ -8066,6 +8108,22 @@ def update_workload_auto_scaler_profile(self, mc: ManagedCluster) -> ManagedClus

return mc

def update_custom_ca_trust_certificates(self, mc: ManagedCluster) -> ManagedCluster:
"""Update Custom CA Trust Certificates for the ManagedCluster object.

:return: the ManagedCluster object
"""
self._ensure_mc(mc)

ca_certs = self.context.get_custom_ca_trust_certificates()
if ca_certs:
if mc.security_profile is None:
mc.security_profile = self.models.ManagedClusterSecurityProfile() # pylint: disable=no-member

mc.security_profile.custom_ca_trust_certificates = ca_certs

return mc

def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster:
"""Update azure monitor profile for the ManagedCluster object.
:return: the ManagedCluster object
Expand Down Expand Up @@ -8448,6 +8506,8 @@ def update_mc_profile_default(self) -> ManagedCluster:
mc = self.update_oidc_issuer_profile(mc)
# update auto upgrade profile
mc = self.update_auto_upgrade_profile(mc)
# update custom ca trust certificates
mc = self.update_custom_ca_trust_certificates(mc)
# update identity
mc = self.update_identity(mc)
# update addon profiles
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

CUSTOM_CA_TEST_CERT_STR = '-----BEGIN CERTIFICATE-----\n' \
'MIICljCCAX4CCQC9zUAgqqqrWzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ\n' \
'TDAeFw0yMjA5MTQwNjIzMjdaFw0yMjA5MTUwNjIzMjdaMA0xCzAJBgNVBAYTAlBM\n' \
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAopKNIIbvvcPCw9fc4KLX\n' \
'KDtRZobp5L+/1hCN+3OGhk5NvSTpSUrFifxqc0o3IF7YkO3K1n2jAvCMXO16Bf9b\n' \
'OAR7VkCrwGFVkXNjM4wvXAX8CNNvjqd1zDPXSKdE7Wd8k3fTzx6nGUM0UgljIPhH\n' \
'yh4a4Zujd5Ig2P/ZSX0pGJm47JTtMu7MDFHVM5wRWcCrN/H0TCYPIvEOs0B8AZxc\n' \
'p3TF7A6veT5U9pVhQ3Xl9JN6LvvLqPxG3ea10rdv9DYzaiXmSY3ujI3Ri1Q11uWC\n' \
'dtrFIpFu5cHW2OBW+jBXxL0v8xQmkxTLik4BR/PLCl30wxKQNsq3pjDgu0mutKuu\n' \
'5wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAVEAIs/hLwTVCwpEXdoXR24LelNNuB\n' \
'/8ptK6lyjE11XwfMN3yy7F2oB1lrA4rI3j9obpDsHDJBNB13bi/lKgvAcbIn/Tyu\n' \
'RKThtUdPgxNnqDUyxnb3OofMF3gB8ePTu+jZpd3zrlEuxdl40ByATCSyOgR6DHMt\n' \
'SDd+joypnOHFAeSM+V0AaTelXSCK9OAWSAp5e6S76a6lRx+D5Xl3hBedBI0tX59h\n' \
'tEYNEGZaRElFU79WcEF0cH+ZW0+jJ95xE3thZffRz6QI6yF63m8aC9l9bbdJS2zg\n' \
'Yv8W+lCZi//ODeOBUugr++z9uj+vGk47JDSpV0n4JOun3ALUDJ0gqmcS\n' \
'-----END CERTIFICATE-----'
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIICljCCAX4CCQC9zUAgqqqrWzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ
TDAeFw0yMjA5MTQwNjIzMjdaFw0yMjA5MTUwNjIzMjdaMA0xCzAJBgNVBAYTAlBM
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAopKNIIbvvcPCw9fc4KLX
KDtRZobp5L+/1hCN+3OGhk5NvSTpSUrFifxqc0o3IF7YkO3K1n2jAvCMXO16Bf9b
OAR7VkCrwGFVkXNjM4wvXAX8CNNvjqd1zDPXSKdE7Wd8k3fTzx6nGUM0UgljIPhH
yh4a4Zujd5Ig2P/ZSX0pGJm47JTtMu7MDFHVM5wRWcCrN/H0TCYPIvEOs0B8AZxc
p3TF7A6veT5U9pVhQ3Xl9JN6LvvLqPxG3ea10rdv9DYzaiXmSY3ujI3Ri1Q11uWC
dtrFIpFu5cHW2OBW+jBXxL0v8xQmkxTLik4BR/PLCl30wxKQNsq3pjDgu0mutKuu
5wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAVEAIs/hLwTVCwpEXdoXR24LelNNuB
/8ptK6lyjE11XwfMN3yy7F2oB1lrA4rI3j9obpDsHDJBNB13bi/lKgvAcbIn/Tyu
RKThtUdPgxNnqDUyxnb3OofMF3gB8ePTu+jZpd3zrlEuxdl40ByATCSyOgR6DHMt
SDd+joypnOHFAeSM+V0AaTelXSCK9OAWSAp5e6S76a6lRx+D5Xl3hBedBI0tX59h
tEYNEGZaRElFU79WcEF0cH+ZW0+jJ95xE3thZffRz6QI6yF63m8aC9l9bbdJS2zg
Yv8W+lCZi//ODeOBUugr++z9uj+vGk47JDSpV0n4JOun3ALUDJ0gqmcS
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
MIICljCCAX4CCQC9zUAgqqqrWzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ
TDAeFw0yMjA5MTQwNjIzMjdaFw0yMjA5MTUwNjIzMjdaMA0xCzAJBgNVBAYTAlBM
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAopKNIIbvvcPCw9fc4KLX
KDtRZobp5L+/1hCN+3OGhk5NvSTpSUrFifxqc0o3IF7YkO3K1n2jAvCMXO16Bf9b
OAR7VkCrwGFVkXNjM4wvXAX8CNNvjqd1zDPXSKdE7Wd8k3fTzx6nGUM0UgljIPhH
yh4a4Zujd5Ig2P/ZSX0pGJm47JTtMu7MDFHVM5wRWcCrN/H0TCYPIvEOs0B8AZxc
p3TF7A6veT5U9pVhQ3Xl9JN6LvvLqPxG3ea10rdv9DYzaiXmSY3ujI3Ri1Q11uWC
dtrFIpFu5cHW2OBW+jBXxL0v8xQmkxTLik4BR/PLCl30wxKQNsq3pjDgu0mutKuu
5wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAVEAIs/hLwTVCwpEXdoXR24LelNNuB
/8ptK6lyjE11XwfMN3yy7F2oB1lrA4rI3j9obpDsHDJBNB13bi/lKgvAcbIn/Tyu
RKThtUdPgxNnqDUyxnb3OofMF3gB8ePTu+jZpd3zrlEuxdl40ByATCSyOgR6DHMt
SDd+joypnOHFAeSM+V0AaTelXSCK9OAWSAp5e6S76a6lRx+D5Xl3hBedBI0tX59h
tEYNEGZaRElFU79WcEF0cH+ZW0+jJ95xE3thZffRz6QI6yF63m8aC9l9bbdJS2zg
Yv8W+lCZi//ODeOBUugr++z9uj+vGk47JDSpV0n4JOun3ALUDJ0gqmcS
-----END CERTIFICATE-----
Loading