AWS Config를 사용하여 보안 그룹에 허용된 인바운드 규칙만 생성할 수 있도록 하는 방안

8분 분량
콘텐츠 수준: 중급
1

해당 기사에서는 AWS Config 와 AWS Lambda를 사용하여 보안 그룹에 허용된 인바운드 규칙만 생성이 가능하도록 하고 허용되지 않은 규칙 생성 시 허용 된 인바운드 규칙으로 자동 변경하도록 하는 방안에 대해서 설명합니다.

사내 규정에 따라 보안 그룹에 허용 가능한 규칙이 지정되어 있을 수 있습니다. 보안 그룹에 허용이 불 가능한 규칙이 생성된다면, 허용되지 않은 액세스 및 트래픽 공격 등 다양한 보안 위험에 노출될 수 있고, 보안 그룹을 관리자가 수동으로 관리하기에는 관리의 어려움 및 변경 추적 어려움 등 제한적인 사항이 있습니다. 이를 AWS ConfigAWS Lambda를 사용하여 보안 그룹에 허용된 규칙만 생성하도록 할 수 있습니다. Config는 리소스 변경 사항에 대한 세부 정보를 기록하여 구성 기록을 제공합니다. 이를 사용하여 리소스의 구성 및 관계에 대한 지속적인 진단, 감사 및 평가를 수행할 수 있습니다. 또한, Config는 관리형 규칙과 사용자가 지정한 규칙 기반으로 리소스의 규정 준수 상태를 평가할 수 있습니다. 이를 사용하여 허용되지 않는 리소스 변경에 대한 평가 결과를 Lambda 함수를 통하여 자동 조치할 수 있습니다.

해당 기사에 최종 목적은 Config 사용자 정의 Lambda 규칙을 사용하여 화이트 리스트에 등록된 보안 그룹을 제외한 보안 그룹에는 허용된 인바운드 규칙만 생성이 가능하도록 하고 허용되지 않는 규칙 생성 시, 보안 그룹에 허용되지 않은 규칙을 삭제 하고, 허용된 규칙으로 자동 변경 조치하도록 합니다. 해당 기사에서는 보안 그룹에 아웃바운드 규칙은 제어하지 않습니다.

사전 요구 사항

  1. AWS Lambda 함수
  2. AWS Config 사용자 정의 규칙
  3. 테스트 용도의 보안 그룹
  4. 로그 확인 및 알림 용도의 AWS CloudWatch, Amazon SNS

단계1. AWS Lambda 함수 생성

AWS Lambda 함수를 사용하여 사용자 정의 코드를 개발하고 AWS Config와 연동할 수 있습니다. 해당 함수를 Config 규칙과 연결하면 리소스 구성 변경에 대한 응답으로 Lambda 함수를 호출합니다. 이후, Lambda 함수는 리소스가 규칙을 준수하는지 여부를 평가하고 평가 결과를 Config로 보냅니다. [1]

해당 Lambda 함수는 EXCEPTION_SECURITY_GROUPS에 포함된 보안 그룹을 제외한 보안 그룹이 REQUIRED_PERMISSIONS에 포함된 규칙만 생성이 가능하며, 다른 규칙을 생성 시 자동으로 REQUIRED_PERMISSIONS에 규칙으로 변경하도록 조치합니다. Config에 대한 평가 결과로 “NOT_APPLICABLE”로 표시되는 경우는 보안 그룹이 아닌 다른 리소스를 평가하거나 보안 그룹이 삭제된 경우, “NON_COMPLIANT”의 경우 REQUIRED_PERMISSIONS에 규칙과 일치하지 않는 경우, “COMPLIANT”의 경우 규칙이 REQUIRED_PERMISSIONS과 일치하는 경우입니다.

import json
import boto3
import botocore

APPLICABLE_RESOURCES = ["AWS::EC2::SecurityGroup"]

# Specify the required ingress permissions, fix when rule evaluates to 'NON_COMPLIANT'
REQUIRED_PERMISSIONS = [
{
    "IpProtocol" : "tcp",
    "FromPort" : 80,
    "ToPort" : 80,
    "UserIdGroupPairs" : [],
    "IpRanges" : [{"CidrIp" : "0.0.0.0/0"}],
    "PrefixListIds" : [],
    "Ipv6Ranges": [
    ]
},
{
    "IpProtocol" : "tcp",
    "FromPort" : 443,
    "ToPort" : 443,
    "UserIdGroupPairs" : [],
    "IpRanges" : [{"CidrIp" : "0.0.0.0/0"}],
    "PrefixListIds" : [],
    "Ipv6Ranges": [
    ]
}]

