From d09fab7f8c6b36002cb31f5c35ee2dc007a720db Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 22:28:28 -0800 Subject: [PATCH 01/10] Fix CKV_AZURE_227 --- .../azure/AKSEncryptionAtHostEnabled.py | 20 +++++++++---- .../main.tf | 30 +++++++++++++++++++ .../azure/test_AKSEncryptionAtHostEnabled.py | 6 +++- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py b/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py index 0640780f851..19eb5fc0b5a 100644 --- a/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py +++ b/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py @@ -1,8 +1,8 @@ from checkov.common.models.enums import CheckCategories, CheckResult -from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck -class AKSEncryptionAtHostEnabled(BaseResourceValueCheck): +class AKSEncryptionAtHostEnabled(BaseResourceCheck): def __init__(self) -> None: """ With host-based encryption, the data stored on the VM host of @@ -22,14 +22,22 @@ def __init__(self) -> None: id=id, categories=categories, supported_resources=supported_resources, - missing_block_result=CheckResult.FAILED, ) - def get_inspected_key(self) -> str: + def scan_resource_conf(self, conf): if self.entity_type == "azurerm_kubernetes_cluster": - return "default_node_pool/[0]/enable_host_encryption" + if conf.get('default_node_pool'): + node_pool = conf['default_node_pool'][0] + if node_pool.get('enable_host_encryption') == [True] or node_pool.get('host_encryption_enabled') == [ + True]: + return CheckResult.PASSED + self.evaluated_keys = ['default_node_pool/[0]/enable_host_encryption', + 'default_node_pool/[0]/host_encryption_enabled'] else: - return "enable_host_encryption" + if conf.get('enable_host_encryption') == [True] or conf.get('host_encryption_enabled') == [True]: + return CheckResult.PASSED + self.evaluated_keys = ['enable_host_encryption', 'host_encryption_enabled'] + return CheckResult.FAILED check = AKSEncryptionAtHostEnabled() diff --git a/tests/terraform/checks/resource/azure/example_AKSEncryptionAtHostEnabled/main.tf b/tests/terraform/checks/resource/azure/example_AKSEncryptionAtHostEnabled/main.tf index 21d1100b97d..24593355961 100644 --- a/tests/terraform/checks/resource/azure/example_AKSEncryptionAtHostEnabled/main.tf +++ b/tests/terraform/checks/resource/azure/example_AKSEncryptionAtHostEnabled/main.tf @@ -35,6 +35,36 @@ resource "azurerm_kubernetes_cluster_node_pool" "pass" { } } +resource "azurerm_kubernetes_cluster" "pass_new" { + name = "internal" + default_node_pool { + host_encryption_enabled = true + } +} + +resource "azurerm_kubernetes_cluster_node_pool" "pass_new" { + name = "internal" + kubernetes_cluster_id = azurerm_kubernetes_cluster.example.id + vm_size = "Standard_DS2_v2" + node_count = 1 + host_encryption_enabled = true +} + + +resource "azurerm_kubernetes_cluster" "fail_new" { + name = "internal" + default_node_pool { + host_encryption_enabled = false + } +} + +resource "azurerm_kubernetes_cluster_node_pool" "fail_new" { + name = "internal" + kubernetes_cluster_id = azurerm_kubernetes_cluster.example.id + vm_size = "Standard_DS2_v2" + node_count = 1 + host_encryption_enabled = false +} resource "azurerm_kubernetes_cluster" "fail1" { name = "internal" diff --git a/tests/terraform/checks/resource/azure/test_AKSEncryptionAtHostEnabled.py b/tests/terraform/checks/resource/azure/test_AKSEncryptionAtHostEnabled.py index 479e1aaaeeb..81aada18c11 100644 --- a/tests/terraform/checks/resource/azure/test_AKSEncryptionAtHostEnabled.py +++ b/tests/terraform/checks/resource/azure/test_AKSEncryptionAtHostEnabled.py @@ -19,13 +19,17 @@ def test(self): passing_resources = { 'azurerm_kubernetes_cluster.pass', - 'azurerm_kubernetes_cluster_node_pool.pass' + 'azurerm_kubernetes_cluster_node_pool.pass', + 'azurerm_kubernetes_cluster.pass_new', + 'azurerm_kubernetes_cluster_node_pool.pass_new', } failing_resources = { 'azurerm_kubernetes_cluster.fail1', 'azurerm_kubernetes_cluster.fail2', + 'azurerm_kubernetes_cluster.fail_new', 'azurerm_kubernetes_cluster_node_pool.fail1', 'azurerm_kubernetes_cluster_node_pool.fail2', + 'azurerm_kubernetes_cluster_node_pool.fail_new', } skipped_resources = {} From 85b8bc9ea1d8eee6dffc9ae3a4e5e44479851723 Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 22:35:04 -0800 Subject: [PATCH 02/10] Fix CKV_AZURE_145 --- .../arm/checks/resource/FunctionAppMinTLSVersion.py | 2 +- .../resource/azure/FunctionAppMinTLSVersion.py | 12 +++++++----- .../example_FunctionAppMinTLSVersion/pass4.json | 2 +- .../azure/example_FunctionAppMinTLSVersion/main.tf | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/checkov/arm/checks/resource/FunctionAppMinTLSVersion.py b/checkov/arm/checks/resource/FunctionAppMinTLSVersion.py index cefe0dd5825..4f556ab5521 100644 --- a/checkov/arm/checks/resource/FunctionAppMinTLSVersion.py +++ b/checkov/arm/checks/resource/FunctionAppMinTLSVersion.py @@ -20,7 +20,7 @@ def get_expected_value(self) -> Any: return 1.2 def get_expected_values(self) -> List[Any]: - return ["1.2", 1.2] + return ["1.2", 1.2, "1.3", 1.3] check = FunctionAppMinTLSVersion() diff --git a/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py b/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py index ab38db080fe..2f24440eb40 100644 --- a/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py +++ b/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py @@ -1,9 +1,11 @@ +from typing import Any, List + from checkov.common.models.enums import CheckResult, CheckCategories from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck class FunctionAppMinTLSVersion(BaseResourceValueCheck): - def __init__(self): + def __init__(self) -> None: """ The minimum supported TLS version for the function app. Defaults to 1.2 for new function apps. @@ -20,17 +22,17 @@ def __init__(self): super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources, missing_block_result=CheckResult.PASSED) - def get_inspected_key(self): + def get_inspected_key(self) -> str: if self.entity_type in ("azurerm_function_app", "azurerm_function_app_slot"): return "site_config/[0]/min_tls_version" else: return "site_config/[0]/minimum_tls_version" - def get_expected_value(self): + def get_expected_value(self) -> Any: return 1.2 - def get_expected_values(self): - return ["1.2", 1.2] + def get_expected_values(self) -> List[Any]: + return ["1.2", 1.2, "1.3", 1.3] check = FunctionAppMinTLSVersion() diff --git a/tests/arm/checks/resource/example_FunctionAppMinTLSVersion/pass4.json b/tests/arm/checks/resource/example_FunctionAppMinTLSVersion/pass4.json index 93359bab29f..0b431e8d3a5 100644 --- a/tests/arm/checks/resource/example_FunctionAppMinTLSVersion/pass4.json +++ b/tests/arm/checks/resource/example_FunctionAppMinTLSVersion/pass4.json @@ -11,7 +11,7 @@ "properties": { "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', 'example-app-service-plan')]", "siteConfig": { - "minTlsVersion": 1.2 + "minTlsVersion": 1.3 } } } diff --git a/tests/terraform/checks/resource/azure/example_FunctionAppMinTLSVersion/main.tf b/tests/terraform/checks/resource/azure/example_FunctionAppMinTLSVersion/main.tf index 4e5cce81fec..659007f18e9 100644 --- a/tests/terraform/checks/resource/azure/example_FunctionAppMinTLSVersion/main.tf +++ b/tests/terraform/checks/resource/azure/example_FunctionAppMinTLSVersion/main.tf @@ -108,7 +108,7 @@ resource "azurerm_function_app" "pass2" { site_config { dotnet_framework_version = "v4.0" scm_type = "LocalGit" - min_tls_version = 1.2 + min_tls_version = 1.3 ftps_state = "AllAllowed" http2_enabled = false cors { @@ -179,7 +179,7 @@ resource "azurerm_linux_function_app_slot" "pass7" { storage_account_name = azurerm_storage_account.example.name site_config { - minimum_tls_version = 1.2 + minimum_tls_version = 1.3 } } resource "azurerm_windows_function_app_slot" "pass8" { From 12b5b6178174700b633731c2c4a9b21e69783729 Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 22:38:10 -0800 Subject: [PATCH 03/10] fix flake8 --- .../checks/resource/azure/AKSEncryptionAtHostEnabled.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py b/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py index 19eb5fc0b5a..e673b1d0a2a 100644 --- a/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py +++ b/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py @@ -28,8 +28,8 @@ def scan_resource_conf(self, conf): if self.entity_type == "azurerm_kubernetes_cluster": if conf.get('default_node_pool'): node_pool = conf['default_node_pool'][0] - if node_pool.get('enable_host_encryption') == [True] or node_pool.get('host_encryption_enabled') == [ - True]: + if (node_pool.get('enable_host_encryption') == [True] or + node_pool.get('host_encryption_enabled') == [True]): return CheckResult.PASSED self.evaluated_keys = ['default_node_pool/[0]/enable_host_encryption', 'default_node_pool/[0]/host_encryption_enabled'] @@ -40,4 +40,5 @@ def scan_resource_conf(self, conf): return CheckResult.FAILED + check = AKSEncryptionAtHostEnabled() From 3afec0ec2a3a371b56861e1f9af57ffee1d9949a Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 23:11:48 -0800 Subject: [PATCH 04/10] add flexible servers --- .../resource/MySQLPublicAccessDisabled.py | 15 +++-- .../fail2.json | 66 +++++++++++++++++++ .../pass2.json | 66 +++++++++++++++++++ .../test_MySQLPublicAccessDisabled.py | 2 + 4 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/fail2.json create mode 100644 tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/pass2.json diff --git a/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py b/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py index 6336a41668a..ded2204ae28 100644 --- a/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py +++ b/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py @@ -1,3 +1,5 @@ +from typing import List + from checkov.common.models.enums import CheckCategories from checkov.arm.base_resource_value_check import BaseResourceValueCheck @@ -6,18 +8,21 @@ class MySQLPublicAccessDisabled(BaseResourceValueCheck): def __init__(self) -> None: name = "Ensure 'public network access enabled' is set to 'False' for mySQL servers" id = "CKV_AZURE_53" - supported_resources = ("Microsoft.DBforMySQL/servers",) + supported_resources = ("Microsoft.DBforMySQL/servers","Microsoft.DBforMySQL/flexibleServers") categories = (CheckCategories.NETWORKING,) super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) def get_inspected_key(self) -> str: - return "properties/publicNetworkAccess" + if self.entity_type == "Microsoft.DBforMySQL/servers": + return "properties/publicNetworkAccess" + else: + return "properties/network/publicNetworkAccess" def get_expected_value(self) -> str: - """ - Returns the default expected value, governed by provider best practices - """ return "disabled" + def get_expected_values(self) -> List[str]: + return ["disabled", "Disabled"] + check = MySQLPublicAccessDisabled() diff --git a/tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/fail2.json b/tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/fail2.json new file mode 100644 index 00000000000..fa88ce13028 --- /dev/null +++ b/tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/fail2.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.DBforMySQL/flexibleServers", + "apiVersion": "2024-10-01-preview", + "name": "fail2", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity": {} + } + }, + "location": "eastus", + "properties": { + "administratorLogin": "adminuser", + "administratorLoginPassword": "YourSecurePassword123!", + "availabilityZone": "1", + "backup": { + "backupIntervalHours": 24, + "backupRetentionDays": 7, + "geoRedundantBackup": "Disabled" + }, + "createMode": "Default", + "databasePort": 3306, + "dataEncryption": { + "type": "SystemManaged" + }, + "highAvailability": { + "mode": "ZoneRedundant", + "standbyAvailabilityZone": "2" + }, + "maintenancePolicy": { + "patchStrategy": "Automatic" + }, + "maintenanceWindow": { + "customWindow": "Sun:02:00-Sun:04:00", + "dayOfWeek": 0, + "startHour": 2, + "startMinute": 0 + }, + "network": { + "delegatedSubnetResourceId": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/myVNet/subnets/mySubnet", + "privateDnsZoneResourceId": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/privateDnsZones/myPrivateDnsZone", + "publicNetworkAccess": "Enabled" + }, + "storage": { + "autoGrow": "Enabled", + "iops": 600, + "storageSizeGB": 128, + "storageRedundancy": "Zone" + }, + "version": "8.0" + }, + "sku": { + "name": "Standard_D2ds_v4", + "tier": "GeneralPurpose" + }, + "tags": { + "Environment": "Production", + "Project": "MySQLMigration" + } + } + ] +} diff --git a/tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/pass2.json b/tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/pass2.json new file mode 100644 index 00000000000..d3446b6b07d --- /dev/null +++ b/tests/arm/checks/resource/example_TestMySQLPublicAccessDisabled/pass2.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.DBforMySQL/flexibleServers", + "apiVersion": "2024-10-01-preview", + "name": "pass2", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity": {} + } + }, + "location": "eastus", + "properties": { + "administratorLogin": "adminuser", + "administratorLoginPassword": "YourSecurePassword123!", + "availabilityZone": "1", + "backup": { + "backupIntervalHours": 24, + "backupRetentionDays": 7, + "geoRedundantBackup": "Disabled" + }, + "createMode": "Default", + "databasePort": 3306, + "dataEncryption": { + "type": "SystemManaged" + }, + "highAvailability": { + "mode": "ZoneRedundant", + "standbyAvailabilityZone": "2" + }, + "maintenancePolicy": { + "patchStrategy": "Automatic" + }, + "maintenanceWindow": { + "customWindow": "Sun:02:00-Sun:04:00", + "dayOfWeek": 0, + "startHour": 2, + "startMinute": 0 + }, + "network": { + "delegatedSubnetResourceId": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/myVNet/subnets/mySubnet", + "privateDnsZoneResourceId": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/privateDnsZones/myPrivateDnsZone", + "publicNetworkAccess": "Disabled" + }, + "storage": { + "autoGrow": "Enabled", + "iops": 600, + "storageSizeGB": 128, + "storageRedundancy": "Zone" + }, + "version": "8.0" + }, + "sku": { + "name": "Standard_D2ds_v4", + "tier": "GeneralPurpose" + }, + "tags": { + "Environment": "Production", + "Project": "MySQLMigration" + } + } + ] +} diff --git a/tests/arm/checks/resource/test_MySQLPublicAccessDisabled.py b/tests/arm/checks/resource/test_MySQLPublicAccessDisabled.py index 12bc369b81e..52da66489f6 100644 --- a/tests/arm/checks/resource/test_MySQLPublicAccessDisabled.py +++ b/tests/arm/checks/resource/test_MySQLPublicAccessDisabled.py @@ -22,9 +22,11 @@ def test_summary(self): passing_resources = { "Microsoft.DBforMySQL/servers.pass", + "Microsoft.DBforMySQL/flexibleServers.pass2", } failing_resources = { "Microsoft.DBforMySQL/servers.fail", + "Microsoft.DBforMySQL/flexibleServers.fail2", } passed_check_resources = {c.resource for c in report.passed_checks} From 5d63a08c4ebeb444735de0b7c7407cae21f771fc Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 23:15:02 -0800 Subject: [PATCH 05/10] fix flake8 --- checkov/arm/checks/resource/MySQLPublicAccessDisabled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py b/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py index ded2204ae28..5608daeeacb 100644 --- a/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py +++ b/checkov/arm/checks/resource/MySQLPublicAccessDisabled.py @@ -8,7 +8,7 @@ class MySQLPublicAccessDisabled(BaseResourceValueCheck): def __init__(self) -> None: name = "Ensure 'public network access enabled' is set to 'False' for mySQL servers" id = "CKV_AZURE_53" - supported_resources = ("Microsoft.DBforMySQL/servers","Microsoft.DBforMySQL/flexibleServers") + supported_resources = ("Microsoft.DBforMySQL/servers", "Microsoft.DBforMySQL/flexibleServers",) categories = (CheckCategories.NETWORKING,) super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) From 8f8498a409536af31142540234641db9aff5b4fa Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 23:24:14 -0800 Subject: [PATCH 06/10] Fix CKV_AZURE_11 --- .../resource/azure/SQLServerNoPublicAccess.py | 1 + .../example_SQLServerNoPublicAccess/main.tf | 39 ++++++++++ .../azure/test_SQLServerNoPublicAccess.py | 78 ++++++++----------- 3 files changed, 73 insertions(+), 45 deletions(-) create mode 100644 tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf diff --git a/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py b/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py index 6c27945402f..5f706ba0a59 100644 --- a/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py +++ b/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py @@ -18,6 +18,7 @@ def __init__(self): 'azurerm_sql_firewall_rule', 'azurerm_postgresql_firewall_rule', 'azurerm_mysql_firewall_rule', + 'azurerm_mysql_flexible_server_firewall_rule', ) categories = (CheckCategories.NETWORKING,) super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) diff --git a/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf b/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf new file mode 100644 index 00000000000..df65b8f8a9f --- /dev/null +++ b/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf @@ -0,0 +1,39 @@ +resource "azurerm_mysql_firewall_rule" "fail" { + name = "office" + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_mysql_server.example.name + start_ip_address = "0.0.0.0" + end_ip_address = "255.255.255.255" +} + +resource "azurerm_mysql_firewall_rule" "pass" { + name = "office" + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_mysql_server.example.name + start_ip_address = "0.0.0.0" + end_ip_address = "0.0.0.0" +} + +resource "azurerm_mysql_firewall_rule" "pass2" { + name = "office" + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_mysql_server.example.name + start_ip_address = "40.112.8.12" + end_ip_address = "40.112.8.17" +} + +resource "azurerm_mysql_flexible_server_firewall_rule" "pass" { + name = "office" + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_mysql_flexible_server.example.name + start_ip_address = "40.112.0.0" + end_ip_address = "40.112.255.255" +} + +resource "azurerm_mysql_flexible_server_firewall_rule" "fail" { + name = "office" + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_mysql_flexible_server.example.name + start_ip_address = "0.0.0.0" + end_ip_address = "255.255.255.255" +} diff --git a/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py b/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py index 67657d4c67c..bb8325f8b93 100644 --- a/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py +++ b/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py @@ -1,54 +1,42 @@ +import os import unittest -import hcl2 - +from checkov.runner_filter import RunnerFilter +from checkov.terraform.runner import Runner from checkov.terraform.checks.resource.azure.SQLServerNoPublicAccess import check -from checkov.common.models.enums import CheckResult class TestSQLServerNoPublicAccess(unittest.TestCase): - - def test_failure(self): - hcl_res = hcl2.loads(""" - resource "azurerm_mysql_firewall_rule" "example" { - name = "office" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_mysql_server.example.name - start_ip_address = "0.0.0.0" - end_ip_address = "255.255.255.255" - } - """) - resource_conf = hcl_res['resource'][0]['azurerm_mysql_firewall_rule']['example'] - scan_result = check.scan_resource_conf(conf=resource_conf) - self.assertEqual(CheckResult.FAILED, scan_result) - - def test_success_allow_azure_services(self): - hcl_res = hcl2.loads(""" - resource "azurerm_mysql_firewall_rule" "example" { - name = "office" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_mysql_server.example.name - start_ip_address = "0.0.0.0" - end_ip_address = "0.0.0.0" - } - """) - resource_conf = hcl_res['resource'][0]['azurerm_mysql_firewall_rule']['example'] - scan_result = check.scan_resource_conf(conf=resource_conf) - self.assertEqual(CheckResult.PASSED, scan_result) - - def test_success(self): - hcl_res = hcl2.loads(""" - resource "azurerm_mysql_firewall_rule" "example" { - name = "office" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_mysql_server.example.name - start_ip_address = "40.112.8.12" - end_ip_address = "40.112.8.17" - } - """) - resource_conf = hcl_res['resource'][0]['azurerm_mysql_firewall_rule']['example'] - scan_result = check.scan_resource_conf(conf=resource_conf) - self.assertEqual(CheckResult.PASSED, scan_result) + def test(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) + + test_files_dir = os.path.join(current_dir, "example_SQLServerNoPublicAccess") + report = runner.run(root_folder=test_files_dir, + runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() + + passing_resources = { + 'azurerm_mysql_firewall_rule.pass', + 'azurerm_mysql_firewall_rule.pass2', + 'azurerm_mysql_flexible_server_firewall_rule.pass' + } + failing_resources = { + 'azurerm_mysql_firewall_rule.fail', + 'azurerm_mysql_flexible_server_firewall_rule.fail', + } + skipped_resources = {} + + passed_check_resources = set([c.resource for c in report.passed_checks]) + failed_check_resources = set([c.resource for c in report.failed_checks]) + + self.assertEqual(summary['passed'], len(passing_resources)) + self.assertEqual(summary['failed'], len(failing_resources)) + self.assertEqual(summary['skipped'], len(skipped_resources)) + self.assertEqual(summary['parsing_errors'], 0) + + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) if __name__ == '__main__': From f358902ab4708c3259ae1d7da680812aa3da3390 Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 13 Dec 2024 23:41:45 -0800 Subject: [PATCH 07/10] Update checks --- .../azure/AzureSQLserverNotOverlyPermissive.yaml | 8 ++++++-- .../resource/azure/SQLServerNoPublicAccess.py | 1 + .../azure/example_SQLServerNoPublicAccess/main.tf | 14 ++++++++++++++ .../resource/azure/test_SQLServerNoPublicAccess.py | 4 +++- .../expected.yaml | 2 ++ .../AzureSQLserverNotOverlyPermissive/main.tf | 11 +++++++++++ 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/checkov/terraform/checks/graph_checks/azure/AzureSQLserverNotOverlyPermissive.yaml b/checkov/terraform/checks/graph_checks/azure/AzureSQLserverNotOverlyPermissive.yaml index 3b8b3ba925e..8cae921ebbc 100644 --- a/checkov/terraform/checks/graph_checks/azure/AzureSQLserverNotOverlyPermissive.yaml +++ b/checkov/terraform/checks/graph_checks/azure/AzureSQLserverNotOverlyPermissive.yaml @@ -6,13 +6,17 @@ metadata: definition: or: - cond_type: "attribute" - resource_types: "azurerm_sql_firewall_rule" + resource_types: + - "azurerm_sql_firewall_rule" + - "azurerm_mssql_firewall_rule" attribute: "start_ip_address" operator: "not_equals" value: "0.0.0.0" - cond_type: "attribute" - resource_types: "azurerm_sql_firewall_rule" + resource_types: + - "azurerm_sql_firewall_rule" + - "azurerm_mssql_firewall_rule" attribute: "end_ip_address" operator: "not_equals" value: "0.0.0.0" diff --git a/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py b/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py index 5f706ba0a59..68eb5705067 100644 --- a/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py +++ b/checkov/terraform/checks/resource/azure/SQLServerNoPublicAccess.py @@ -19,6 +19,7 @@ def __init__(self): 'azurerm_postgresql_firewall_rule', 'azurerm_mysql_firewall_rule', 'azurerm_mysql_flexible_server_firewall_rule', + 'azurerm_mssql_firewall_rule', ) categories = (CheckCategories.NETWORKING,) super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) diff --git a/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf b/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf index df65b8f8a9f..c9ed915a8d2 100644 --- a/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf +++ b/tests/terraform/checks/resource/azure/example_SQLServerNoPublicAccess/main.tf @@ -37,3 +37,17 @@ resource "azurerm_mysql_flexible_server_firewall_rule" "fail" { start_ip_address = "0.0.0.0" end_ip_address = "255.255.255.255" } + +resource "azurerm_mssql_firewall_rule" "pass" { + name = "FirewallRule1" + server_id = azurerm_mssql_server.example.id + start_ip_address = "10.0.17.62" + end_ip_address = "10.0.17.62" +} + +resource "azurerm_mssql_firewall_rule" "fail" { + name = "FirewallRule1" + server_id = azurerm_mssql_server.example.id + start_ip_address = "0.0.0.0" + end_ip_address = "255.255.255.255" +} diff --git a/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py b/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py index bb8325f8b93..278ea80d08f 100644 --- a/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py +++ b/tests/terraform/checks/resource/azure/test_SQLServerNoPublicAccess.py @@ -19,11 +19,13 @@ def test(self): passing_resources = { 'azurerm_mysql_firewall_rule.pass', 'azurerm_mysql_firewall_rule.pass2', - 'azurerm_mysql_flexible_server_firewall_rule.pass' + 'azurerm_mysql_flexible_server_firewall_rule.pass', + 'azurerm_mssql_firewall_rule.pass' } failing_resources = { 'azurerm_mysql_firewall_rule.fail', 'azurerm_mysql_flexible_server_firewall_rule.fail', + 'azurerm_mssql_firewall_rule.fail' } skipped_resources = {} diff --git a/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/expected.yaml b/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/expected.yaml index c90c8033910..9751845be77 100644 --- a/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/expected.yaml +++ b/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/expected.yaml @@ -1,6 +1,8 @@ pass: - "azurerm_sql_firewall_rule.pass_1" - "azurerm_sql_firewall_rule.pass_2" + - "azurerm_mssql_firewall_rule.pass_2" fail: - "azurerm_sql_firewall_rule.fail" + - "azurerm_mssql_firewall_rule.fail" diff --git a/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/main.tf b/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/main.tf index ea7be641587..a5b78e170cc 100644 --- a/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/main.tf +++ b/tests/terraform/graph/checks/resources/AzureSQLserverNotOverlyPermissive/main.tf @@ -18,6 +18,12 @@ resource "azurerm_sql_firewall_rule" "pass_2" { end_ip_address = "0.0.0.0" } +resource "azurerm_mssql_firewall_rule" "pass_2" { + name = "pud_AZ_SQL_FW" + start_ip_address = "10.0.0.0" + end_ip_address = "0.0.0.0" +} + # FAIL case: start_ip_address and end_ip_address equals to 0.0.0.0 @@ -29,4 +35,9 @@ resource "azurerm_sql_firewall_rule" "fail" { end_ip_address = "0.0.0.0" } +resource "azurerm_mssql_firewall_rule" "fail" { + name = "pud_AZ_SQL_FW" + start_ip_address = "0.0.0.0" + end_ip_address = "0.0.0.0" +} From b4e1a517495db754dec999995473fedcac94550a Mon Sep 17 00:00:00 2001 From: Taylor Date: Sat, 14 Dec 2024 00:01:48 -0800 Subject: [PATCH 08/10] Update check --- .../azure/VAisEnabledInStorageAccount.yaml | 2 ++ .../VAisEnabledInStorageAccount/expected.yaml | 1 + .../VAisEnabledInStorageAccount/main.tf | 20 +++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/checkov/terraform/checks/graph_checks/azure/VAisEnabledInStorageAccount.yaml b/checkov/terraform/checks/graph_checks/azure/VAisEnabledInStorageAccount.yaml index cedab96538d..bc5d9f3425b 100644 --- a/checkov/terraform/checks/graph_checks/azure/VAisEnabledInStorageAccount.yaml +++ b/checkov/terraform/checks/graph_checks/azure/VAisEnabledInStorageAccount.yaml @@ -8,9 +8,11 @@ definition: attribute: resource_type value: - azurerm_sql_server + - azurerm_mssql_server operator: within - resource_types: - azurerm_sql_server + - azurerm_mssql_server connected_resource_types: - azurerm_mssql_server_security_alert_policy operator: exists diff --git a/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/expected.yaml b/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/expected.yaml index d18a58ca052..3db4b14c6f6 100644 --- a/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/expected.yaml +++ b/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/expected.yaml @@ -1,4 +1,5 @@ pass: - "azurerm_sql_server.okExample" + - "azurerm_mssql_server.pass" fail: - "azurerm_sql_server.badExample" \ No newline at end of file diff --git a/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/main.tf b/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/main.tf index e5e2a3598b5..60c5a8b4d47 100644 --- a/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/main.tf +++ b/tests/terraform/graph/checks/resources/VAisEnabledInStorageAccount/main.tf @@ -106,4 +106,24 @@ resource "azurerm_mssql_server_vulnerability_assessment" "badExampleNotEnabled" "email@example2.com" ] } +} + +resource "azurerm_mssql_server" "pass" { + name = "mssqlserver-pass" + resource_group_name = azurerm_resource_group.okExample.name + location = azurerm_resource_group.okExample.location + version = "12.0" +} + +resource "azurerm_mssql_server_security_alert_policy" "pass" { + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_mssql_server.pass.name + state = "Enabled" + storage_endpoint = azurerm_storage_account.example.primary_blob_endpoint + storage_account_access_key = azurerm_storage_account.example.primary_access_key + disabled_alerts = [ + "Sql_Injection", + "Data_Exfiltration" + ] + retention_days = 20 } \ No newline at end of file From 1eeefb7e24fa1e8e3c1050fe081546f821faf507 Mon Sep 17 00:00:00 2001 From: Taylor Date: Sat, 14 Dec 2024 00:07:00 -0800 Subject: [PATCH 09/10] Update check --- .../resource/azure/AKSNodePublicIpDisabled.py | 25 ++++++++++++------- .../example_AKSNodePublicIpDisabled/main.tf | 14 +++++++++++ .../azure/test_AKSNodePublicIpDisabled.py | 1 + 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/checkov/terraform/checks/resource/azure/AKSNodePublicIpDisabled.py b/checkov/terraform/checks/resource/azure/AKSNodePublicIpDisabled.py index b7e8268734d..976c87580c9 100644 --- a/checkov/terraform/checks/resource/azure/AKSNodePublicIpDisabled.py +++ b/checkov/terraform/checks/resource/azure/AKSNodePublicIpDisabled.py @@ -1,21 +1,28 @@ -from checkov.common.models.enums import CheckCategories -from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck -from typing import List, Any +from typing import Dict, List, Any +from checkov.common.models.enums import CheckCategories, CheckResult +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck -class AKSNodePublicIpDisabled(BaseResourceNegativeValueCheck): - def __init__(self): + +class AKSNodePublicIpDisabled(BaseResourceCheck): + def __init__(self) -> None: name = "Ensure AKS cluster nodes do not have public IP addresses" id = "CKV_AZURE_143" supported_resources = ['azurerm_kubernetes_cluster'] categories = [CheckCategories.NETWORKING] super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) - def get_inspected_key(self) -> str: - return "default_node_pool/[0]/enable_node_public_ip" + def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult: + if 'default_node_pool' in conf: + default_node_pool = conf['default_node_pool'][0] + if isinstance(default_node_pool, dict): + if default_node_pool.get('enable_node_public_ip') == [True] or default_node_pool.get('node_public_ip_enabled') == [True]: + return CheckResult.FAILED + + return CheckResult.PASSED - def get_forbidden_values(self) -> List[Any]: - return [True] + def get_evaluated_keys(self) -> List[str]: + return ['default_node_pool/[0]/enable_node_public_ip', 'default_node_pool/[0]/node_public_ip_enabled'] check = AKSNodePublicIpDisabled() diff --git a/tests/terraform/checks/resource/azure/example_AKSNodePublicIpDisabled/main.tf b/tests/terraform/checks/resource/azure/example_AKSNodePublicIpDisabled/main.tf index 92d21a79f0d..993fdf0ab4e 100644 --- a/tests/terraform/checks/resource/azure/example_AKSNodePublicIpDisabled/main.tf +++ b/tests/terraform/checks/resource/azure/example_AKSNodePublicIpDisabled/main.tf @@ -42,4 +42,18 @@ resource "azurerm_kubernetes_cluster" "ckv_unittest_fail" { tags = { Environment = "Production" } +} + +resource "azurerm_kubernetes_cluster" "fail" { + name = "example-aks1" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + dns_prefix = "exampleaks1" + + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_D2_v2" + node_public_ip_enabled = true + } } \ No newline at end of file diff --git a/tests/terraform/checks/resource/azure/test_AKSNodePublicIpDisabled.py b/tests/terraform/checks/resource/azure/test_AKSNodePublicIpDisabled.py index 13e09aa2fb8..8e2db3e10f2 100644 --- a/tests/terraform/checks/resource/azure/test_AKSNodePublicIpDisabled.py +++ b/tests/terraform/checks/resource/azure/test_AKSNodePublicIpDisabled.py @@ -22,6 +22,7 @@ def test(self): } failing_resources = { 'azurerm_kubernetes_cluster.ckv_unittest_fail', + 'azurerm_kubernetes_cluster.fail' } skipped_resources = {} From b7d99e7630c1765f7039b2919dffc3c195ed5016 Mon Sep 17 00:00:00 2001 From: Taylor Date: Mon, 16 Dec 2024 00:37:32 -0800 Subject: [PATCH 10/10] Add types --- .../checks/resource/azure/AKSEncryptionAtHostEnabled.py | 2 +- .../terraform/checks/resource/azure/FunctionAppMinTLSVersion.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py b/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py index e673b1d0a2a..9ffd47c31f4 100644 --- a/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py +++ b/checkov/terraform/checks/resource/azure/AKSEncryptionAtHostEnabled.py @@ -24,7 +24,7 @@ def __init__(self) -> None: supported_resources=supported_resources, ) - def scan_resource_conf(self, conf): + def scan_resource_conf(self, conf) -> CheckResult: if self.entity_type == "azurerm_kubernetes_cluster": if conf.get('default_node_pool'): node_pool = conf['default_node_pool'][0] diff --git a/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py b/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py index 2f24440eb40..5630ee90804 100644 --- a/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py +++ b/checkov/terraform/checks/resource/azure/FunctionAppMinTLSVersion.py @@ -28,7 +28,7 @@ def get_inspected_key(self) -> str: else: return "site_config/[0]/minimum_tls_version" - def get_expected_value(self) -> Any: + def get_expected_value(self) -> float: return 1.2 def get_expected_values(self) -> List[Any]: