- 개인 서버의 Gitea와 블로그가 스크래핑 트래픽에 눌리며 디스크·CPU·메모리 경고가 이어졌고, 운영자는 로그 분석·Nginx·Fail2Ban을 조합해 방어에 나섬
- Zabbix 기준 트래픽은 평소보다 크게 뛰었으며, Nginx 한 달 평균은 초당 8 요청, 최악의 구간은 초당 20+ 요청까지 올라감
- 원인은 Reddit이나 Hacker News 같은 단일 유입 폭증이 아니라, 여러 IP 블록이
www.lambdacreate.com과krei.lambdacreate.com의 URL을 긁는 분산 스크래핑에 가까웠음 - Nginx는 알려진 악성 user agent에 403을 반환하고 IP별 요청 제한을 적용했으며, 403을 과도하게 발생시키는 클라이언트는 Fail2Ban으로 24시간 차단함
- 작성 시점 차단 목록은 총 735 bans까지 늘었고, 특히 Gitea 공개 저장소의 모든 커밋에 대해 tarball을 생성하려는 요청이 서버 부담을 키움
개인 서버를 압박한 스크래핑 트래픽
- 개인 인터넷 공간에 원치 않는 봇 트래픽이 갑자기 몰리며 실제 독자가 접근해야 할 서비스까지 영향을 받음
- Archive.org처럼 사이트 보존을 목적으로 색인하는 서비스는 허용 대상이지만, Amazon, Facebook, OpenAI와 여러 무작위 봇은 콘텐츠를 자기 목적에 소비하는 존재로 구분됨
- 대형 기업의 광범위한 인터넷 데이터 수집과 AI 모델 학습용 데이터 수요가 스크래핑 압박을 더 키운 것으로 봄
- 이 트래픽은 Lambdacreate의 실제 소비자가 아니라, 사람 독자의 접근성을 떨어뜨리는 방해 요소로 취급됨
Zabbix가 먼저 잡아낸 이상 징후
- 처음 문제를 알린 것은 Zabbix였고, 컨테이너용 디스크가 가득 찼다는 경고가 발생함
- LXD 기반 환경에서 ZFS sparse file을 확장하고 사이트를 잠시 내렸다가 다시 올렸지만, 근본 원인은 남아 있었음
- 이후 Gitea 인스턴스가 매일 디스크를 모두 소비하며 하루 20~30GB의 데이터를 생성함
- 초기에는 Gitea가 저장소 아카이브 정리를 기본 활성화하지 않는 문제로 보고, 공격적인 정리 작업을 설정함
- 곧 CPU와 메모리 경고까지 이어졌고, Gitea에서
git pull·git push가 어려워졌으며 weechat 클라이언트도 연결을 유지하지 못함
평소 대비 10배 수준으로 뛴 요청량
- 과거 지표와 현재 상태를 비교하기 위해 out-of-band 모니터링을 둔 덕분에 비정상 패턴을 확인할 수 있었음
- Zabbix 대시보드에서 핵심 지표는 Nginx 요청 수와 네트워크 처리량 그래프였음
- 한 달 기준 평균 Nginx 요청량은 초당 8 요청 수준임
- 최악의 시기에는 초당 20+ 요청이 들어왔고, 대형 서비스 기준으로는 작아도 개인 서버에는 평소의 10배라 영향이 컸음
- 단순 요청량보다 Gitea 관련 디스크·CPU 사용 증가가 함께 나타나 서버 운영 부담이 커짐
lnav와 goaccess로 로그 추적
- 서버를 충분히 오래 살려 로그를 확인하기 위해 컨테이너와 Nginx를 잠시 끄고 분석을 시작함
- 사용한 도구는 lnav와 goaccess였음
lnav는 로그 파일을 색상화된 TUI로 보여주고, 일반 로그 포맷 위에 추상화 계층을 제공해 SQL 쿼리로 로그를 질의할 수 있음access.log에서는 다음 질문을 중심으로 확인함- 전체 방문자 IP가 몇 개인지
- IP 주소 패턴이 있는지
- 특정 referrer에서 오는 트래픽인지
- 어떤 user agent가 쓰이는지
- 어떤 IP가 어떤 user agent와 연결되는지
- 분석 결과 단일 referrer에서 몰려온 “hug of death”가 아니라, 여러 IP 블록이 사이트 전체 URL을 긁고 있었음
user agent 기반 403과 요청 제한
- 첫 번째 대응은 Nginx에서 문제를 일으키는 user agent를 목록화해 403을 반환하는 방식이었음
- 예시 설정은
map $http_user_agent $badagent를 사용해AdsBot-Google,Amazonbot,Amazonbot/0.1같은 agent를 표시함 - 기본 Nginx 설정에는 user agent 규칙을 include하고, IP별 rate limit도 함께 설정함
- 가상 호스트 설정에는 다음 동작이 들어감
limit_req zone=krei burst=20 nodelay;로 요청 제한 적용$badagent가 참이면return 403;으로 콘텐츠 접근 차단
- 이 방식은 백엔드 처리를 줄이는 데 도움이 되지만, 서버가 HTTP 요청을 받아 403을 처리해야 하므로 대량 동시 요청에서는 여전히 부담이 남음
Fail2Ban으로 방화벽 차단 자동화
- 403 로그가 쌓인 뒤에는
lnav로 403을 받은 고유 IP를 확인할 수 있음 goaccess는 과거·현재 로그를 함께 분석해 서버에 들어온 요청 수와 가장 많이 겨냥된 엔드포인트를 확인하는 데 쓰임- 실제 서버 보호를 위해 Fail2Ban을 추가함
- Fail2Ban 규칙은 Nginx access log에서 403 응답을 과도하게 발생시키는 IP를 잡는 단순한 형태임
- 차단 시간은 86400초, 즉 24시간으로 설정됨
- 작성 시점의
fail2ban-client status nginx-forbidden결과는 다음과 같음- 현재 실패: 13
- 총 실패: 57135
- 현재 차단: 38
- 총 차단: 735
실제 표적은 블로그가 아니라 Gitea tarball 생성
- 최종적으로 독자는 블로그와 Lambdacreate의 다른 서비스에 다시 접근할 수 있게 됨
- 로봇 트래픽을 막아야 하는 상황에서는 블로그 글을 쓰기조차 어려움
- 문제 트래픽은 블로그 스크래핑이 아니라, Gitea 인스턴스의 공개 저장소마다 모든 커밋의 tarball 생성을 겨냥하고 있었음
- 장기적으로는 차단 목록을 확장하거나 Archive.org 같은 합법적 서비스에 예외를 두는 방법을 고민할 예정임
- 검색 엔진에서 콘텐츠가 사라지길 원하지는 않지만, 인터넷의 AI 악화를 위한 연료가 되도록 두고 싶지도 않다는 입장임