# This is a security group exception list and the list is not evaluated.
EXCEPTION_SECURITY_GROUPS = ["sg-0a69caa14e5017bd2"]

def normalize_parameters(rule_parameters):
    for key, value in rule_parameters.items():
        normalized_key=key.lower()
        normalized_value=value

        if normalized_value == "true":
            rule_parameters[normalized_key] = True
        elif normalized_value == "false":
            rule_parameters[normalized_key] = False
        else:
            rule_parameters[normalized_key] = True
        rule_parameters[normalized_key] = rule_parameters.pop(normalized_key)
    return rule_parameters


def evaluate_compliance(configuration_item, debug_enabled):
    if configuration_item["resourceType"] not in APPLICABLE_RESOURCES:
        return {
            "compliance_type" : "NOT_APPLICABLE",
            "annotation" : "The rule doesn't apply to resources of type " +
            configuration_item["resourceType"] + "."
        }

    if configuration_item["configurationItemStatus"] == "ResourceDeleted":
        return {
            "compliance_type": "NOT_APPLICABLE",
            "annotation": "The configurationItem was deleted and therefore cannot be validated."
        }

    group_id = configuration_item["configuration"]["groupId"]
    client = boto3.client("ec2");

    if group_id in EXCEPTION_SECURITY_GROUPS:
        return {
            "compliance_type": "COMPLIANT",
            "annotation": "The group is in the exception list."
        }

    try:
        response = client.describe_security_groups(GroupIds=[group_id])
    except botocore.exceptions.ClientError as e:
        return {
            "compliance_type" : "NON_COMPLIANT",
            "annotation" : "describe_security_groups failure on group " + group_id
        }

    if debug_enabled:
        print(("security group definition: ", json.dumps(response, indent=2)))

    ip_permissions = response["SecurityGroups"][0]["IpPermissions"]
    authorize_permissions = [item for item in REQUIRED_PERMISSIONS]
    revoke_permissions = [item for item in ip_permissions]

    if authorize_permissions or revoke_permissions:
        annotation_message = "Permissions were modified."
    else:
        annotation_message = "Permissions are correct."

    if revoke_permissions:
        if debug_enabled:
            print(("revoking for ", group_id, ", ip_permissions ", json.dumps(revoke_permissions, indent=2)))

        try:
            client.revoke_security_group_ingress(GroupId=group_id, IpPermissions=revoke_permissions)
            annotation_message += " " + str(len(revoke_permissions)) +" new revocation(s)."
        except botocore.exceptions.ClientError as e:
            return {
                "compliance_type" : "NON_COMPLIANT",
                "annotation" : "revoke_security_group_ingress failure on group " + group_id
            }

    if authorize_permissions:
        if debug_enabled:
            print(("authorizing for ", group_id, ", ip_permissions ", json.dumps(authorize_permissions, indent=2)))

        try:
            client.authorize_security_group_ingress(GroupId=group_id, IpPermissions=authorize_permissions)
            annotation_message += " " + str(len(authorize_permissions)) +" new authorization(s)."
        except botocore.exceptions.ClientError as e:
            print(("Error - "+ str(e)))
            return {
                "compliance_type" : "NON_COMPLIANT",
                "annotation" : "authorize_security_group_ingress failure on group " + group_id
            }

    return {
        "compliance_type": "COMPLIANT",
        "annotation": annotation_message
    }

def send_sns_message(sns_topic_arn, subject, message):
    sns = boto3.client('sns')
    response = sns.publish(
        TopicArn=sns_topic_arn,
        Subject=subject,
        Message=message
    )
    return response

