[AWS] Resource 생성/변경/삭제 에 대한 Slack Notification 구축

Nari.·2025년 6월 25일
0

AWS

목록 보기
14/14
post-thumbnail

지속되는 IAM AccessKey 탈취 사건으로 보안 관련 IAM 서비스 변경 사항 또는 EC2 Security Group 변경에 대해서 알람을 받고 있었다. 그런데 최근 최소 권한이었던 IAM User 키가 또 어디선가 유출되면서 여러가지 시도가 있었어서 모든 리소스에 대한 생성/변경/삭제 에 대한 Slack Notification을 구축하게 되었다.

AWS Architecture Description

Service Flow

  • 사용자가 AWS Cloud에서 작업하는 모든 이벤트에 대해서 EventBridge가 이벤트를 캐치하고
  • SNS(Simple Notification Services)로 이벤트를 전달
  • SNS를 트리거로 삼는 Lambda 함수가 Slack으로 메시지를 보냄
  • Lambda 함수의 로그들을 CloudWatch Log Group에 StreamLogs로 저장

1. Set EventBridge Rules

사용자가 AWS Cloud에서 작업하는 모든 이벤트에 대해서 이벤트를 감지하는데 '조회'하는 활동만 빼고 모든 것을 감지하는 패턴을 작성했다. 아래 패턴은 ELB가 자동으로 NetworkInterface를 추가 및 삭제해도 알람이 오게된다!

  • Event pattern
{
  "source": [{
    "anything-but": ["aws.logs", "aws.config"]
  }],
  "detail-type": [{
    "anything-but": ""
  }],
  "detail": {
    "readOnly": [false],
    "eventName": [{
      "anything-but": {
        "prefix": ["Head"]
      }
    }]
  }
}

위의 내용은 정말 많은 시행착오 끝에 도출해낸 패턴이지만 모든 곳에서 다 잘될거라는 확신은 없다. 하지만 현재 내 상황에서 사용하는 서비스에 대해서는 테스트가 되었다.

[ 위의 Event pattern 이 감자하는 항목 ]

  • CloudWatch, Config 활동에 대해서는 알람이 발생하지 않음 ❌
    • 로그를 저장하거나 생성하는 활동에 대해서는 알람을 껐다. 어떤 활동이 있었기에 로그가 저장될거라고 생각해서 무의미하다고 판단.
  • detail-type 에 대해서는 모든 것을 다 이벤트로 감지하도록 설정
  • AWS SSO Login - ConsoleLogin 도 알람 발생
  • "readOnly": [false] 의 의미는 해당 API 호출이 AWS 리소스를 변경하지 않는 "읽기 전용" 작업이라는 것을 의미함
  • eventName 중에서 S3를 조회하면 HeadBucket이라는 API를 호출하게 되는데 해당 API는 조회의 느낌이 강해서 예외처리로 뺌

2. Set SNS Topic

  • SNS Topic을 생성해야하는데, Lambda에서 사용할 것이라서 Type: Standard 로 했다.
  • 대량의 트래픽을 사용할게 아니라서 SQS까지는 연결하지 않았지만 필요하다면 SQS를 껴서 하면 누락되거나 지연없이 알람을 받을 수 있을 것같다.

  • 별다른 설정은 하지 않고 Name 만 설정하고 생성했다.

이렇게 SNS Topic을 생성하면 SNS 부분은 준비 완료!

3. Set Lambda Function

람다 함수에서 사용하는 코드는 조금 길어서 GitHub에 올리고 공유할 예정입니다!

Python으로 Lambda 함수를 작성할 예정이라 Python 3.13 버전으로 새로 생성했다.
람다에서 사용할 Role이 하나 필요한데,

위의 설정에서 Change default execution role > Create a new role from AWS policy template 를 선택하면 role name 만 정의하면 AWS에서 Lambda Loggging을 위해 필요한 그나마 최소권한을 가진 Role을 생성해준다.

아래와 같이 권한이 설정되어 생성되는데

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:<lambda_region>:<AccountID>:*"
        },
        {
            "Sid": "AllowCreateLogs",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:<lambda_region>:<AccountID>:log-group:/aws/lambda/<lambda_name>:*"
            ]
        }
    ]
}
  • Lambda Logs를 저장할 CloudWatch Log Group을 생성하는 권한
  • Lambda 이름으로 생성된 Log Group에 새로운 Log Stream을 생성하고 Log들을 저장하는 권한

이 들어가게 되는데, 내가 생각할 때 정말 최소 권한은
Lambda 로그를 저장할 CloudWatch Log Group을 직접 만들어 놓고 logs:CreateLogStream, logs:PutLogEvents 권한만 주는 것이라고 생각된다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowCreateLogs",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:<lambda_region>:<AccountID>:log-group:/aws/lambda/<lambda_name>:*"
            ]
        }
    ]
}

이렇게 하는게 가장 최소권한이지 않을까... 하는 생각!

하드코딩하기엔 조금 부담스러운 값들은 Lambda > Configuration > Environment variables로 빼서 호출하도록 했다.

4. Check CloudWatch Logs

실제로 알람이 울리면 아래와 같이 CloudWatch Log Groups에 저장이 된다.

  • Slack으로 메시지가 정상적으로 발송되었을 때 로그
  • Slack으로 메시지가 정상적으로 발송되지 않았을 때 로그

Slack으로 메시지가 정상적으로 발송되지 않는 경우는 한가지다. userName이 없는 경우. 이 때는 Lambda 함수에서 일지중지하고 끝내는것으로 로직을 만들었다. 수많은 테스트 중에서 userName이 없는 경우는 AssumeRole을 이용했을 때인데, 우리가 흔히 알고있는 역할을 맡기는 Assume Role의 경우 userType = AssumedRole 이라고 나와서 이거는 다른 경우여서 예외처리를 했다.

보통 userType = AssumeRole 이라고 나올 때를 예를 들어보자면, Lambda를 실행할 때 Lambda가 알아서 연결해둔 Assume Role을 호출해서 CloudWatch에 로그를 쌓고 하는데 이 경우가 예외처리로 제외시킨 경우이다. 사람이 어떤 작업을 했을 때, AWS서비스들이 서로 역할을 전환하여 처리하는 경우.
(혹시.. 혹시나 .. 제가 틀렸다면 댓글로 알려주세요!! : )

5. Check Slack Notification

정상적으로 작동하면 이렇게 알람이 오게 된다.

  • 맨 첫 줄에 대괄호[]안에는 ACCUNT_NAME이 들어가게 되고
  • Request Parameters 부분에는 람다 함수의 ARN이 들어가고
  • 아래는 작업을 한 사람의 이름, 위치, 작업한 이벤트 이름, 시간 등이 들어간다.
  • '상세 내용 확인' 버튼에서는 해당 이벤트에 대한 CloudTrail 페이지로 갈 수 있도록 링크를 걸어뒀다.

0개의 댓글