- history/README.md: 변경 이력/인시던트 기록 규약 - history/2026-04-10-edge-cleanup.md: 오늘의 엣지 정리·인시던트·복구 전체 연대기 - infra/cloudflare.md: 연대기 주석/strikethrough/인시던트 서사 제거, 현재 사실만 - infra/crowdsec-safeline.md: 인시던트 bullet 제거, 과거 이력은 history/ 참조 - services/bunnycdn-security.md: sitekey 이력표 제거, 현재 위젯 정보만 - infra/nas-storage.md: reverse proxy 섹션의 날짜 주석 제거
6.1 KiB
title, updated, tags
| title | updated | tags | |||||
|---|---|---|---|---|---|---|---|
| BunnyCDN 엣지 보안 (CrowdSec + 국가 차단) | 2026-04-10 |
|
구성
- 엣지 스크립트:
crowdsec-bouncer-middleware(ID64811, ScriptType2Middleware) - 연결된 Pull Zone: iron-kr (5555227), iron-jp (5555247) — 둘 다
MiddlewareScriptId: 64811공유 - 미장착 풀존:
iron-git(git smart HTTP 보호 우회, 의도),iron-kr-waf(SafeLine 다운스트림),i-gate(idle) - bloom filter 동기화: jp1
infra-tool컨테이너의/opt/crowdsec-bouncer/bouncer.py(3분 delta + 매시 full sync) - 소스: crowdsec-safeline bloom filter (FNV-1a 임베디드
BLOOM_B64) + Cloudflare Turnstile 캡차
cfb-manager (K8s default/cfb-manager) 는 Cloudflare Worker bouncer 관리 API 이며 BunnyCDN 미들웨어 64811 와 무관.
동작 순서
요청 → BunnyCDN 엣지
1. 국가 차단 (Cdn-RequestCountryCode 헤더 기반)
- 차단 국가 → 403 반환
2. CrowdSec bloom filter (악성 IP 판별)
- hit → 캡차 또는 403
- miss → 통과
3. 캡차 (Cloudflare Turnstile)
- bloom hit IP 에게 캡차 제시
- 통과 시 4시간 허용 (DB + 쿠키)
4. 정상 → Origin 으로 전달
차단 국가
북미·유럽·구소련 권역:
US, CA, GB, DE, FR, IT, ES, NL, BE, AT, CH, SE, NO, DK, FI, PL, CZ, PT, IE, RO, HU, BG, HR, SK, SI, LT, LV, EE, LU, MT, CY, GR, IS, UA, RU, BY
주의사항
- BunnyCDN Traffic Manager 국가 차단은 사용하지 않음 — Let's Encrypt 검증 및 Origin Shield fetch 를 막아서 Free SSL 발급 실패 + 500 에러 유발
- 엣지 스크립트의 국가 차단은
Cdn-RequestCountryCode헤더 기반 → CDN 내부 요청 (Let's Encrypt, Origin Shield) 에는 이 헤더가 없으므로 영향 없음 - Free SSL 자동 발급/갱신 정상 동작
Pull Zone
| ID | 이름 | OriginUrl | Middleware | Smart Cache | IgnoreQS | BlockNoRef | 비고 |
|---|---|---|---|---|---|---|---|
| 5555227 | iron-kr |
https://220.120.65.245 |
64811 | ✅ | false | true | 메인 inouter.com 계열 |
| 5555247 | iron-jp |
https://172.233.93.180 |
64811 | ✅ | false | true | anvil.it.com 계열 |
| 5555224 | iron-kr-waf |
https://220.120.65.245:9443 |
null | ❌ | true | true | SafeLine WAF 다운스트림 보호 |
| 5584382 | iron-git |
https://220.120.65.245 |
null | ❌ | true | false | git smart HTTP 보호 우회 (의도) |
| 5557897 | i-gate |
https://172.233.93.180 |
null | ❌ | true | true | idle 슬롯 |
공통 설정: EnableLogging: true, EnableWebSockets: true (max 500), EnableGeoZone*: true (전 지역), BlockedCountries: [], BlockedIps: [], EdgeRules: [] (모든 분기는 미들웨어 코드 안에서), OriginType: 0 (URL), VerifyOriginSSL: false, EnableOriginShield: false. OptimizerEnabled 는 iron-kr / iron-jp 만 true.
호스트네임 인벤토리
| Pull Zone | Hostname | 시스템? |
|---|---|---|
| iron-kr | iron-kr.b-cdn.net |
✅ |
| iron-kr | actions.it.com |
|
| iron-kr | n8n.inouter.com |
|
| iron-kr | jarvis.inouter.com |
|
| iron-kr | telegram-webhook.inouter.com |
|
| iron-kr | vault.inouter.com |
|
| iron-kr | outline.inouter.com |
|
| iron-jp | iron-jp.b-cdn.net |
✅ |
| iron-jp | anvil.it.com |
|
| iron-jp | n8n.anvil.it.com |
|
| iron-jp | tg.anvil.it.com |
|
| iron-jp | linode.actions.it.com |
|
| iron-kr-waf | iron-kr-waf.b-cdn.net |
✅ |
| iron-kr-waf | juiceshop.keepanker.cv |
|
| iron-git | iron-git.b-cdn.net |
✅ |
| iron-git | gitea.inouter.com |
|
| i-gate | i-gate.b-cdn.net |
✅ |
총 17 (사용자 12 + 시스템 5). 전부 HasCertificate: true (Let's Encrypt 자동 발급/갱신). Bunny API 응답에는 인증서 만료일이 노출되지 않아 만료 모니터링은 외부 cert 체크 또는 Bunny 대시보드 알림에 의존.
Edge Script / Middleware attach
| Script ID | 이름 | Type | 부착 풀존 |
|---|---|---|---|
| 64811 | crowdsec-bouncer-middleware |
2 (Middleware) | iron-kr, iron-jp |
기타 풀존 미장착 사유: iron-git 은 git smart HTTP 바이너리 보호 불가라 의도적 우회, iron-kr-waf 는 SafeLine 이 담당, i-gate 는 idle.
CrowdSec bloom filter 동기화
CrowdSec LAPI (jp1, 10.253.100.240:8080) → decision stream
↓
infra-tool 컨테이너 /opt/crowdsec-bouncer/bouncer.py
├ 3분 delta sync
└ 매시 full sync
↓
bloom filter (FNV-1a) base64 → BunnyCDN compute API
→ Edge Script 64811 코드의 BLOOM_B64 / BLOOM_VERSION 상수 정규식 교체
→ publish
↓
엣지 스크립트가 요청마다 client IP 를 bloom filter 로 체크
hit → Turnstile 캡차 (verified 4h 쿠키)
miss → origin 통과
Turnstile sitekey
미들웨어 64811 의 TURNSTILE_SITE_KEY 와 TURNSTILE_SECRET_KEY 는 env 변수로 주입 (코드에 hardcoded 아님). 현재 값은 cloudflare#Turnstile 위젯 의 inouter-bunny-middleware (0x4AAAAAAC3otPWhldI96Aks). 이 위젯은 cs-cf-worker-bouncer 의 이름 패턴 (crowdsec-cloudflare-worker-bouncer-widget) 과 다르므로 bouncer rotation 대상 외 — 수동 관리.
위젯 교체 절차:
- 신규 위젯 생성 (이름에
bouncer포함 금지) - Vault
secret/cloud/cloudflare/turnstile-inouter-bunny에sitekey,secret,name저장 - 미들웨어 64811 의
TURNSTILE_SITE_KEY/TURNSTILE_SECRET_KEYenv 두 변수 동시 갱신 + publish - 옛 sitekey 가 미들웨어 env 에 남아있지 않은지 검증 (grep), 옛 위젯 삭제 가능
특이사항
- i-gate (5557897): 시스템 호스트 1개, 0 byte 트래픽. 빈 슬롯 — 용도 부여 또는 제거 필요
- Turnstile sitekey zone gap:
TURNSTILE_SITE_KEY가inouter.comzone 단일 키 →actions.it.com,anvil.it.com,*.b-cdn.net호스트에서 ban 발동 시 캡차 위젯 로드 실패 가능. zone별 dispatcher 또는 multi-domain 위젯 통합 TODO
관련 문서
- bunnycdn — BunnyCDN API 레퍼런스
- crowdsec-safeline — CrowdSec 및 SafeLine WAF 설정
- cloudflare — Cloudflare DNS/CDN (Turnstile 위젯 정본)