def lambda_handler(event, context):
    invoking_event = json.loads(event['invokingEvent'])
    configuration_item = invoking_event["configurationItem"]
    rule_parameters = normalize_parameters(json.loads(event["ruleParameters"]))

    debug_enabled = True

    if "debug" in rule_parameters:
        debug_enabled = rule_parameters["debug"]

    if debug_enabled:
        print("Received event: " + json.dumps(event, indent=2))

    evaluation = evaluate_compliance(configuration_item, debug_enabled)

    security_group_id = configuration_item['configuration']['groupId']
    sns_topic_arn = 'arn:aws:sns:ap-northeast-2:123456789012:MyTopic'  # Input your SNS topic ARN here
    subject = 'Lambda function result'
    message = 'The evaluation result for security group ID {}: {}'.format(security_group_id, evaluation)

    send_sns_message(sns_topic_arn, subject, message)

    config = boto3.client('config')

    response = config.put_evaluations(
       Evaluations=[
           {
               'ComplianceResourceType': invoking_event['configurationItem']['resourceType'],
               'ComplianceResourceId': invoking_event['configurationItem']['resourceId'],
               'ComplianceType': evaluation["compliance_type"],
               "Annotation": evaluation["annotation"],
               'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime']
           },
       ],
       ResultToken=event['resultToken'])

  • AWS Lambda 함수의 실행 역할에 아래 정책을 연결하여 AWS CloudWatch 로그 그룹 생성 및 스트림 생성과 로그 이벤트를 게시하도록 합니다. 또한 Config 평가 결과를 게시하도록 하고 보안 그룹을 읽고 인바운드 규칙을 추가 및 제거할 수 있도록 합니다.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "config:PutEvaluations",
                "ec2:DescribeSecurityGroups",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

  • AWS Config 평가 결과를 Amazon S3 버킷으로 전달이 필요한 경우 아래와 같은 정책을 Lambda 실행 역할에 연결합니다. S3 버킷에 대한 Config 액세스 권한을 부여하기 위한 S3 버킷 정책은 다음 문서를 참고할 수 있습니다. [2]
{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Action":[
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Resource":[
        "arn:aws:s3:::myBucketName/prefix/AWSLogs/myAccountID/*"
      ],
      "Condition":{
        "StringLike":{
          "s3:x-amz-acl":"bucket-owner-full-control"
        }
      }
    },
    {
      "Effect":"Allow",
      "Action":[
        "s3:GetBucketAcl"
      ],
      "Resource":"arn:aws:s3:::myBucketName"
    }
  ]
}

  • AWS Config 평가 결과를 Amazon SNS로 전달이 필요한 경우 아래와 같은 정책을 AWS Lambda 실행 역할에 연결합니다. [3] Amazon SNS를 구성하는 방법은 다음 문서를 참고할 수 있습니다. [4] 다른 계정이 소유한 SNS 주제에 권한을 부여하기 위해서는 SNS 주제에 다음 문서의 정책을 연결해야 합니다. [5]
{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": "sns:Publish",
        "Resource": "arn:aws:sns:ap-northeast-2:123456789012:MyTopic"
    }]
}

여기에 이미지 설명 입력

  • AWS Lambda 리소스 기반 정책을 사용하여 Config와 CloudWatch가 함수를 호출하도록 허용합니다. 여기에 이미지 설명 입력

보안 모범 사례로서 Service Principal Name(SPN)에 대해 Lambda 함수를 호출하는 호출 권한을 부여하지 않으려면 호출 요청에서 sourceARN 또는 sourceAccountId를 사용하여 Lambda 리소스 기반 정책에서 액세스를 제한하는 것이 좋습니다. [6]

  • AWS Lambda 함수가 평가해야 하는 리소스가 많은 경우 함수의 제한 시간 변경이 필요할 수 있습니다.

단계 2. AWS Config 사용자 정의 Lambda 규칙 생성

사용자 정의 Lambda 규칙은 Java 또는 Python을 사용하여 Config 사용자 정의 Lambda 규칙에 대한 Lambda 함수를 생성하는 옵션을 제공합니다. Lambda 함수는 Lambda에 업로드하는 사용자 정의 코드이며, 이벤트 소스에 의해 게시되는 이벤트에 의해 호출됩니다. Lambda 함수가 Config 규칙과 연결된 경우 Config는 규칙이 시작될 때 이를 호출합니다. 그런 다음 Lambda 함수는 Config에서 전송한 구성 정보를 평가하고 평가 결과를 반환합니다. [7]

여기에 이미지 설명 입력

여기에 이미지 설명 입력

규칙의 평가 모드를 탐정 평가 모드로 설정하여 기존 리소스와 리소스 구성 변경 시 규칙이 트리거가 될 수 있게 설정합니다. 평가 모드에 대한 설명은 다음 문서를 참고할 수 있습니다. [8]

