클라우드플레어 n8n 웹훅 트래픽 화이트리스트 보안, 방치했다가 서버 폭파당한 썰 😱
혹시 자가 호스팅 서버에 올린 클라우드플레어 n8n 웹훅 트래픽 화이트리스트 설정을 깜빡하고 그냥 방치하고 계시진 않나요?
"설마 내 듣보잡 개인 도메인 웹훅 URL을 누가 알고 털겠어?"
저도 딱 그렇게 생각하고 평화롭게 잠들었다가, 새벽 4시에 울리는 슬랙 서버 다운 알람에 비명을 질렀습니다.
중국발 무차별 스캔 봇이 /webhook/ 경로를 기가 막히게 찾아내 초당 수백 건의 더미 요청을 쏟아부었고, n8n 인스턴스는 메모리 누수로 장렬하게 산화해 있었습니다.
클라우드플레어 주황색 구름 켜뒀다고 안심하면 절대 안 됩니다. 내 n8n 서버를 요새로 탈바꿈시키는 핵심 보안 세팅을 실전 경험담과 함께 풀어보겠습니다.
클라우드플레어 WAF 웹훅 화이트리스트 세팅, 무차별 호출 봇 박멸하는 법
클라우드플레어 WAF 커스텀 규칙에서 웹훅 경로에 허용된 IP 목록이나 전용 인증 헤더 조건을 걸어두면 인가되지 않은 트래픽을 원천 차단할 수 있습니다.
n8n의 웹훅 노드는 기본적으로 URL만 알면 누구나 트리거를 당길 수 있는 구조입니다. 이를 보호하려면 클라우드플레어 대시보드의 Security > WAF > Custom rules 메뉴로 이동해서 강력한 문지기 규칙을 세워야 합니다. 예를 들어 슬랙이나 토스페이먼츠처럼 송신 IP 대역이 공개된 서비스라면 IP List를 등록하고, 그렇지 않다면 사전에 정의한 시크릿 헤더를 검사하도록 구성합니다.
(http.request.uri.path starts_with "/webhook/" and not ip.src in $trusted_webhook_ips)
-> Action: Block
또는 외부 발송처에 커스텀 헤더를 실어 보낼 수 있는 환경이라면 http.request.headers["x-webhook-secret"] ne "내_비밀_키" 조건을 활용해 1초 만에 튕겨낼 수도 있습니다.
도메인 연결 자체가 처음이라 헷갈리시는 분들은 개인 도메인 클라우드플레어 연동 가이드 글을 먼저 읽어보시면 기본 프록시 구조를 이해하는 데 큰 도움이 됩니다.
n8n N8N_PROXY_HOPS 환경변수 설정, 클라우드플레어 뒤에서 원본 IP 살려내는 팁
n8n 컨테이너 환경변수에 N8N_PROXY_HOPS를 1로 지정해야 클라우드플레어가 넘겨주는 헤더를 통해 접속자의 진짜 원본 IP를 정확하게 식별할 수 있습니다.
클라우드플레어 프록시를 켜는 순간, n8n 입장에서는 모든 요청이 클라우드플레어 에지 서버(172.68.x.x 등)에서 들어오는 것처럼 보입니다.
이 상태에서 n8n 웹훅 노드의 자체 IP 화이트리스트 기능을 켜버리면 정상적인 결제사 웹훅까지 몽땅 차단되어 비즈니스가 마비되는 참사가 일어납니다.
# docker-compose.yml 예시
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest
environment:
- N8N_PROXY_HOPS=1
- N8N_TRUST_PROXY=true
이 환경변수를 넣고 재시작해야 비로소 CF-Connecting-IP와 X-Forwarded-For 헤더를 신뢰하여 접속자의 원래 IP를 제대로 로그에 남기고 내부 규칙에 활용할 수 있습니다.
초기 컨테이너 배포 구조가 궁금하다면 n8n 업무 자동화 초보 가이드: 설치부터 첫 워크플로우까지 글에 상세한 도커 세팅법이 정리되어 있으니 참고해 보시기 바랍니다.
클라우드플레어 봇 파이트 모드 403 오류, 정상 웹훅만 쏙 골라 통과시키는 우회책
클라우드플레어의 봇 파이트 모드는 브라우저가 아닌 기계적 요청을 차단하므로 웹훅 경로에 대해서는 WAF 스킵 규칙이나 제로 트러스트 바이패스를 반드시 적용해야 합니다.
많은 분들이 보안 올린답시고 'Bot Fight Mode'나 'Super Bot Fight Mode'를 무지성으로 켰다가, 정작 결제 완료 웹훅이 모조리 403 Forbidden에 걸려 결제 누락 대란을 겪습니다. 외부 서버가 보내는 웹훅은 자바스크립트 연산 챌린지를 풀 수 없기 때문에 봇 방어 시스템의 철퇴를 그대로 맞게 됩니다.
# WAF 스킵 룰 예시
(http.request.uri.path starts_with "/webhook/" and http.request.headers["user-agent"] contains "GitHub-Hookshot")
-> Action: Skip (WAF Components: All remaining rules, Super Bot Fight Mode)
클라우드플레어 제로 트러스트(Cloudflare Access)로 n8n 관리자 페이지를 잠가둘 때도 마찬가지입니다.
도메인 전체에 사내 이메일 OTP 로그인을 걸어버리면 외부 웹훅까지 로그인 창에 막히므로, 반드시 /webhook/* 경로는 Access 정책에서 Bypass 규칙으로 빼주어야 합니다.
네트워크 단의 방어벽뿐만 아니라 n8n 워크플로우 첫머리에 Crypto 노드를 두어 HMAC 서명까지 교차 검증해주면 그 어떤 해커가 찔러도 끄떡없는 요새가 완성됩니다.
자주 묻는 질문 (FAQ)
Q. 클라우드플레어를 거친 n8n에서 웹훅 발송자의 실제 IP가 안 잡히는 이유는 무엇인가요?
클라우드플레어의 리버스 프록시 노드가 요청을 중계하면서 원본 IP 대신 프록시 IP가 기록되기 때문이며 N8N_PROXY_HOPS 값을 1로 지정해야 정상 식별됩니다. 도커 환경변수에 해당 옵션과 N8N_TRUST_PROXY=true를 추가하면 해결됩니다.
Q. 결제사나 깃허브 웹훅이 클라우드플레어에서 자꾸 403 오류로 차단되는 이유는 무엇인가요?
봇 파이트 모드가 웹훅 요청을 브라우저가 아닌 자동화 스크립트로 오인해 자바스크립트 챌린지를 강제하기 때문이며 WAF 커스텀 룰에서 바이패스 처리를 해야 합니다. 해당 웹훅 경로와 신뢰할 수 있는 User-Agent에 대해 WAF 검사를 건너뛰도록 설정해야 합니다.
Q. 클라우드플레어 제로 트러스트 적용 시 웹훅 엔드포인트만 외부에 개방하려면 어떻게 하나요?
Access 애플리케이션 정책에서 기본 경로는 SSO 인증을 걸고 /webhook/* 경로에 대해서는 액션을 Bypass로 지정한 별도 규칙을 등록하면 됩니다. 이렇게 분리하면 관리자 대시보드는 완벽히 보호하면서 외부 웹훅 수신은 정상 동작합니다.
웹훅 URL은 한번 털리면 서버 자원 낭비는 물론이고 데이터베이스까지 순식간에 쓰레기 데이터로 뒤덮일 수 있는 가장 취약한 접점입니다. 오늘 정리해 드린 클라우드플레어 n8n 웹훅 트래픽 화이트리스트와 프록시 홉 세팅만 10분 투자해서 적용해 두셔도 새벽에 식은땀 흘리며 서버 재부팅하는 일은 사라집니다. 혹시 n8n 운영하시면서 겪었던 황당한 웹훅 공격 경험이나 여러분만의 방어 꿀팁이 있으신가요? 지금 댓글로 생생한 썰을 들려주세요!