IP 범위 차단하는 법? '차단된 IP'가 차단되지 않음

안녕하세요. 악성 행위를 하는 /16 서브넷을 일시적으로 차단하려고 합니다. 이 서브넷이 Discourse 포럼에 대량의 요청을 보내고 있습니다. 다음을 시도해 보았지만…

/var/discourse/shared/standalone/log/var-log/nginx/access.log 파일에서 같은 범위에서 여전히 대량의 새로운 요청이 발생하고 있습니다.

이 설정들이 Discourse에서 이루어지기 때문에, IP가 NGINX가 아닌 Discourse에 의해 차단되는 것이라 생각되며, 따라서 NGINX 로그에 표시될 것입니다. 차단하는 주체는 nginx가 아니라 Discourse입니다. NGINX 로그에 표시되지 않도록 하려면 방화벽을 통해 차단할 수 있습니다.

제이, 감사합니다. 앱 레벨에서 차단하더라도 Nginx 로그에 여전히 기록된다는 점에 대해서는 말씀이 맞습니다. 다만, 앱 레벨에서의 차단도 제가 기대했던 대로 작동하지 않고 있습니다. VPN으로 연결한 후 제 IP 주소를 “Screened IPs” 목록에 추가해 보았지만, 여전히 포럼을 탐색할 수 있었습니다. 아마도 "Blocked IPs"가 아니라 "Screened IPs"라고 불리는 이유는, 해당 IP 주소가 계정 등록만 방지하는 것일 수도 있기 때문이 아닐까 싶습니다.

제가 원하는 것은, 해당 주소에서 오는 요청에 대해 Discourse 앱이 요청된 페이지에 대한 액세스를 거부하고, 특히 해당 페이지를 렌더링하지 않는 것입니다. 모든 이러한 요청이 CPU 사용량을 최대치로 끌어올리고 있기 때문입니다.

관리자 패널의 /admin/users에서 사용자 기록을 확인하면, Discourse가 해당 사용자가 차단된 IP 중 하나로 접속한 것으로 인식합니까? (Cloudflare 같은 프록시 뒤에 있는 경우, Discourse는 사용자의 실제 IP가 아니라 프록시 IP를 확인할 수 있습니다.)

네, VPN에 연결한 후 Discourse에서 내 사용자 계정의 마지막 IP를 확인하면 VPN이 할당해준 IP가 표시됩니다. 하지만 시크릿 모드 브라우저 창을 열어 새 계정을 등록하려고 하면 허용되지 않습니다:

새 등록은 이 IP 주소에서 허용되지 않습니다.

즉, "스크리닝된 IP"는 웹사이트에 대한 완전한 접근을 차단하는 것이 아니라, 등록에만 사용되는 것 같습니다.

그리고 더 복잡하게 만드는 것은, 호스트 서버의 ufw / nftables가 Docker 내부에서 예상대로 차단하지 않는 것 같습니다:

이 내용을 어디에 문서화했는지 확실하지 않지만, 최근 몇 번이나 이런 질문이 올라왔습니다.

"Screened IPs"는 차단된 IP와 일치하는 경우 해당 IP에서의 등록과 로그인을 차단하지만, 기존 세션에는 적용되지 않습니다.

(혹시) 이 내용을 어디에 문서화했는지 확실하지 않습니다.

확인해 주셔서 감사합니다.

그렇다면 특정 IP 또는 IP 범위에서 오는 요청을 거부하는 가장 간단한 방법은 무엇일까요? 아래 내용을 찾았지만, 제가 원하는 것(블랙리스트)과 정반대인 화이트리스트 방식인 것 같고, 컨테이너 내부의 Nginx 설정을 건드리는 것은 완전한 해킹 작업처럼 느껴집니다:

UFW나 IPTABLES를 사용하는 것이 좋다고 생각합니다. 이렇게 하면 Discourse가 개입하기 전에 요청을 차단할 수 있습니다. 저는 항상 방화벽 설정을 만지면 자산을 잠금에 걸릴까 봐 두려워 하지만, 포트 443만 표적으로 삼는다면 아무런 위험이 없습니다.

Digital Ocean에 몇 가지 힌트가 있습니다: UFW Essentials: Common Firewall Rules and Commands for Linux Security | DigitalOcean. 그냥 구글로 예시를 검색해 보시면 됩니다.

저도 정확히 같은 걱정입니다. 호스트에서 활성화했는데도 여전히 문제 있는 IP 범위를 차단하지 못하고 있습니다. 도커 컨테이너에 거부 규칙을 적용하려면 복잡한 규칙 집합이 필요한 것 같습니다.

아, 네. 맞아요. 제 도커 기반 웹 서버에서 도커 기반 postgres 데이터베이스로의 접근을 차단하게 되었습니다(아마도 당신의 문제는 아닐 겁니다).

또 다른 아이디어를 제안합니다: Geo Blocking plugin

감사합니다. 저도 그 플러그인을 보고 있었는데, 숫자 IP 범위를 차단하는 기능이 없는 것 같고, 국가 단위로만 차단할 수 있는 것 같습니다?

최근에 설치해 보지는 않았지만, 그렇게 할 수 있다고 확신합니다.