규칙의 리소스 유형이 지정되지 않은 경우 Config 사용자 정의 Lambda 규칙으로 인해 많은 수의 Lambda 함수 호출이 발생할 수 있습니다. 리소스 유형을 선택하지 않으면 규칙은 계정의 모든 리소스에 대해 Lambda 함수를 호출하기에, 사용자 정의 Lambda 규칙 리소스를 지정하는 것을 권장합니다. 해당 기사에서는 보안 그룹 리소스 변경이 발생하면 규칙을 평가하기에, 리소스를 보안 그룹으로 지정합니다.

단계 3. AWS Config 규칙 트리거 확인

생성한 규칙이 정상적으로 동작 하는지 확인하기 위해 “REQUIRED_PERMISSIONS”에서 허용하지 않는 인바운드 규칙인 보안 그룹을 생성합니다.

  1. 인바운드 규칙(0.0.0.0/0: SSH)인 보안 그룹을 생성 후 Config 트리거 후 보안 그룹을 확인하면, “REQUIRED_PERMISSION”에 지정한 인바운드 규칙으로 자동 변경된 것을 확인할 수 있습니다. 여기에 이미지 설명 입력 여기에 이미지 설명 입력
  2. AWS Config 규칙 확인 시, 허용되지 않은 인바운드 규칙이 삭제 되고 허용 된 인바운드 규칙으로 교체 후 COMPLIANT로 평가하는 것을 확인할 수 있습니다. 여기에 이미지 설명 입력
  3. AWS CloudWatch Log insights를 통해 보안 그룹의 지정되지 않은 인바운드 규칙이 회수되고 지정된 규칙이 추가되는 로그를 확인할 수 있습니다. 여기에 이미지 설명 입력

단계 4. 보안 그룹 화이트리스트 처리

특정 보안 그룹에 경우 AWS Config 규칙의 트리거 되지 않게 예외 처리를 진행해야 합니다. Lambda 함수 코드 “EXCEPTION_SECURITY_GROUPS”에 보안 그룹 ID를 추가해주면 COMPLIANT로 평가합니다. 여기에 이미지 설명 입력 여기에 이미지 설명 입력

단계 5. Amazon SNS를 활용하여 이메일 및 메신저 서비스 알림 받기

Amazon SNS를 사용하여 이메일로 결과를 전달할 수 있고 AWS Chatbot과 SNS를 연동하여 Amazon Chime, Microsoft Teams, Slack으로 알림을 전달할 수 있습니다. Chatbot에 대한 자세한 내용은 다음 문서를 참고할 수 있습니다. [9]

Amazon SNS로 결과를 전달하기 위해서 단계1. Lambda 함수 코드에서 “sns_topic_arn”을 자신의 SNS 주제 ARN으로 변경하면 아래와 같이 이메일을 받는 것을 확인할 수 있습니다. 여기에 이미지 설명 입력


관련 정보

[1] AWS Config 사용자 정의 Lambda 규칙 생성 - https://docs.aws.amazon.com/config/latest/developerguide/evaluate-config_develop-rules_lambda-functions.html

[2] Amazon S3 버킷에 대한 AWS Config 액세스 권한 부여 - https://docs.aws.amazon.com/ko_kr/config/latest/developerguide/s3-bucket-policy.html#granting-access-in-another-account

[3] Amazon SNS로 자격 증명 기반 정책 사용 - https://docs.aws.amazon.com/ko_kr/sns/latest/dg/sns-using-identity-based-policies.html

[4] Amazon SNS 구성 - https://docs.aws.amazon.com/ko_kr/sns/latest/dg/sns-configuring.html

[5] Amazon SNS 주제에 대한 권한 - https://docs.aws.amazon.com/ko_kr/config/latest/developerguide/sns-topic-policy.html

[6] AWS Lambda리소스 기반 정책에 대한 보안 모범 사례 - https://docs.aws.amazon.com/ko_kr/config/latest/developerguide/evaluate-config_develop-rules_nodejs.html#restricted-lambda-policy

[7] AWS Config Custom Lambda Rules - https://docs.aws.amazon.com/ko_kr/config/latest/developerguide/evaluate-config_develop-rules.html#evaluate-config_develop-rules-lambda

[8] 평가 모드 - https://docs.aws.amazon.com/ko_kr/config/latest/developerguide/evaluate-config-rules.html#aws-config-rules-evaluation-modes

[9] What is AWS Chatbot?- https://docs.aws.amazon.com/chatbot/latest/adminguide/what-is.html

댓글 없음

관련 콘텐츠