| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- EC2
- 마이그레이션
- REST
- SSL
- restful
- RDS
- HTTP 상태 코드
- DDos
- AWS
- nginx
- spring
- aop
- Grafana
- 프리티어
- EC2 인스턴스 생성
- CRUD
- postgresql
- 백업
- 복구
- RDS생성
- log
- 자동 배포
- API
- CD
- REST API 설계
- ec2 rds 연결
- springboot
- 인증
- Prometheus
- 서버
- Today
- Total
SU_DING_GI
🤯 뭐? 우리 서버가 공격 받고 있다고? 본문
EC2 서버 장애 분석 및 대응 기록
최근, 알록 서버에 Grafana와 Prometheus를 활용한 모니터링 시스템을 적용해보고 있었다. 그리고 서버의 CPU 사용량, 메모리 사용량이 많아지거나 Spring 서버에서 500 에러가 발생하면, 디스코드 웹훅으로 알람을 받도록 설정했다.
그런데 어느 순간부터...

이 알림이 계속 뜨더니 알람이 resolve되지 않고 서버 도커 컨테이너(Spring 애플리케이션)가 exited 상태로 떨어지는 사건이 발생했다.
처음 생각한 원인
처음에는 그냥 "EC2 프리티어 스펙이 낮아서 그런가?" 하고 단순하게 넘겼다. 그래서 컨테이너를 재시작했는데, 두 번째 반복될 때 느낌이 이상했다.
"이거 진짜 문제가 있는 거 같은데? 제대로 원인 파악해보자."
바로 원인 분석을 시작했다.
1. Spring 서버 로그 분석
먼저 컨테이너 안에서 Spring 로그를 확인했다.

발견된 에러: HikariCP - housekeeper - Thread starvation or clock leap detected
이 에러는 다음을 의미한다.
- Thread starvation: 스레드가 부족하거나 CPU가 바빠서 housekeeper 스케줄링이 밀렸다.
- Clock leap: 시스템 시간이 비정상적으로 튀었다.
특히, 로그에서는 "50초", "7분 24초" 정도 housekeeper가 밀렸다고 나온다. 30초 주기 작업이 이 정도 밀렸다면 서버에 무슨 문제가 있었다는 뜻이다.
의심했던 원인들
- 서버 CPU, 메모리 과부하
- GC Stop-the-World 발생
- 서버 시간 변경 (NTP 동기화)
- 디스크 I/O 과부하
NTP 시간 동기화로 7분이 튈 리 없다고 생각했다. 그래서 1번, CPU/메모리 문제에 집중해서 보게 됐다.
2. 서버 상태 모니터링
2-1. Grafana 대시보드 확인


- CPU 사용량: 0%~5% (거의 사용 안 함)
- JVM Heap/Non-Heap 메모리: 충분히 여유 있음
- JVM Thread 수: 약 25개 (정상)
- GC 상태:
- Minor GC만 간간히 발생 (정상 범위)
- Full GC는 거의 없음
- GC Pause 시간도 5~10ms 수준
"서버 애플리케이션(Spring)은 문제 없어 보인다."
2-2. EC2 시스템 로그 확인
dmesg 명령어로 시스템 로그를 확인했더니...
Out of memory: Killed process 259723 (java)
JVM 프로세스가 메모리 부족(OOM) 으로 강제 종료됐다.
추가로 journalctl -xe 로그를 보니
systemd-journald: Under memory pressure, flushing caches
서버 전체가 메모리 압박을 받고 있었다는 사실도 확인했다.
2-3. CloudWatch 확인
AWS EC2 모니터링 지표를 확인했다.
- CPU 사용률: 99% 이상 급등
- 네트워크 트래픽: 입출력 급증

"외부에서 트래픽이 엄청나게 몰려왔다."
3. 비정상 트래픽 분석 (Nginx Access Log)
3-1. 최근 HTTP 요청 확인
sudo cat /var/log/nginx/access.log | tail -n 100
로그를 보니 이상한 요청들이 엄청 많이 들어왔다.
3-2. 이상 요청 패턴 분석
요청 경로 설명 의심 행위
| /.git/objects/info/ | Git 리포지토리 정보 탐색 | 소스코드 유출 시도 |
| /.env | 환경변수 파일 접근 시도 | DB 비번 탈취 시도 |
| /cgi-bin/../bin/sh | 시스템 명령어 실행 경로 접근 | 리모트 코드 실행(RCE) 시도 |
| /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php | PHP 취약점 공격 | 원격 명령 실행 시도 |
특정 IP에서 지속적으로 공격 시도가 있었다.
3-3. 공격 IP 목록
IP 주소 주요 행위
| 195.211.191.76 | .git 접근 |
| 78.153.140.158 | .env 접근 |
| 223.130.11.165 | RCE 시도 |
4. 보안 강화 조치 (1차)
4-1. 악성 IP 차단
# 악성 IP 차단
deny 195.211.191.76;
deny 78.153.140.158;
deny 223.130.11.165;
allow all;
✅ 악성 IP 차단 완료
4-2. 민감 경로 차단
location ~* /\\.(git|env|htaccess|htpasswd) {
deny all;
}
location ~* (eval-stdin|phpinfo|vendor) {
deny all;
}
✅ .git, .env, vendor/ 등 접근 차단
4-3. 요청 속도 제한
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
location / {
limit_req zone=one burst=20 nodelay;
proxy_pass <http://127.0.0.1:8080>;
}
}
✅ 1초에 10회 요청 제한 (봇 공격 방어)
4-4. 타임아웃 설정
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
✅ 요청 지연 공격(슬로우로리스) 방어
그 결과,


5. 아직 부족한 점
설정 적용 후에도
- 다른 IP로 계속 스캐닝 공격이 발생하고 있음
- nginx 차단만으로는 완벽 방어 어려움
그래서 다음 단계로 fail2ban 적용을 준비 중이다.
fail2ban은 nginx 로그를 분석해서 비정상 요청을 자동으로 감지하고, 방화벽(IP 차단)을 걸어주는 도구다.
다음 목표: fail2ban으로 자동 차단 시스템 구축하기! 🚀
마무리
이번 경험을 통해
- 단순한 모니터링만으로는 서버를 지킬 수 없다.
- 자동 방어 시스템이 반드시 필요하다.
는 걸 제대로 깨달았다.
앞으로는 nginx + fail2ban 조합으로 더 튼튼한 서버를 만들 예정이다.
- 추가로 디스코드로 오는 웹훅으로 바로 어떤 경고인지 알면 좋을 것 같아서 각 경고 규칙마다 커스텀해서 디스코드로 오도록 수정했다!
'INFRA' 카테고리의 다른 글
| [AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 3. CI/CD 코드 및 여러 설정 파일 수정하기 (0) | 2025.02.06 |
|---|---|
| [AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 2. EC2 인스턴스 생성하기 (0) | 2025.02.06 |
| [AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 1. RDS 생성 및 백업 (0) | 2025.02.04 |