EC2 SSH 로그인 이벤트 Slack 알림 설정하기 (CloudWatch Logs + Lambda)

운영 중인 EC2 서버에서 누가 SSH로 접속하는지는 중요한 보안 이벤트다. 단순 모니터링을 넘어서, 로그인 성공/실패 내역을 Slack으로 바로 받아보면 이상 징후에 빠르게 대응할 수 있다. 이번 글에서는 CloudWatch Logs → Subscription Filter → Lambda → Slack Webhook 흐름으로 알림을 구성하는 방법을 정리한다.

1. CloudWatch Agent 설치 및 로그 수집

우선 EC2의 SSH 로그를 CloudWatch Logs로 수집해야 한다.

  • Amazon Linux 계열: /var/log/secure
  • Ubuntu/Debian 계열: /var/log/auth.log

운영체제에 따라 파일 위치와 로그 포맷이 다르기 때문에, 반드시 OS별 로그 경로를 확인해야 한다. CloudWatch Agent를 설치 후 cloudwatch-agent.json 설정에서 위 로그 파일을 Log Group/Log Stream으로 전송하도록 지정한다.

# Amazon Linux 예시
sudo yum install amazon-cloudwatch-agent -y
sudo vi /opt/aws/amazon-cloudwatch-agent/bin/config.json

여기서 Log Group 이름은 예를 들어 /ec2/ssh-login으로 통일해 두면 관리하기 좋다.

2. CloudWatch Log Group 및 Subscription Filter 작성

로그가 수집되면, 특정 문자열 패턴을 기반으로 이벤트를 Lambda로 전달해야 한다.

SSH 로그는 OS마다 표현이 달라질 수 있다. 예를 들어:

  • Ubuntu: Accepted password for user ...
  • Amazon Linux: authentication success

따라서 정확히 이 패턴만 써야 한다고 생각하면 안 된다. 운영 환경 로그를 실제로 확인한 뒤, 알맞게 Subscription Filter를 작성해야 한다.

예시로 두 가지 패턴을 필터링한다고 가정하자:

  • "authentication success" (로그인 성공)
  • "authentication failure" (로그인 실패)

이렇게 Subscription Filter를 두 개 만들어, 각각 Lambda로 전달되도록 한다.

aws logs put-subscription-filter \
  --log-group-name "/ec2/ssh-login" \
  --filter-name "ssh-auth-success" \
  --filter-pattern "authentication success" \
  --destination-arn arn:aws:lambda:ap-northeast-2:111111111111:function:sshAlertLambda

aws logs put-subscription-filter \
  --log-group-name "/ec2/ssh-login" \
  --filter-name "ssh-auth-failure" \
  --filter-pattern "authentication failure" \
  --destination-arn arn:aws:lambda:ap-northeast-2:111111111111:function:sshAlertLambda

여기서 중요한 점은 필터 패턴이 실제 OS 로그 포맷과 맞지 않으면 이벤트가 Lambda로 전달되지 않는다는 것. 환경에 따라 "Accepted", "Failed password" 등을 써야 할 수도 있다.

3. Lambda 실행 Role 생성

Lambda가 CloudWatch 로그를 읽고 Slack에 전송하려면 적절한 IAM Role이 필요하다. 아래는 최소 권한 예시 정책이다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "logs:FilterLogEvents",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "ec2:DescribeInstances",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "lambda:InvokeFunction",
      "Resource": "*"
    }
  ]
}

4. Lambda 함수 작성 (Slack Webhook 연동)

이제 Lambda 함수에서 Slack Webhook으로 메시지를 전송한다.

주의: 코드에 Webhook URL을 직접 넣는 것은 보안상 좋지 않다. 반드시 SSM Parameter StoreSecrets Manager를 이용하는 게 권장된다. 여기서는 이해를 돕기 위해 하드코딩 예시를 쓴다.

import json, urllib.request, base64, gzip, re

def lambda_handler(event, context):
    cw_data = event['awslogs']['data']
    compressed_payload = base64.b64decode(cw_data)
    uncompressed_payload = gzip.decompress(compressed_payload)
    log_data = json.loads(uncompressed_payload.decode('utf-8'))

    for log_event in log_data['logEvents']:
        message = log_event['message']

        if "authentication success" in message:
            slack_message = f"SSH Login 성공: {message}"
            post_message_to_slack(slack_message)
        elif "authentication failure" in message:
            slack_message = f"SSH Login 실패: {message}"
            post_message_to_slack(slack_message)

    return {"status": "done"}

def post_message_to_slack(text):
    webhook_url = "https://hooks.slack.com/services/xxxx/yyyy/zzzz"  # 변경 필요
    post_data = json.dumps({"text": text, "username": "SSH Alert", "icon_emoji": ":key:"}).encode('utf-8')
    request = urllib.request.Request(webhook_url, data=post_data, headers={'Content-Type': 'application/json'})
    urllib.request.urlopen(request)

이 코드는 단순히 메시지를 Slack으로 전달한다. 더 나아가면 user, rhost, hostname 같은 필드를 regex로 추출해 깔끔하게 보여줄 수도 있다.

5. 동작 테스트

  1. EC2에 SSH 로그인 시도 (성공/실패 모두)
  2. CloudWatch Logs에서 해당 이벤트가 수집되는지 확인
  3. Subscription Filter가 Lambda를 호출하는지 확인
  4. Slack 채널에서 메시지가 수신되는지 확인

정리

  • EC2 SSH 로그인 이벤트는 /var/log/secure 또는 /var/log/auth.log에서 확인 가능하다.
  • Subscription Filter의 패턴은 OS마다 다르므로 반드시 실 로그를 보고 작성해야 한다.
  • Slack Webhook URL은 하드코딩하지 말고 보안 저장소를 이용하는 게 좋다.

이렇게 구성해 두면 SSH 로그인 시도가 있을 때마다 Slack 채널에 바로 알림이 도착한다.
그렇게 어려운 구성은 아니라고 생각한다.

zabbix와의 통합도 가능한 부분이다. 언제 시간이 있다면 여기에 대한 글도 따로
쓰도록 하겠다.

ⓒ 2025 엉뚱한 녀석의 블로그 [quirky guy's Blog]. 본문 및 이미지를 무단 복제·배포할 수 없습니다. 공유 시 반드시 원문 링크를 명시해 주세요.
ⓒ 2025 엉뚱한 녀석의 블로그 [quirky guy's Blog]. All rights reserved. Unauthorized copying or redistribution of the text and images is prohibited. When sharing, please include the original source link.

🛠 마지막 수정일: 2025.09.26