116 lines
5.0 KiB
Markdown
116 lines
5.0 KiB
Markdown
---
|
|
title: CrowdSec 및 SafeLine WAF
|
|
updated: 2026-03-18
|
|
---
|
|
|
|
## DB 테이블
|
|
|
|
DB 테이블은 blocklist (ip PK, reason, origin, expires_at), verified_ips, metadata
|
|
|
|
## 시나리오
|
|
|
|
시나리오: safeline/xml-injection, safeline/command-injection, custom/safeline-waf-blocked (trigger), custom/safeline-waf-repeated (leaky bucket 3+/5min)
|
|
|
|
## Bouncer
|
|
|
|
Bouncer 목록: [[apisix]]-waf-bouncer, bunny-cdn-bouncer, cs-[[cloudflare|cf]]-worker-bouncer
|
|
|
|
### bunny-cdn-bouncer 상세
|
|
|
|
- **동기화**: jp1 `infra-tool` 컨테이너, `/opt/crowdsec-bouncer/bouncer.py`
|
|
- **크론**: 3분 간격 delta sync, 매시 정각 full sync (`/etc/cron.d/crowdsec-bunny-bouncer`)
|
|
- **방식**: CrowdSec LAPI → Bloom filter 생성 → BunnyCDN Edge Script 코드에 임베딩 → 퍼블리시
|
|
- **Edge Script**: BunnyCDN Compute Script ID 64811 (`crowdsec-bouncer-middleware`)
|
|
- Bloom filter (FNV-1a) 기반 IP 차단
|
|
- 차단 시 Cloudflare Turnstile CAPTCHA 챌린지 (false positive 대응)
|
|
- LibSQL DB로 verified IP 4시간 캐싱 + HMAC 서명 쿠키
|
|
- Clean IP 네거티브 캐시 (최대 50K)
|
|
- **적용 풀존**: inouter (5316471) — actions (5330178)은 미적용
|
|
- **API 키**: Vault `secret/infra/crowdsec-bunny-bouncer` (bouncer_key)
|
|
- **소스 코드**: `~/crowdsec-bunny-bouncer/`
|
|
|
|
## 3중 보안 구조 (KR존, 2026-03-15)
|
|
|
|
```
|
|
클라이언트 → BunnyCDN Edge Script (CrowdSec bouncer, 0차)
|
|
→ BunnyCDN WAF (1차)
|
|
→ APISIX + SafeLine WAF (2차)
|
|
→ CrowdSec (분석/3차) → bunny-cdn-bouncer로 피드백 루프
|
|
```
|
|
|
|
### 1차: BunnyCDN WAF (OWASP CRS)
|
|
- 위치: CDN 에지 (오리진 도달 전 차단)
|
|
- 차단: SQLi, XSS, CMDi, SSRF, Shellshock, Log4j
|
|
- 비활성화한 룰: DATA LEAKAGES SQL (id=911) — NocoDB API 응답 오탐 방지
|
|
- 통과: Request Smuggling, NoSQLi, 일반 경로 스캔
|
|
|
|
### 2차: SafeLine WAF (chaitin-waf 플러그인)
|
|
- 위치: APISIX 내부 플러그인 → K3s safeline ns의 detector (safeline-detector.safeline.svc.cluster.local:8000)
|
|
- APISIX plugin_metadata로 detector 주소 설정 (etcd 저장)
|
|
- BunnyCDN을 통과한 공격 차단
|
|
- 라우트별 개별 적용 (gitea는 `.git/` 경로 제외, 바이너리 프로토콜 파싱 불가)
|
|
- SafeLine v9.3.2, Helm chart yaencn/safeline 10.1.0
|
|
- tengine 미사용 (replicas: 0), APISIX가 직접 detector 연동
|
|
- 관리 콘솔: safeline.inouter.com (IngressRoute + ServersTransport insecureSkipVerify)
|
|
- API 토큰: [[vault]] `secret/infra/safeline` (api_token)
|
|
- API 문서: https://safeline.inouter.com/swagger/index.html
|
|
- API 헤더: `X-SLCE-API-TOKEN`
|
|
- 이전: kr2 Incus VM → K3s safeline ns로 이전 (2026-03-18)
|
|
|
|
### 3차: CrowdSec (로그 분석)
|
|
- 위치: jp1 CrowdSec (10.253.100.240:8080)
|
|
- APISIX http-logger → CrowdSec HTTP acquisition (global_rules)
|
|
- 파서: custom/apisix-json-logs (APISIX http-logger JSON 파싱)
|
|
- 반복 공격자 패턴 탐지 (시나리오 매칭)
|
|
- 인증: `Authorization: apisix-crowdsec-log-2024`
|
|
|
|
### 공격 테스트 결과 (sandbox-tokyo → nocodb.inouter.com)
|
|
|
|
| 공격 | 결과 | 차단 위치 |
|
|
|------|------|----------|
|
|
| SQLi (`OR 1=1`) | 403 | BunnyCDN WAF |
|
|
| SQLi (대소문자 혼합) | 403 | BunnyCDN WAF |
|
|
| SQLi (더블 인코딩) | 403 | BunnyCDN WAF |
|
|
| SQLi (POST body) | 403 | BunnyCDN WAF |
|
|
| XSS (`<script>`) | 403 | BunnyCDN WAF |
|
|
| XSS (img onerror) | 403 | BunnyCDN WAF |
|
|
| CMDi | 403 | BunnyCDN WAF |
|
|
| SSRF | 403 | BunnyCDN WAF |
|
|
| Shellshock | 403 | BunnyCDN WAF |
|
|
| Log4j (JNDI) | 403 | BunnyCDN WAF |
|
|
| Path Traversal | 404 | 경로 정규화 (무해) |
|
|
| Request Smuggling | 302 | 통과 (CDN 정규화로 실제 smuggling 불가) |
|
|
| NoSQLi (JSON) | 401 | 통과 (PostgreSQL이라 무효, 인증에서 차단) |
|
|
| WP scan | 404 | 통과 (존재하지 않는 경로) |
|
|
|
|
### waf-kr BunnyCDN Pull Zone (2026-03-25)
|
|
|
|
APISIX SafeLine WAF 전용 CDN zone.
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| Zone ID | 5554681 |
|
|
| Name | waf-kr |
|
|
| Origin | https://220.120.65.245:9443 |
|
|
| 경로 | 인터넷 → BunnyCDN(waf-kr) → OpenWrt HAProxy(:9443) → APISIX NodePort(31137) → SafeLine WAF → K3s 서비스 |
|
|
|
|
등록된 호스트: `juiceshop.keepanker.cv` (OWASP Juice Shop, WAF 테스트용)
|
|
|
|
### Juice Shop WAF 테스트 결과 (2026-03-25)
|
|
|
|
| 요청 | 결과 | WAF Action |
|
|
|------|------|------------|
|
|
| 일반 GET / | 200 | pass |
|
|
| SQLi (`?id=1 OR 1=1`) | 403 | reject (SafeLine) |
|
|
| XSS (`?q=<script>alert(1)</script>`) | 403 | reject (SafeLine) |
|
|
|
|
### 다음 단계: CrowdSec 자동 차단
|
|
|
|
계획: SafeLine WAF 차단 로그 → APISIX http-logger → jp1 CrowdSec → 커스텀 시나리오 (3회 차단 시 ban) → bouncer로 APISIX ip-restriction 적용
|
|
|
|
### 참고
|
|
- BunnyCDN WAF 차단 시 오리진에 로그 안 옴 → CrowdSec에 미수신
|
|
- 리얼 IP: 외부 트래픽은 `X-Forwarded-For`로 정상 전달, LAN은 `127.0.0.1`
|
|
- OpenWrt에 CrowdSec firewall bouncer 설치 가능하나 DNAT 구조라 리얼 IP 매칭 불가
|
|
- chaitin-waf 플러그인은 `plugin_attr`이 아닌 **`plugin_metadata`(etcd)**에서 detector 노드를 읽음 — 주의
|