variable "region_id" {
type = string
default = "cn-shenzhen"
}
# main.tf
provider "alicloud" {
region = var.region_id
}
resource "local_file" "python_script" {
content = <<EOF
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import sys
sys.path.append('/opt/python')
import json
import logging
import jmespath # Gunakan jmespath sebagai pengganti jsonpath.
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_openapi.client import Client as OpenApiClient
from alibabacloud_openapi_util.client import Client as OpenApiUtilClient
from alibabacloud_tea_util import models as util_models
logger = logging.getLogger()
def handler(event, context):
logger.info(f"Ini adalah event: {str(event, encoding='utf-8')}")
get_resources_non_compliant(event, context)
def get_resources_non_compliant(event, context):
# Dapatkan informasi tentang resource yang tidak sesuai.
resources = parse_json(event)
# Lakukan iterasi terhadap resource yang tidak sesuai dan lakukan remediasi.
for resource in resources:
remediation(resource, context)
def parse_json(content):
"""
Uraikan string menjadi objek json
:param content: konten string json
:return: Objek Json
"""
try:
return json.loads(content)
except Exception as e:
logger.error('Gagal mengurai konten:{} menjadi json: {}.'.format(content, e))
return None
def remediation(resource, context):
logger.info(f"Informasi tentang resource yang akan diperbaiki: {resource}")
region_id = resource['regionId']
account_id = resource['accountId']
resource_id = resource['resourceId']
resource_type = resource['resourceType']
if resource_type == 'ACS::ECS::SecurityGroup':
# Dapatkan konfigurasi security group yang tidak sesuai dan verifikasi ulang untuk memastikan akurasi penilaian.
resource_result = get_discovered_resource(context, resource_id, resource_type, region_id)
configuration = json.loads(resource_result["body"]["DiscoveredResourceDetail"]["Configuration"])
# Periksa apakah security group tersebut merupakan security group yang dikelola.
is_managed_security_group = configuration.get('ServiceManaged')
# Gunakan jmespath untuk mendapatkan ID aturan security group yang memiliki arah inbound dan memberikan akses ke 0.0.0.0/0.
delete_security_group_rule_ids = jmespath.search(
"Permissions.Permission[?SourceCidrIp=='0.0.0.0/0'].SecurityGroupRuleId",
configuration
)
# Jika security group bukan security group yang dikelola dan memiliki aturan inbound yang memberikan akses ke 0.0.0.0/0, hapus aturan tersebut.
if is_managed_security_group is False and delete_security_group_rule_ids:
logger.info(f"Catatan: Menghapus aturan security group {region_id}:{resource_id}:{delete_security_group_rule_ids}")
revoke_security_group(context, region_id, resource_id, delete_security_group_rule_ids)
def revoke_security_group(context, region_id, resource_id, security_group_rule_ids):
creds = context.credentials
config = open_api_models.Config(
access_key_id=creds.access_key_id,
access_key_secret=creds.access_key_secret,
security_token=creds.security_token,
endpoint=f'ecs.{region_id}.aliyuncs.com'
)
client = OpenApiClient(config)
params = open_api_models.Params(
style='RPC', # Gaya API
version='2014-05-26', # Nomor versi API
action='RevokeSecurityGroup', # Nama API
method='POST', # Metode permintaan
pathname='/', # Jalur API. Jalur default untuk API RPC adalah "/".
protocol='HTTPS', # Protokol API.
auth_type='AK',
req_body_type='json', # Format badan permintaan.
body_type='json' # Format badan respons.
)
query = {'RegionId': region_id, 'SecurityGroupId': resource_id, 'SecurityGroupRuleId': security_group_rule_ids}
# Buat objek permintaan API.
request = open_api_models.OpenApiRequest(
query=OpenApiUtilClient.query(query),
)
runtime = util_models.RuntimeOptions()
response = client.call_api(params, request, runtime)
logger.info(f"Hasil penghapusan: {response}")
# Dapatkan detail resource.
def get_discovered_resource(context, resource_id, resource_type, region_id):
"""
Panggil operasi API untuk mendapatkan detail konfigurasi resource.
:param context: Konteks Function Compute.
:param resource_id: ID resource.
:param resource_type: Jenis resource.
:param region_id: ID wilayah tempat resource berada.
:return: Detail resource.
"""
# Peran layanan untuk Function Compute (FC) harus memiliki izin AliyunConfigFullAccess.
creds = context.credentials
config = open_api_models.Config(
access_key_id=creds.access_key_id,
access_key_secret=creds.access_key_secret,
security_token=creds.security_token,
endpoint='config.cn-shanghai.aliyuncs.com'
)
client = OpenApiClient(config)
params = open_api_models.Params(
style='RPC', # Gaya API
version='2020-09-07', # Nomor versi API
action='GetDiscoveredResource', # Nama API
method='POST', # Metode permintaan
pathname='/', # Jalur API. Jalur default untuk API RPC adalah "/".
protocol='HTTPS', # Protokol API.
auth_type='AK',
req_body_type='json', # Format badan permintaan.
body_type='json' # Format badan respons.
)
query = {'ResourceId': resource_id, 'ResourceType': resource_type, 'Region': region_id}
# Buat objek permintaan API.
request = open_api_models.OpenApiRequest(
query=OpenApiUtilClient.query(query),
)
runtime = util_models.RuntimeOptions()
try:
response = client.call_api(params, request, runtime)
return response
except Exception as e:
logger.error('Kesalahan GetDiscoveredResource: %s' % e)
EOF
filename = "${path.module}/python/index.py"
}
resource "local_file" "requirements_txt" {
content = <<EOF
alibabacloud-tea-openapi
jmespath>= 0.10.0
EOF
filename = "${path.module}/python/requests/requirements.txt"
}
locals {
code_dir = "${path.module}/python/"
archive_output = "${path.module}/code.zip"
base64_output = "${path.module}/code_base64.txt"
}
data "archive_file" "code_package" {
type = "zip"
source_dir = local.code_dir
output_path = local.archive_output
depends_on = [
local_file.python_script,
local_file.requirements_txt,
]
}
resource "null_resource" "upload_code" {
provisioner "local-exec" {
command = <<EOT
base64 -w 0 ${local.archive_output} > ${local.base64_output}
EOT
interpreter = ["sh", "-c"]
}
depends_on = [data.archive_file.code_package]
}
data "local_file" "base64_encoded_code" {
filename = local.base64_output
depends_on = [null_resource.upload_code]
}
resource "alicloud_fcv3_function" "fc_function" {
runtime = "python3.10"
handler = "index.handler"
function_name = "HHM-FC-TEST"
role = alicloud_ram_role.role.arn
code {
zip_file = data.local_file.base64_encoded_code.content
}
lifecycle {
ignore_changes = [
code
]
}
# Secara eksplisit atur log_config menjadi kosong.
log_config {}
depends_on = [data.local_file.base64_encoded_code]
}
resource "alicloud_config_rule" "default" {
rule_name = "SPM0014-sg-disallow-risky-ports-for-all-ips"
description = "Melarang security group membuka port rentan 22 dan 3389 ke seluruh rentang IP."
source_owner = "ALIYUN"
# (Wajib, ForceNew) Menentukan apakah aturan dimiliki dan dikelola oleh Anda atau Alibaba Cloud. Nilai yang valid: CUSTOM_FC: Aturan adalah aturan kustom milik Anda. ● ALIYUN: Aturan adalah aturan terkelola milik Alibaba Cloud.
source_identifier = "sg-risky-ports-check"
# Pengidentifikasi aturan. Untuk aturan terkelola, nilainya adalah nama aturan terkelola tersebut. Untuk aturan kustom, nilainya adalah Nama Sumber Daya Alibaba Cloud (ARN) dari aturan kustom tersebut. (Wajib, ForceNew)
resource_types_scope = ["ACS::ECS::SecurityGroup"]
# ID resource yang dikecualikan dari cakupan pemantauan. Pisahkan beberapa ID dengan koma (,). Parameter ini hanya berlaku untuk aturan yang dibuat berdasarkan aturan terkelola. Untuk aturan kustom, parameter ini kosong.
config_rule_trigger_types = "ConfigurationItemChangeNotification" # Aturan dipicu saat konfigurasi berubah.
# Nilai yang valid: One_Hour, Three_Hours, Six_Hours, Twelve_Hours, dan TwentyFour_Hours.
risk_level = 1 # ● 1: Kritis ● 2: Peringatan ● 3: Informasi
input_parameters = {
"ports" : "22,3389"
}
}
resource "alicloud_config_remediation" "default" {
config_rule_id = alicloud_config_rule.default.id
remediation_template_id = alicloud_fcv3_function.fc_function.function_arn
remediation_source_type = "CUSTOM"
invoke_type = "AUTO_EXECUTION"
params = "{}"
remediation_type = "FC"
}
resource "random_integer" "default" {
min = 10000
max = 99999
}
resource "alicloud_ram_role" "role" {
name = "tf-example-role-${random_integer.default.result}"
document = <<EOF
{
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": [
"fc.aliyuncs.com"
]
}
}
],
"Version": "1"
}
EOF
description = "Peran ram ECS."
force = true
}
resource "alicloud_ram_policy" "policy" {
policy_name = "tf-example-ram-policy-${random_integer.default.result}"
policy_document = <<EOF
{
"Statement": [
{
"Action": [
"config:GetDiscoveredResource",
"ecs:RevokeSecurityGroup"
],
"Effect": "Allow",
"Resource": ["*"]
}
],
"Version": "1"
}
EOF
description = "Ini adalah uji coba kebijakan."
force = true
}
resource "alicloud_ram_role_policy_attachment" "attach" {
policy_name = alicloud_ram_policy.policy.policy_name
policy_type = "Custom"
role_name = alicloud_ram_role.role.name
}