(강조 추가)

하지만 다음을 발견했습니다:

따라서 원하는 네트워크를 입력할 수 있다고 생각합니다.

/var/discourse/launcher enter app
apt install nano
nano /etc/nginx/conf.d/discourse.conf

그리고 server { 블록에 다음을 추가하세요:

## 2025-10-27
deny 12.34.0.0/16;

그 후 저장하고

nginx -t
service nginx reload

Nginx의 access.log에서 여전히 12.34.x.x에서 온 요청을 확인하는 이유를 정확히 이해하지는 못했지만, CPU 사용량이 다시 정상 수준으로 돌아왔기 때문에 작동하는 것으로 보입니다. 개인적으로 이 과정은 믿을 수 없을 만큼 복잡하다고 생각하지만, 당장 사이트를 다시 정상적으로 구동시키기에는 충분한 해결책입니다.

네, 하지만 현재는 CIDR 표기법(예: 1.2.3.0/24) 대신 AS 번호만 입력할 수 있습니다.

그렇게 해서 잘 되셨나요? 아마 다음을 실행해야 할 것 같습니다.

sv restart nginx

하지만 오류 메시지가 없었다면 제가 잘못 알고 있는 것입니다.

Nginx에서 여전히 그 요청들을 확인하고 있나요? 실제로 그 요청들을 처리하고 있는 것으로 표시되나요? Rails의 production.log에서도 해당 요청들을 볼 수 있나요?

아. 아마도 AS 번호에 대한 그 위키백과 페이지에 더 주의를 기울였어야 했나 봅니다.

둘 다 지금은 systemd 명령어로 매핑되는 오래된 별칭인 것 같습니다. 따라서 systemctl restart nginx가 가장 적절할 것입니다.
수정: systemctl은 Docker 내부에서 작동하지 않는 것 같습니다. 차이점에 대한 설명은 다음과 같습니다:

네, access.log에는 여전히 다음과 같이 나타납니다:

[28/Oct/2025:00:29:27 +0000] "myforum.com" 12.34.56.78 "GET /permalink/12345 HTTP/1.1" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36" "-" 403 691 "-" - 0.000 "-" "-" "-" "-" "-" "-" "-"

주로 이제 영구 링크(permalinks)를 타격하고 있는 것 같습니다(오래된 포럼 플랫폼에서 이관된 것). deny 규칙을 우회하는 영구 링크 관련 취약점이 있는 걸까요? 아직 production.log를 확인하지 않았습니다.

전체적으로 말씀드리자면, 액세스 로그를 검사하기 위한 GUI가 없고 앱 수준의 IP 차단 목록이 없다는 것은 Discourse의 상당히 큰 한계라고 생각합니다. 드문 일이긴 하지만, 이런 봇/스크레이퍼/크롤러 공격을 받을 때 원인을 즉시 파악하고 대응하고 싶어지는데, 특히 Docker 컨테이너 깊숙한 곳에서 일어나는 이상한 추상화 문제를 고려할 때, 수많은 설정 파일과 난해한 명령어에 얽매이는 것은 피하고 싶습니다. 제가 이전에서 이관해 온 아주 오래된 포럼 플랫폼에는 조정 가능한 시간 창 동안 가장 많은 요청을 보낸 사용자 또는 IP의 간단한 목록을 보여주었고, 심지어 CPU 시간을 가장 많이 차지한 사용자 또는 IP로 필터링할 수도 있었습니다. 이렇게 하면 문제의 주소나 범위를 빠르게 식별할 수 있었고, 차단 목록에 추가하기 위한 클릭형 인터페이스도 있어 해당 IP에서 온 요청에는 404를 반환하도록 할 수 있었습니다.

문제는 없습니다.

deny 규칙이 있는 경우에도 nginx에는 여전히 기록이 남습니다. 그리고 이는 정상적으로 작동하고 있는 것입니다. HTTP 응답 코드에 403이 포함되어 있기 때문에 클라이언트의 접근이 거부되었음을 의미합니다.

이러한 요청은 Discourse로 전달되지 않습니다. 차단이 올바르게 작동하는지 확인하려면 다음 중 하나를 수행해야 합니다.

  • 대신 Discourse의 production.log를 확인하세요
  • nginx 로그를 확인하되, HTTP 응답 코드 필드에 403이 포함된 항목은 무시하세요.

이 주제는 이제 해결된 건가요? @rgj 또는 @pfaffman의 게시글 중 하나를 해결책으로 선택할 수 있을까요? 이 주제에 대해 협력하신 것 같네요!

:handshake:

안녕하세요. 현재 이 문제는 “일반적인” 방법으로 해결하기가 쉽지 않다고 생각합니다. “차단된 IP” 관리 인터페이스는 대부분의 사용자가 기대하는 동작을 하지 않으며, 실제로 작동하는 방법은 Discourse의 Docker 컨테이너 내부의 설정 파일을 직접 수정해야 하는 방식이라, 적어도 제 생각에는 해결책이라기보다 더티 해킹에 가깝습니다. 이 문제가 좀 더 관심을 받았으면 좋겠습니다: