From 71a4d228304d6702231e2722ca072e649a1e50dc Mon Sep 17 00:00:00 2001 From: Kuemjong Jeong Date: Mon, 3 Oct 2022 00:57:42 +0900 Subject: [PATCH] feat(terraform): add CKV_NCP_1 about lb target group health check, CKV_NCP_2 about access control group description (#3569) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [22.09.27][추가] CKV_NCP_1 * [22.09.27][추가] CKV_NCP_2 * Apply suggestions from code review Co-authored-by: Anton Grübel * Apply suggestions from code review Co-authored-by: Anton Grübel * Apply suggestions from code review Co-authored-by: Anton Grübel * Create main.yml * [22.09.28][수정] Lint test * Delete main.yml * [22.09.29][수정]testcode 수정 * [22.09.29][수정] 테스트 코드 수정 * [22.09.29][수정] 테스트코드 수정 * [22.09.29][수정] add test resource for 'ncloud_access_control_group_rule' Co-authored-by: pj991207 Co-authored-by: Anton Grübel --- checkov/terraform/checks/resource/__init__.py | 1 + .../ncp/AccessControlGroupRuleDescription.py | 47 +++++++++++ .../ncp/LBTargetGroupDefinesHealthCheck.py | 24 ++++++ .../terraform/checks/resource/ncp/__init__.py | 4 + pytest.ini | 2 +- .../terraform/checks/resource/ncp/__init__.py | 0 .../main.tf | 78 +++++++++++++++++++ .../main.tf | 26 +++++++ .../test_AccessControlGroupRuleDescription.py | 43 ++++++++++ .../test_LBTargetGroupDefinesHealthCheck.py | 40 ++++++++++ 10 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 checkov/terraform/checks/resource/ncp/AccessControlGroupRuleDescription.py create mode 100644 checkov/terraform/checks/resource/ncp/LBTargetGroupDefinesHealthCheck.py create mode 100644 checkov/terraform/checks/resource/ncp/__init__.py create mode 100644 tests/terraform/checks/resource/ncp/__init__.py create mode 100644 tests/terraform/checks/resource/ncp/example_AccessControlGroupRuleDescription/main.tf create mode 100644 tests/terraform/checks/resource/ncp/example_LBTargetGroupDefinesHealthCheck/main.tf create mode 100644 tests/terraform/checks/resource/ncp/test_AccessControlGroupRuleDescription.py create mode 100644 tests/terraform/checks/resource/ncp/test_LBTargetGroupDefinesHealthCheck.py diff --git a/checkov/terraform/checks/resource/__init__.py b/checkov/terraform/checks/resource/__init__.py index 1325e4f5ccc..e28fd64ce5f 100644 --- a/checkov/terraform/checks/resource/__init__.py +++ b/checkov/terraform/checks/resource/__init__.py @@ -11,3 +11,4 @@ from checkov.terraform.checks.resource.alicloud import * # noqa from checkov.terraform.checks.resource.kubernetes import * # noqa from checkov.terraform.checks.resource.yandexcloud import * # noqa +from checkov.terraform.checks.resource.ncp import * # noqa diff --git a/checkov/terraform/checks/resource/ncp/AccessControlGroupRuleDescription.py b/checkov/terraform/checks/resource/ncp/AccessControlGroupRuleDescription.py new file mode 100644 index 00000000000..2e66bb093db --- /dev/null +++ b/checkov/terraform/checks/resource/ncp/AccessControlGroupRuleDescription.py @@ -0,0 +1,47 @@ +from checkov.common.models.enums import CheckResult, CheckCategories +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck + + +class AccessControlGroupRuleDescription(BaseResourceCheck): + def __init__(self): + name = "Ensure every access control groups rule has a description" + id = "CKV_NCP_002" + supported_resource = [ + 'ncloud_access_control_group', + 'ncloud_access_control_group_rule', + ] + categories = [CheckCategories.NETWORKING] + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resource) + + def scan_resource_conf(self, conf): + """ + https://registry.terraform.io/providers/NaverCloudPlatform/ncloud/latest/docs/resources/access_control_group + :return: + """ + group_result = self.check_rule(rule_type='group_or_rule_description', conf=conf) + if 'type' not in conf.keys(): + outbound_result = self.check_rule(rule_type='outbound', conf=conf) + inbound_result = self.check_rule(rule_type='inbound', conf=conf) + if group_result == CheckResult.PASSED or (outbound_result == CheckResult.PASSED and inbound_result == CheckResult.PASSED): + return CheckResult.PASSED + return CheckResult.FAILED + + return group_result + + def check_rule(self, rule_type, conf): + + if rule_type == 'group_or_rule_description': + self.evaluated_keys = ['description'] + if conf.get('description'): + return CheckResult.PASSED + return CheckResult.FAILED + + if rule_type in conf.keys(): + for rule in conf[rule_type]: + if isinstance(rule, dict) and rule.get('description'): + self.evaluated_keys.append(f'{rule_type}/[{conf[rule_type].index(rule)}]') + return CheckResult.PASSED + return CheckResult.FAILED + + +check = AccessControlGroupRuleDescription() diff --git a/checkov/terraform/checks/resource/ncp/LBTargetGroupDefinesHealthCheck.py b/checkov/terraform/checks/resource/ncp/LBTargetGroupDefinesHealthCheck.py new file mode 100644 index 00000000000..7e3db3b44b1 --- /dev/null +++ b/checkov/terraform/checks/resource/ncp/LBTargetGroupDefinesHealthCheck.py @@ -0,0 +1,24 @@ +from checkov.common.models.enums import CheckResult, CheckCategories +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck + + +class LBTargetGroupDefinesHealthCheck(BaseResourceCheck): + def __init__(self): + name = "Ensure HTTP HTTPS Target group defines Healthcheck" + id = "CKV_NCP_1" + supported_resources = ['ncloud_lb_target_group'] + categories = [CheckCategories.GENERAL_SECURITY] + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def scan_resource_conf(self, conf): + if conf.get("protocol") in (["HTTP"], ["HTTPS"]): + health_checks = conf.get("health_check") + if health_checks and isinstance(health_checks, list): + healthcheck = health_checks[0] + if isinstance(healthcheck, dict) and healthcheck.get("url_path"): + return CheckResult.PASSED + return CheckResult.FAILED + return CheckResult.UNKNOWN + + +check = LBTargetGroupDefinesHealthCheck() \ No newline at end of file diff --git a/checkov/terraform/checks/resource/ncp/__init__.py b/checkov/terraform/checks/resource/ncp/__init__.py new file mode 100644 index 00000000000..bfe83e75bf9 --- /dev/null +++ b/checkov/terraform/checks/resource/ncp/__init__.py @@ -0,0 +1,4 @@ +from pathlib import Path + +modules = Path(__file__).parent.glob("*.py") +__all__ = [f.stem for f in modules if f.is_file() and not f.stem == "__init__"] diff --git a/pytest.ini b/pytest.ini index c30a1d3d655..cdc07445a3e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,3 @@ [pytest] addopts = -n 2 --dist loadfile -asyncio_mode = auto + diff --git a/tests/terraform/checks/resource/ncp/__init__.py b/tests/terraform/checks/resource/ncp/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/terraform/checks/resource/ncp/example_AccessControlGroupRuleDescription/main.tf b/tests/terraform/checks/resource/ncp/example_AccessControlGroupRuleDescription/main.tf new file mode 100644 index 00000000000..7fc36a0a759 --- /dev/null +++ b/tests/terraform/checks/resource/ncp/example_AccessControlGroupRuleDescription/main.tf @@ -0,0 +1,78 @@ +resource "ncloud_access_control_group" "pass" { + name = "example-acg" + vpc_no = data.ncloud_vpc.selected.id + description = "description" +} + + +resource "ncloud_access_control_group" "fail" { + name = "example-acg" + vpc_no = data.ncloud_vpc.selected.id +} + + +resource "ncloud_access_control_group_rule" "pass" { + access_control_group_no = ncloud_access_control_group.acg.id + + inbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "22" + description = "inbound 22" + } + inbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "80" + description = "inbound 80" + } + outbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "1-65535" + description = "accept 1-65535 port" + } +} + + +resource "ncloud_access_control_group_rule" "fail" { + access_control_group_no = ncloud_access_control_group.acg.id + + inbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "22" + description = "inbound 22" + } + inbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "80" + } + outbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "1-65535" + } +} + + +resource "ncloud_access_control_group_rule" "fail2" { + access_control_group_no = ncloud_access_control_group.acg.id + + inbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "22" + } + inbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "80" + } + outbound { + protocol = "TCP" + ip_block = "0.0.0.0/0" + port_range = "1-65535" + } +} diff --git a/tests/terraform/checks/resource/ncp/example_LBTargetGroupDefinesHealthCheck/main.tf b/tests/terraform/checks/resource/ncp/example_LBTargetGroupDefinesHealthCheck/main.tf new file mode 100644 index 00000000000..fdbcb842499 --- /dev/null +++ b/tests/terraform/checks/resource/ncp/example_LBTargetGroupDefinesHealthCheck/main.tf @@ -0,0 +1,26 @@ + +resource "ncloud_lb_target_group" "pass" { + vpc_no = ncloud_vpc.main.vpc_no + protocol = "HTTP" + target_type = "VSVR" + port = 8080 + description = "for test" + health_check { + protocol = "HTTP" + http_method = "GET" + port = 8080 + url_path = "/monitor/l7check" + cycle = 30 + up_threshold = 2 + down_threshold = 2 + } + algorithm_type = "RR" +} +resource "ncloud_lb_target_group" "fail" { + vpc_no = ncloud_vpc.main.vpc_no + protocol = "HTTP" + target_type = "VSVR" + port = 8080 + description = "for test" + algorithm_type = "RR" +} \ No newline at end of file diff --git a/tests/terraform/checks/resource/ncp/test_AccessControlGroupRuleDescription.py b/tests/terraform/checks/resource/ncp/test_AccessControlGroupRuleDescription.py new file mode 100644 index 00000000000..a922dc8f905 --- /dev/null +++ b/tests/terraform/checks/resource/ncp/test_AccessControlGroupRuleDescription.py @@ -0,0 +1,43 @@ +import unittest +from pathlib import Path + +from checkov.runner_filter import RunnerFilter +from checkov.terraform.checks.resource.ncp.AccessControlGroupRuleDescription import check +from checkov.terraform.runner import Runner + + +class TestAccessControlGroupRuleDescription(unittest.TestCase): + def test(self): + # given + test_files_dir = Path(__file__).parent / "example_AccessControlGroupRuleDescription" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "ncloud_access_control_group.pass", + "ncloud_access_control_group_rule.pass", + } + failing_resources = { + "ncloud_access_control_group.fail", + "ncloud_access_control_group_rule.fail", + "ncloud_access_control_group_rule.fail2", + } + + passed_check_resources = {c.resource for c in report.passed_checks} + failed_check_resources = {c.resource for c in report.failed_checks} + + self.assertEqual(summary["passed"], 2) + self.assertEqual(summary["failed"], 3) + self.assertEqual(summary["skipped"], 0) + self.assertEqual(summary["parsing_errors"], 0) + + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/terraform/checks/resource/ncp/test_LBTargetGroupDefinesHealthCheck.py b/tests/terraform/checks/resource/ncp/test_LBTargetGroupDefinesHealthCheck.py new file mode 100644 index 00000000000..47e6f7a4962 --- /dev/null +++ b/tests/terraform/checks/resource/ncp/test_LBTargetGroupDefinesHealthCheck.py @@ -0,0 +1,40 @@ +import unittest +from pathlib import Path + +from checkov.runner_filter import RunnerFilter +from checkov.terraform.checks.resource.ncp.LBTargetGroupDefinesHealthCheck import check +from checkov.terraform.runner import Runner + + +class TestLBTargetGroupDefinesHealthCheck(unittest.TestCase): + def test(self): + # given + test_files_dir = Path(__file__).parent / "example_LBTargetGroupDefinesHealthCheck" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "ncloud_lb_target_group.pass", + } + failing_resources = { + "ncloud_lb_target_group.fail", + } + + passed_check_resources = {c.resource for c in report.passed_checks} + failed_check_resources = {c.resource for c in report.failed_checks} + + self.assertEqual(summary["passed"], 1) + self.assertEqual(summary["failed"], 1) + self.assertEqual(summary["skipped"], 0) + self.assertEqual(summary["parsing_errors"], 0) + + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file