4P by GN⁺ | ★ favorite | 댓글 1개
  • 개인 서버의 Gitea와 블로그가 스크래핑 트래픽에 눌리며 디스크·CPU·메모리 경고가 이어졌고, 운영자는 로그 분석·Nginx·Fail2Ban을 조합해 방어에 나섬
  • Zabbix 기준 트래픽은 평소보다 크게 뛰었으며, Nginx 한 달 평균은 초당 8 요청, 최악의 구간은 초당 20+ 요청까지 올라감
  • 원인은 Reddit이나 Hacker News 같은 단일 유입 폭증이 아니라, 여러 IP 블록이 www.lambdacreate.comkrei.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를 잠시 끄고 분석을 시작함
  • 사용한 도구는 lnavgoaccess였음
  • 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 악화를 위한 연료가 되도록 두고 싶지도 않다는 입장임

댓글과 토론

Hacker News 의견들
  • 덜 양심적인 크롤러 상당수는 큰 크롤러들을 흉내 내는 듯함. User-Agent가 진짜일 거라고 가정하는 사람이 많은 느낌임
    가장 좋아하는 방식은 robots.txt에 미끼 정보를 넣어 두고, 그 URL이 gzip 폭탄을 내려주게 한 뒤 이후 요청을 자동 차단하는 것임
    Caddy에서 설정하기 쉬웠고, 최악의 크롤러들을 잘 잡아냄
    봇 행동을 옹호하는 건 아니지만, 몇몇 봇이 눈먼 요청만으로 사이트를 다운시킬 수 있다면 악의적인 공격자는 훨씬 신나게 털 수 있음

    • 마지막 말이 꽤 맞는 듯함
      이런 블로그 글을 쓰는 사람들과 세대가 달라서 그런지 모르겠지만, 이렇게 낮은 자원 사용량에 집착하는 걸 잘 이해 못 하겠음
      LED 전등 안 껐다고 조부모가 난리치거나, 갤런당 5센트 아끼려고 15마일 운전하는 걸 보는 느낌임
      초당 20요청은 정말 아무것도 아님
      전부 동적으로 생성한다고 해도, 애초에 왜 그렇게 하는지 모르겠고 캐싱을 고치는 데 시간을 쓰는 편이 훨씬 나았을 것임
      요즘 분위기상 “봇 꺼져”류 글이 인기 있는 건 알겠지만, 이건 별로 새롭지도 않음
      시간을 훨씬 덜 낭비하면서 처리할 생산적인 방법이 많음
    • robots.txt 방식에 대해 좀 더 설명해 줄 수 있는지 궁금함. 대부분의 AI가 robots.txt 관련 내용을 완전히 무시한다고 알고 있어서, 실제로는 지키려고 시도하는 봇만 맞는 것 아닌가 싶음
      여기 다른 사람들처럼 반대하는 건 아니고, 정상적인 행위자를 해치지 않으면서 구현하는 방법이 궁금함
    • 내 분야에서 가장 큰 위키 중 하나를 운영하는데, 개발팀의 다른 사람들을 설득해 gzip 폭탄을 방어 수단으로 쓰게 하는 게 불가능했음
      그들은 위험한 법적 책임이 될 수 있고, 시도할 가치가 없다고 확신함. 정말 실제 공개 웹사이트에서 이런 걸 쓰는지 궁금함
  • 봇들이 더 이상 robots.txt 파일을 전혀 존중하지 않는 게 짜증남. 이런 것들을 만드는 사람들은 완전한 악당임. 본인이 그런 사람이라면 알아서 꺼지길 바람

    • 그렇게 말하면 챗봇, 검색엔진, 가격 비교 도구를 쓰는 사람들에게도 똑같이 꺼지라고 해야 함. 스크래퍼에 금전적 유인을 주는 쪽이 그 사람들이기 때문임
    • robots.txt를 그냥 무시하지 않고 굳이 뒤져보는 봇들에는 허니팟을 거는 게 유용함
  • 디스크 공간 문제는 Gitea 버그라고 봄. 누군가 zip을 다운로드하면 클라이언트로 스트리밍할 수 있어야 하는데, 실제로는 임시 공간에 zip을 만들고 클라이언트에 제공한 뒤 삭제하지 않음
    해당 디렉터리를 읽기 전용으로 표시해서 해결했음. 당연히 zip 다운로드는 동작하지 않음. 정말 필요하면 저장소를 체크아웃해서 직접 만들면 됨
    정말 신경 썼다면 버그를 고치거나, 기능을 제대로 끄는 방법을 확인하거나, 로그인 사용자에게만 켰을 것임
    또한 특정 User-Agent를 서버 쪽에서 https://git.immibis.com/gptblock.html로 리다이렉트함
    이제는 자원 낭비 때문이 아니라 그냥 마음에 안 들기 때문임. 그들이 하는 일은 어차피 가치 없고, 내가 그렇게 할 수 있으니까 함
    Git 저장소의 데이터가 정말 필요하면 멍청하게 긁지 말고 Git 저장소를 클론하면 됨. 그건 원래부터 허용돼 있었음
    초당 8요청은 각 요청이 무거운 처리를 유발하지 않는 한 그리 많지 않고, 실제로도 디스크 공간 버그 외에는 작은 VPS에 부하가 되지 않았음
    DoS라서 막은 게 아니라, 멍청해서 막았음

  • 글쓴이가 “더는 신경 쓰지 않는다”는 건 이해하지만, 차단된 IP에 Google, ripe.net, semrush가 보였음
    다른 사람들이 보고 이야기하길 원한다면, 이 중 Google은 확실히 차단하지 않을 것이고 나머지도 아마 막지 않을 것임
    콘텐츠가 누군가의 AI 봇에 긁힌다 해도, 공개 사이트를 운영하면서 사이트가 사용되길 기대하지 않는 이유가 뭔지 모르겠음
    사람들이 찾아오길 바라면서 모텔 간판 불을 꺼버리는 건 좋은 초대 방식이 아님

    • Semrush는 오랫동안 여러 수준의 무능함으로 너무 심하게 굴어서, 최소 8년 전부터 robots.txt 파일에 별도 메모를 남겼고 결국 법무 쪽 사람이 주목하게 만들었음
      ‘SEO’ 회사가 사이트를 무식하게 짓밟으며 실제 방문자를 밀어내도록 허용해도 나나 잠재 사용자에게 아무 가치가 없음
      Semrush의 일부 경쟁사도 똑같이 나빴음
      현재의 AI 난장판도 매우 형편없음. 적어도 한 유명 사례에서는 기술적 조치뿐 아니라 투자자 관계와 홍보 부서에 법적 메모까지 보내야 어느 정도 질서가 회복됐음
    • 봇 요청이 대역폭, 메모리, CPU, 디스크 공간을 상당히 소비하기 때문임. 도입부 말처럼 그냥 무례한 트래픽이고, 그런 수집자에게 트래픽을 제공할 이유가 없음
      Google도 AI 스크래퍼를 운영하니, 거기서 본 게 그것일 수도 있음
    • 지난 10년 동안 이제는 Google을 쓰면 안 된다고 사람들에게 가르치기 시작하지 않았단 말인가?
      특히 이런 일이 벌어질 때는 더 그렇음
      Google is using AI to censor independent websites like mine
      https://news.ycombinator.com/item?id=44124820
      이제는 Google이 더 큰 부담이 된 지점에 도달한 것처럼 들림
    • Google 스크래퍼인 척하는 악성 행위자도 있음. Google은 한때 정중하게 긁는다는 평판이 있었지만, Googlebot이 있든 없든 필요한 트래픽을 얻고 있다면 굳이 신경 쓸 이유가 없음
  • 이런 봇들 때문에 로그가 너무 빨리 커져서 서버의 로깅을 꺼버렸음. 정말 끈질기고, 모든 폼을 채우며 사이트에서 클릭으로만 접근할 수 있는 API까지 호출함
    Anthropic, OpenAI, Facebook은 지금도 계속 긁고 있음

    • “사이트에서 클릭으로만 접근할 수 있는 API”라면 달리 어떻게 접근한다는 건지 궁금함
    • 웹사이트에서 클릭으로만 쓸 수 있는 API에 접근하는 크롤러들에 대해 정보를 공유해 줄 수 있는지 궁금함
      확인하자면, UI의 일부라서 사람만 눌러야 하는 것이고, 그 API에 접근할 다른 방법은 없다는 뜻인가?
      이제 AI 에이전트가 존재하므로, 사람 행동 패턴을 흉내 내면 실제 사용자와 봇을 구분할 방법은 사실상 거의 없음
  • AI 크롤러 봇들이 User-Agent 헤더를 정직하게 채운다는 건 좋음. 다만 그렇게 많은 트래픽의 원인이었다는 건 놀라움
    전체 웹사이트의 99%는 이 정도 트래픽이 필요할 만큼 자주 바뀌지 않고, 개발 블로그라면 더더욱 아님

    • 그들은 robots.txt도 존중함
      다만 어떤 방식으로든 차단당한 뒤에는 주거용 IP에서 브라우저 User-Agent로 크롤링하기 시작했다는 보고도 봤음
      하지만 처음부터 누군가가 자기 크롤러를 OpenAI/Amazon/Facebook 등으로 속였을 가능성도 있음
  • tirreno [1] 만든 사람임
    우리 플랫폼은 주로 실시간 로그인 사용자를 대상으로 설계됐지만, 봇 탐지와 차단에도 잘 동작함
    IP 주소의 마지막 옥텟을 별표로 바꿔 익명화하고, 사실상 같은 서브넷을 하나의 계정 아래로 묶음
    이후 내장 규칙 엔진으로 과도한 500/404 오류, 무차별 로그인 시도, 데이터센터 IP 트래픽 같은 조건에 따라 자동으로 차단 목록을 만들 수 있음
    마지막으로 tirreno 차단 목록 API를 애플리케이션 로직에 통합해 원치 않는 트래픽을 오류 페이지로 리다이렉트할 수 있음
    보너스로, 활동을 모니터링하고 정상 사용자를 차단하지 않도록 목록을 미세 조정할 수 있는 대시보드 [2]도 제공됨
    [1] https://github.com/tirrenotechnologies/tirreno
    [2] https://play.tirreno.com/login (admin/tirreno)

    • 사용자 입장에서는 “데이터센터 IP” 기반의 차단벽 오탐을 감수해야 함. 즉 마지막 구간 회선의 정보 누출 주소에서 순진하게 브라우징하지 않는 대부분의 환경이 걸릴 수 있다는 뜻임
      사전에 차단되지 않은 뭔가를 찾거나 신호등 87칸을 클릭해 통과하더라도, 완전히 무관한 주소 이웃과 한데 묶임
      동의 없이 개인 정보를 처리해 사용자 감시 시스템을 만들고 있지 않다는 양심을 달래기 위한 방식처럼 보임
      고객들이 실제 돈을 쓰는 진짜 고객을 잃고 있다는 사실을 볼 수 있을 만큼 충분한 피드백 절차는 꼭 갖추길 바람
    • ISP들이 CGNAT로 이동하는 상황은 어떻게 처리하는지 궁금함. IP 하나가 현실적으로 수백 명의 사용자를 대표할 수 있음
    • 같은 대시보드를 통해 공개된 IP 범위 기반으로 봇을 차단하는 기능도 작업 중임. 제안은 환영함
  • 내 사이트들은 하루 1만~2만 요청을 받음. 대부분 AI 스크래퍼
    눈에 띈 점은 많은 봇이 특정 PHP 페이지를 찾는다는 것임. PHP를 쓰지 않는다면 PHP 페이지를 요청하는 IP를 자동 차단할 수 있음. PHP를 쓴다면 실제로 없는 페이지를 요청하는 IP를 막으면 됨
    우리 중 일부는 AI 학습은 기꺼이 허용하지만 과부하는 막고 싶어 함. 예를 들어 복음과 성서 신학에 관한 페이지가 긁히는 건 반가움
    긁히길 원치 않는 큰 콘텐츠는 특정 디렉터리에 넣어두는 게 도움이 될 수 있음. 그 뒤 봇을 감지하면 해당 IP가 그 디렉터리에 접근하지 못하게 막으면 됨
    내 경우 많은 요청을 처리하기 위한 기본 전략도 있음. 텍스트 전용, HTML/CSS 표현, 기타 요소는 외부 호스팅, 그리고 BunnyCDN의 Perma-Cache($10/월 + GB당 1센트)를 씀
    BunnyCDN 요청은 Digital Ocean의 월 $5 VM으로 감. AI 스크래퍼는 처음엔 알아차리지도 못했는데, 성능에 영향이 없었고 한 달 동안 잔액이 $30에서 $29.99로 바뀐 정도였기 때문임

  • “페이지 노킹” 같은 게 있는지 궁금했음. 특정 순서로 여러 페이지를 열면 접근 권한이 생기는 식임
    예를 들어 특정한 비공개 URL로 시작하지 않으면 모든 게 404가 되게 할 수도 있음

    • 반대 방식은 본 적 있음. 사이트 푸터에 사용자가 클릭하거나 미리 가져오기할 가능성이 낮은 링크를 두고, 로봇이 그걸 GET하는 순간 나머지 사이트를 문법적으로는 유효한 헛소리로 바꿔버리는 방식임
    • 그 방식으로 인가된 사용자가 어떻게 접근할 수 있는지 모르겠음. 일반 사용자가 검색 결과 페이지에서 프로젝트를 발견하고, 계정 생성이나 인증 없이 바로 둘러볼 수 있기를 원한다는 점을 감안해야 함
    • 어떻게 설계하든 사용자에게 불친절할 것임. 내가 북마크를 저장하거나 친구에게 링크를 보내면 그 사람은 404를 보게 됨
  • 작은 서버가 그냥 조용히 돌아가고 있어서 최근 fail2ban 상태를 확인하지 않았음
    sudo fail2ban-client status | sed -n '/Jail list:/{s/^.*://; s/,//g; p}' | xargs -n1 sudo fail2ban-client status | awk '/jail:/{i=$5}; /Total failed:/{jail[i]=$5}; END{for(i in jail) printf("%s: %s\n", i, jail[i])}' | column -t
    sshd-ddos: 0
    postfix: 583
    dovecot: 9690
    postfix-sasl: 4227
    nginx-botsearch: 1421
    nginx-http-auth: 0
    postfix-botnet: 5425
    sshd: 202157
    헉, 차단된 IP가 22만 개라니

    • DotBot/1.2로 식별되는 봇 하나가 있는데, 오늘 기준 지난 2주 동안 22만 건 넘는 요청을 보냈음
      웹 서버에서 파일명과 폴더를 무작위로 요청함
      얼마나 깊이 파고드는지 한계가 있는지 궁금해서 차단하지 않고 지켜보는 중임