diff --git a/history/2026-04-23-netbis-firewall-bouncer-migration.md b/history/2026-04-23-netbis-firewall-bouncer-migration.md new file mode 100644 index 0000000..ccf2bab --- /dev/null +++ b/history/2026-04-23-netbis-firewall-bouncer-migration.md @@ -0,0 +1,143 @@ +--- +date: 2026-04-23 +topic: Netbis Firewall Bouncer 도입 (Worker Bouncer 병행) +areas: [infra/security/crowdsec-safeline.md, services/netbis.md, infra/security/cloudflare.md] +tags: [crowdsec, cloudflare, bouncer, netbis, cost-control] +--- + +## 배경 + +Netbis 계정 2026-04 Cloudflare Workers 요금 $100 중 **82%가 KV read 비용 오버**. Worker Bouncer(`netbis-cf`) 구조는 요청이 CF에 도달 → Worker 실행 → KV.get으로 IP 확인 → 차단/통과 순서. 즉 Worker가 실행돼야 KV read가 발생. 악성 IP라 결국 차단해도 KV read 비용은 이미 청구됨. + +기존 Bouncer는 제거하지 않고 **병행**. Worker Bouncer는 Turnstile/captcha 등 정교한 챌린지용 유지, Firewall Rule Bouncer는 엣지 즉시 차단으로 Worker 실행 자체를 회피해 비용 방어. + +## 최종 구조 + +``` +LAPI (jp1:crowdsec, 10.253.100.240:8080) + ├─ netbis-cf → CF Worker + KV (기존, Turnstile/정교) + └─ netbis-cf-firewall → CF Rule List + Firewall Rule (신규, 엣지 차단) +``` + +CF 요청 평가 순서상 Firewall Rule → Worker. Firewall Rule에서 block되면 Worker는 실행되지 않아 KV read 없음. + +## 실행 단계 + +### 1. 사전 조사 — CF 플랜 / IP List 용량 + +6개 zone 플랜 (scoped API 아님, global_api_key X-Auth 조회): + +| Zone | Zone ID | Plan | +|---|---|---| +| fall-vip.com | 662312b0ca619d1d5c8f4c112150d749 | Pro | +| fall-mvp.com | 6c171579912a271c0fc89c8187493b0f | Free | +| fall-vip7.com | a8832b9d3b546f96505abeadea4750d1 | Free | +| psd777.com | a14533c2937b19e5b7ed19cbecd58679 | Pro | +| rss-555.com | 6d4b084940520c1f820927e5d8ade2c6 | Pro | +| rss-7790.com | d9db9e50e202339326498baa340a9d16 | Pro | + +Free/Pro/Biz 공통 IP list 상한 = 10,000. `total_ip_list_capacity: 10000` 설정. + +### 2. 컨테이너 패키지 설치 + +`jp1:netbis-cf-bouncer` (기존 Worker Bouncer 컨테이너 재사용, 서비스 분리). + +```bash +incus exec jp1:netbis-cf-bouncer -- apt-get update +incus exec jp1:netbis-cf-bouncer -- apt-get install -y crowdsec-cloudflare-bouncer # v0.3.0 +``` + +### 3. LAPI 바우언서 등록 + +```bash +incus exec jp1:crowdsec -- cscli bouncers add netbis-cf-firewall +# API key 출력을 즉시 설정 파일에 주입 +``` + +### 4. CF scoped API token 발급 + +v0.3.0 바우언서는 `cloudflare.NewWithAPIToken(token)` **Bearer-only**. Global API Key는 X-Auth 헤더 전용이라 `token` 필드에 그대로 못 넣음 (Bearer auth에서 `Invalid request headers` 거절 확인). 해결: global_api_key 경유로 scoped API token 신규 발급. + +| 항목 | 값 | +|---|---| +| Token 이름 | `crowdsec-cloudflare-bouncer-netbis` | +| Account 스코프 | Netbis (8fcf3c7876332aba33e974cbbfdad951) 전용, All zones | +| TTL | 무기한 | +| 권한 | Account Rule Lists Write + Account Settings Read + Zone Firewall Services Write + Zone WAF Write + Zone Read | +| 저장 | Vault `secret/cloud/cloudflare-netbis` `api_token` 필드 (기존 `global_api_key` 보존) | + +발급 시 CF API permission_group IDs: +- `2edbf20661fd4661b0fe10e9e12f485c` Account Rule Lists Write +- `c1fde68c7bcc44588cbb6ddbc16d6480` Account Settings Read +- `43137f8d07884d3198dc0ee77ca6e79b` Firewall Services Write +- `fb6778dc191143babbfaa57993f1d275` Zone WAF Write +- `c8fed203ed3043cba015a93ad1616f1f` Zone Read + +메모: `Account.Account Filter Lists:Edit` (user 스펙 명)은 현재 CF에서 `Account Rule Lists Write`로 이름 변경됨. 기능 동일. Rulesets API 경로 사용 시 403 발생하면 `Account Rulesets:Edit` (`56907406c3d548ed902070ec4df0e328`) 추가 후 재발급. + +### 5. Config 파일 전송 + +`/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` — 전체 파일 push (sed -i 금지, Incus 컨테이너 내 in-place 편집 피함): + +```bash +incus exec jp1:netbis-cf-bouncer --mode=non-interactive -- \ + bash -c 'cat > /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml && chmod 600 /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml' \ + < /tmp/crowdsec-cloudflare-bouncer.yaml +``` + +주요 값: +- `crowdsec_lapi_url: http://10.253.100.240:8080` +- `crowdsec_lapi_key`: cscli bouncers add 출력 +- `cloudflare_config.accounts[0].token`: 위에서 발급한 scoped token +- `ip_list_prefix: crowdsec` +- `default_action: block`, 모든 zone `actions: [block]` 단일 +- `total_ip_list_capacity: 10000` + +### 6. 서비스 기동 + CF 리소스 자동 생성 + +```bash +incus exec jp1:netbis-cf-bouncer -- systemctl enable --now crowdsec-cloudflare-bouncer +``` + +바우언서 부팅 직후: +- CF IP List `crowdsec` (ID `b14745a3306a4f81b46d96593135f78b`) 생성 — Account Rule List +- 6개 zone에 firewall rule 자동 생성 (ip.src in $crowdsec → block) +- LAPI 스트림 첫 풀: 28,885건 decision 수신, 10,000건 CF로 push + +### 7. E2E 검증 + +- 추가 경로: `cscli decisions add --ip 203.0.113.99 --duration 2m --type ban` → LAPI 기록 확인 → 바우언서 다음 사이클에 처리. **단 LAPI 총량 > 10k 상황에서 신규 IP는 용량 우선순위 밀려 CF 반영 보장 안 됨**. 실제 테스트에서 `1 IPs won't be inserted/kept to avoid exceeding IP list limit` 경고 발생 +- 삭제 경로: CF 리스트 내 IP (`1.14.7.100`) LAPI decision 삭제 → 다음 사이클 제거. CF API 레이트리밋(10040) 영향으로 즉시 반영 아닌 다음 rate-limit window 이후 반영 +- 실제 push 성공 증거: 바우언서 로그 `added 10000 new IPs and deleted 0 IPs` — 초기 sync 정상 + +### 8. Obsidian 커밋 + graphify + +- `infra/security/crowdsec-safeline.md` — `### netbis-cf-firewall` 서브섹션 추가, 바우언서 역할 분담 도식 +- `history/2026-04-23-netbis-firewall-bouncer-migration.md` — 이 문서 +- commit: `netbis: crowdsec-cloudflare-bouncer (firewall rule) 추가 — worker bouncer와 병행` +- `/graphify update ~/obsidian` + +## 운영 관찰 사항 + +### IP List 용량 제약 + +LAPI 전체 decision 수가 ~29k, CF Pro 제한 10k. 바우언서는 10k 초과분을 우선순위 선별해 push하지 않음. 결과: +- **오래된/높은 우선순위 decision은 CF 반영** (일관성 있는 차단) +- **신규 short-TTL decision은 CF 반영 불확실** (커뮤니티 CAPI 결정 다수 + 큰 TTL 누적 영향) +- 운영상 Worker Bouncer 경로가 여전히 필요한 이유 — Firewall Rule로 못 잡는 부분은 Worker Bouncer가 KV 조회로 커버 + +### CF API 레이트리밋 (10040) + +바우언서 `update_frequency: 30s`에서 매 사이클 신규 IP 1~수건 push 시도. CF Rule List items API 제한(분당 N건)에 금방 도달. 로그에 `you have been ratelimited` 출력, 다음 사이클에서 자동 재시도. + +### 향후 개선 후보 + +- CrowdSec LAPI `only_include_decisions_from` 필터로 CAPI 커뮤니티 feed 제외 → 차단 quality 향상하고 CF 10k 용량 효율화 +- `include_scenarios_containing: ["http"]` 등 시나리오 필터로 포커스 +- Enterprise zone 업그레이드 시 `total_ip_list_capacity: 50000` 가능 + +## 영향 및 롤백 + +- Worker Bouncer `netbis-cf` 무변경 — 병행 운영, 기존 Turnstile/captcha 동작 그대로 +- 롤백: `systemctl stop crowdsec-cloudflare-bouncer && cscli bouncers delete netbis-cf-firewall` + CF IP List/Firewall Rule 수동 삭제 (Rule List `crowdsec` + 6 zone firewall rules) +- CF 요금 방어 효과는 다음 월 청구서에서 확인 가능 diff --git a/infra/security/crowdsec-safeline.md b/infra/security/crowdsec-safeline.md index f4abb88..50a5644 100644 --- a/infra/security/crowdsec-safeline.md +++ b/infra/security/crowdsec-safeline.md @@ -1,6 +1,6 @@ --- title: CrowdSec 및 SafeLine WAF -updated: 2026-04-17 +updated: 2026-04-23 --- ## CrowdSec LAPI @@ -236,6 +236,56 @@ Kappa 계정용 `cs-cf-worker-bouncer`와 별도 컨테이너로 분리 운영. | Turnstile | 6개 zone managed 모드, 168시간 secret key 로테이션 | | 로그 소스 | sandbox-tokyo APISIX → CrowdSec http-logger (8085/apisix-logs) | +### netbis-cf-firewall (Cloudflare Firewall Rule — Netbis 계정, 비용 방어) + +`netbis-cf` Worker Bouncer와 **병행** 운영. Worker Bouncer는 Worker 실행 후 KV.get 구조라 KV read 비용이 그대로 청구됨 (2026-04 청구서 $100 중 82%가 KV read 오버). Firewall Rule 바우언서는 CF 엣지 단계에서 IP를 차단 — Worker가 실행되지 않아 Worker/KV 비용 0. Worker Bouncer는 Turnstile/정교한 챌린지용으로 계속 사용. + +| 항목 | 값 | +|------|-----| +| 위치 | jp1 Incus `netbis-cf-bouncer` 컨테이너 (10.253.103.33, Worker Bouncer와 동일 컨테이너) | +| 패키지 | `crowdsec-cloudflare-bouncer` v0.3.0 (crowdsec apt repo) | +| 바운서 이름 | `netbis-cf-firewall` (cscli 등록) | +| 설정 | `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` (mode 600) | +| 동기화 | LAPI 10초, CF 업데이트 30초 | +| CF 계정 | Netbis (netbis@netbis.io, Account ID 8fcf3c7876332aba33e974cbbfdad951) | +| CF 인증 | scoped API token `crowdsec-cloudflare-bouncer-netbis` (Vault `secret/cloud/cloudflare-netbis` `api_token` 필드). 권한: Account Rule Lists Write + Account Settings Read + Zone Firewall Services Write + Zone WAF Write + Zone Read (All zones of account). 발급 주체: global_api_key 경유 CF API | +| IP List | `crowdsec` (CF Rule Lists, account scope) — ip_list_prefix 설정 | +| IP List 용량 | 10000 (Free/Pro 공통 상한, `total_ip_list_capacity`) | +| 보호 zone | fall-vip.com, fall-mvp.com, fall-vip7.com, psd777.com, rss-555.com, rss-7790.com (6) | +| Action | `block` (단일) — 모든 zone, default_action 동일 | +| Firewall Rule | 각 zone 당 1건 (6건 자동 생성, `ip.src in $crowdsec` 조건) | +| 로그 | `/var/log/crowdsec-cloudflare-bouncer.log` (log_mode: file) | + +### 바우언서 역할 분담 (Netbis) + +``` +LAPI (jp1 crowdsec) → 두 바우언서 병행 구독 + ├─ netbis-cf (Worker Bouncer) → CF Worker + KV (정교한 차단·captcha·Turnstile) + └─ netbis-cf-firewall (FW Bouncer) → CF Rule List + Firewall Rule (엣지 즉시 차단, Worker 실행 없음) +``` + +Firewall Rule이 먼저 평가되어 차단되면 Worker는 아예 실행되지 않아 KV 비용 미발생. Worker까지 도달한 요청에 대해서만 captcha/Turnstile 플로우 적용. + +### 운영 주의 + +- IP 용량 초과 시 바우언서 로그에 `N IPs won't be inserted/kept to avoid exceeding IP list limit` 경고. LAPI 전체 decision 수가 10000 초과면 일부 IP는 CF에 반영 안 됨 (바우언서가 우선순위로 선별) +- CF Rule Lists items API는 레이트리밋 존재 (에러 10040). 바우언서는 자동 재시도로 다음 사이클에서 처리 +- scoped API token 권한 부족으로 CF Rulesets API 403 발생 시 `Account Rulesets:Edit` 추가 후 재발급 필요 + +### 적용 / 재기동 + +```bash +# config 전체 푸시 (sed -i 금지) +cat updated-config.yaml | ssh incus-jp1 'incus exec netbis-cf-bouncer -- tee /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml >/dev/null' +ssh incus-jp1 'incus exec netbis-cf-bouncer -- systemctl restart crowdsec-cloudflare-bouncer' + +# 상태 +ssh incus-jp1 'incus exec netbis-cf-bouncer -- systemctl status crowdsec-cloudflare-bouncer' +ssh incus-jp1 'incus exec netbis-cf-bouncer -- tail -f /var/log/crowdsec-cloudflare-bouncer.log' +``` + +도입 이력: [[../../history/2026-04-23-netbis-firewall-bouncer-migration|history]] + ### bunny-cdn-bouncer (BunnyCDN Edge Script) | 항목 | 값 |