|
| 1 | +variable "region_id" { |
| 2 | + type = string |
| 3 | + default = "cn-shenzhen" |
| 4 | +} |
| 5 | + |
| 6 | +provider "alicloud" { |
| 7 | + region = var.region_id |
| 8 | +} |
| 9 | + |
| 10 | +resource "local_file" "python_script" { |
| 11 | + content = <<EOF |
| 12 | +#!/usr/bin/env python |
| 13 | +# -*- encoding: utf-8 -*- |
| 14 | +import sys |
| 15 | +sys.path.append('/opt/python') |
| 16 | +import json |
| 17 | +import logging |
| 18 | +import jmespath # 使用jmespath代替jsonpath |
| 19 | +from aliyunsdkcore.client import AcsClient |
| 20 | +from aliyunsdkcore.auth.credentials import AccessKeyCredential |
| 21 | +from aliyunsdkcore.auth.credentials import StsTokenCredential |
| 22 | +from aliyunsdkcore.request import CommonRequest |
| 23 | +
|
| 24 | +
|
| 25 | +logger = logging.getLogger() |
| 26 | +
|
| 27 | +
|
| 28 | +def handler(event, context): |
| 29 | + logger.info(f"This is event: {str(event, encoding='utf-8')}") |
| 30 | + get_resources_non_compliant(event, context) |
| 31 | +
|
| 32 | +
|
| 33 | +def get_resources_non_compliant(event, context): |
| 34 | + # 获取不合规的资源信息 |
| 35 | + resources = parse_json(event) |
| 36 | + # 遍历不合规资源,进行修正操作 |
| 37 | + for resource in resources: |
| 38 | + remediation(resource, context) |
| 39 | +
|
| 40 | +
|
| 41 | +def parse_json(content): |
| 42 | + """ |
| 43 | + Parse string to json object |
| 44 | + :param content: json string content |
| 45 | + :return: Json object |
| 46 | + """ |
| 47 | + try: |
| 48 | + return json.loads(content) |
| 49 | + except Exception as e: |
| 50 | + logger.error('Parse content:{} to json error:{}.'.format(content, e)) |
| 51 | + return None |
| 52 | +
|
| 53 | +
|
| 54 | +def remediation(resource, context): |
| 55 | + logger.info(f"需要修复的资源信息: {resource}") |
| 56 | + region_id = resource['regionId'] |
| 57 | + account_id = resource['accountId'] |
| 58 | + resource_id = resource['resourceId'] |
| 59 | + resource_type = resource['resourceType'] |
| 60 | + if resource_type == 'ACS::ECS::SecurityGroup' : |
| 61 | + # 获取不合规安全组的配置信息,重新校验,确保不合规安全组的评估准确性 |
| 62 | + resource_result = get_discovered_resource(context, resource_id, resource_type, region_id) |
| 63 | + resource_json = json.loads(resource_result) |
| 64 | + configuration = json.loads(resource_json["DiscoveredResourceDetail"]["Configuration"]) |
| 65 | + # 判断是否是托管的安全组 |
| 66 | + is_managed_security_group = configuration.get('ServiceManaged') |
| 67 | + # 使用jmespath获取入方向为接受且授权0.0.0.0/0的安全组规则id |
| 68 | + delete_security_group_rule_ids = jmespath.search( |
| 69 | + "Permissions.Permission[?SourceCidrIp=='0.0.0.0/0'].SecurityGroupRuleId", |
| 70 | + configuration |
| 71 | + ) |
| 72 | + # 非托管的安全组,且授权了0.0.0.0/0的入方向安全组规则,则删除 |
| 73 | + if is_managed_security_group is False and delete_security_group_rule_ids: |
| 74 | + logger.info(f"注意:删除安全组规则 {region_id}:{resource_id}:{delete_security_group_rule_ids}") |
| 75 | + revoke_security_group(context, region_id, resource_id, delete_security_group_rule_ids) |
| 76 | +
|
| 77 | +def revoke_security_group(context, region_id, resource_id, security_group_rule_ids): |
| 78 | + creds = context.credentials |
| 79 | + client = AcsClient(creds.access_key_id, creds.access_key_secret, region_id=region_id) |
| 80 | + request = CommonRequest() |
| 81 | + request.set_accept_format('json') |
| 82 | + request.set_domain(f'ecs.{region_id}.aliyuncs.com') |
| 83 | + request.set_method('POST') |
| 84 | + request.set_protocol_type('https') # https | http |
| 85 | + request.set_version('2014-05-26') |
| 86 | + request.set_action_name('RevokeSecurityGroup') |
| 87 | + request.add_query_param('RegionId', region_id) |
| 88 | + for index, value in enumerate(security_group_rule_ids): |
| 89 | + request.add_query_param(f'SecurityGroupRuleId.{index + 1}', value) |
| 90 | + request.add_query_param('SecurityGroupId', resource_id) |
| 91 | + request.add_query_param('SecurityToken', creds.security_token) |
| 92 | +
|
| 93 | + response = client.do_action_with_exception(request) |
| 94 | + logger.info(f"删除结果: {str(response, encoding='utf-8')}") |
| 95 | +
|
| 96 | +
|
| 97 | +# 获取资源详情 |
| 98 | +def get_discovered_resource(context, resource_id, resource_type, region_id): |
| 99 | + """ |
| 100 | + 调用API获取资源配置详情 |
| 101 | + :param context:函数计算上下文 |
| 102 | + :param resource_id:资源ID |
| 103 | + :param resource_type:资源类型 |
| 104 | + :param region_id:资源所属地域ID |
| 105 | + :return: 资源详情 |
| 106 | + """ |
| 107 | + # 需具备权限AliyunConfigFullAccess的函数计算FC的服务角色。 |
| 108 | + creds = context.credentials |
| 109 | + client = AcsClient(creds.access_key_id, creds.access_key_secret, region_id='cn-shanghai') |
| 110 | +
|
| 111 | + request = CommonRequest() |
| 112 | + request.set_domain('config.cn-shanghai.aliyuncs.com') |
| 113 | + request.set_version('2020-09-07') |
| 114 | + request.set_action_name('GetDiscoveredResource') |
| 115 | + request.add_query_param('ResourceId', resource_id) |
| 116 | + request.add_query_param('ResourceType', resource_type) |
| 117 | + request.add_query_param('Region', region_id) |
| 118 | + request.add_query_param('SecurityToken', creds.security_token) |
| 119 | + request.set_method('GET') |
| 120 | +
|
| 121 | + try: |
| 122 | + response = client.do_action_with_exception(request) |
| 123 | + resource_result = str(response, encoding='utf-8') |
| 124 | + return resource_result |
| 125 | + except Exception as e: |
| 126 | + logger.error('GetDiscoveredResource error: %s' % e) |
| 127 | + EOF |
| 128 | + filename = "${path.module}/python/index.py" |
| 129 | +} |
| 130 | + |
| 131 | +resource "local_file" "requirements_txt" { |
| 132 | + content = <<EOF |
| 133 | + aliyun-python-sdk-core==2.15.2 |
| 134 | + jmespath>=0.10.0 |
| 135 | + EOF |
| 136 | + filename = "${path.module}/python/requests/requirements.txt" |
| 137 | +} |
| 138 | +locals { |
| 139 | + code_dir = "${path.module}/python/" |
| 140 | + archive_output = "${path.module}/code.zip" |
| 141 | + base64_output = "${path.module}/code_base64.txt" |
| 142 | +} |
| 143 | + |
| 144 | +data "archive_file" "code_package" { |
| 145 | + type = "zip" |
| 146 | + source_dir = local.code_dir |
| 147 | + output_path = local.archive_output |
| 148 | + |
| 149 | + depends_on = [ |
| 150 | + local_file.python_script, |
| 151 | + local_file.requirements_txt, |
| 152 | + ] |
| 153 | +} |
| 154 | + |
| 155 | +resource "null_resource" "upload_code" { |
| 156 | + provisioner "local-exec" { |
| 157 | + command = <<EOT |
| 158 | + base64 -w 0 ${local.archive_output} > ${local.base64_output} |
| 159 | + EOT |
| 160 | + |
| 161 | + interpreter = ["sh", "-c"] |
| 162 | + } |
| 163 | + |
| 164 | + depends_on = [data.archive_file.code_package] |
| 165 | +} |
| 166 | + |
| 167 | +data "local_file" "base64_encoded_code" { |
| 168 | + filename = local.base64_output |
| 169 | + depends_on = [null_resource.upload_code] |
| 170 | +} |
| 171 | +resource "alicloud_fcv3_function" "fc_function" { |
| 172 | + runtime = "python3.10" |
| 173 | + handler = "index.handler" |
| 174 | + function_name = "HHM-FC-TEST" |
| 175 | + role = alicloud_ram_role.role.arn |
| 176 | + |
| 177 | + code { |
| 178 | + zip_file = data.local_file.base64_encoded_code.content |
| 179 | + } |
| 180 | + lifecycle { |
| 181 | + ignore_changes = [ |
| 182 | + code |
| 183 | + ] |
| 184 | + } |
| 185 | + |
| 186 | + # 显式设置 log_config 为空 |
| 187 | + log_config {} |
| 188 | + |
| 189 | + depends_on = [data.local_file.base64_encoded_code] |
| 190 | +} |
| 191 | + |
| 192 | +resource "alicloud_config_rule" "default" { |
| 193 | + rule_name = "SPM0014安全组不允许对全部网段开启风险端口" |
| 194 | + description = "禁止安全组对所有网段开放风险端口22, 3389" |
| 195 | + source_owner = "ALIYUN" |
| 196 | + # (必需,ForceNew)指定是您还是阿里云拥有并管理该规则。有效值: CUSTOM_FC: 该规则是自定义规则,您拥有该规则 ● ALIYUN: 该规则是托管规则,阿里云拥有该规则。 |
| 197 | + source_identifier = "sg-risky-ports-check" |
| 198 | + #配置审计ARN(必需,ForceNew)规则的标识符。对于托管规则,值为托管规则的名称。对于自定义规则,值为自定义规则的ARN。 |
| 199 | + resource_types_scope = ["ACS::ECS::SecurityGroup"] |
| 200 | + #规则监控被排除的资源ID,多个ID用逗号分隔,仅适用于基于托管规则创建的规则,定制规则此字段为空。 |
| 201 | + config_rule_trigger_types = "ConfigurationItemChangeNotification" #规则在配置更改时被触发 |
| 202 | + #有效值包括:One_Hour、Three_Hours、Six_Hours、Twelve_Hours、TwentyFour_Hours。 |
| 203 | + risk_level = 1 # ● 1: 严重 ● 2: 警告● 3: 信息 |
| 204 | + |
| 205 | + input_parameters = { |
| 206 | + "ports" : "22,3389" |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +resource "alicloud_config_remediation" "default" { |
| 211 | + config_rule_id = alicloud_config_rule.default.id |
| 212 | + remediation_template_id = alicloud_fcv3_function.fc_function.function_arn |
| 213 | + remediation_source_type = "CUSTOM" |
| 214 | + invoke_type = "AUTO_EXECUTION" |
| 215 | + params = "{}" |
| 216 | + remediation_type = "FC" |
| 217 | +} |
| 218 | + |
| 219 | +resource "random_integer" "default" { |
| 220 | + min = 10000 |
| 221 | + max = 99999 |
| 222 | +} |
| 223 | + |
| 224 | +resource "alicloud_ram_role" "role" { |
| 225 | + name = "tf-example-role-${random_integer.default.result}" |
| 226 | + document = <<EOF |
| 227 | +{ |
| 228 | + "Statement": [ |
| 229 | + { |
| 230 | + "Action": "sts:AssumeRole", |
| 231 | + "Effect": "Allow", |
| 232 | + "Principal": { |
| 233 | + "Service": [ |
| 234 | + "fc.aliyuncs.com" |
| 235 | + ] |
| 236 | + } |
| 237 | + } |
| 238 | + ], |
| 239 | + "Version": "1" |
| 240 | +} |
| 241 | +EOF |
| 242 | + description = "Ecs ram role." |
| 243 | + force = true |
| 244 | +} |
| 245 | +resource "alicloud_ram_policy" "policy" { |
| 246 | + policy_name = "tf-example-ram-policy-${random_integer.default.result}" |
| 247 | + policy_document = <<EOF |
| 248 | + { |
| 249 | + "Statement": [ |
| 250 | + { |
| 251 | + "Action": [ |
| 252 | + "config:GetDiscoveredResource", |
| 253 | + "ecs:RevokeSecurityGroup" |
| 254 | + ], |
| 255 | + "Effect": "Allow", |
| 256 | + "Resource": ["*"] |
| 257 | + } |
| 258 | + ], |
| 259 | + "Version": "1" |
| 260 | + } |
| 261 | + EOF |
| 262 | + description = "this is a policy test" |
| 263 | + force = true |
| 264 | +} |
| 265 | + |
| 266 | +resource "alicloud_ram_role_policy_attachment" "attach" { |
| 267 | + policy_name = alicloud_ram_policy.policy.policy_name |
| 268 | + policy_type = "Custom" |
| 269 | + role_name = alicloud_ram_role.role.name |
| 270 | +} |
0 commit comments