- sync/bouncer.py: CrowdSec LAPI → bloom filter → BunnyCDN Edge Script - edge/middleware.ts: BunnyCDN edge middleware with bloom filter + Turnstile CAPTCHA - README.md: architecture and deployment docs
143 lines
3.8 KiB
Markdown
143 lines
3.8 KiB
Markdown
# CrowdSec BunnyCDN Bouncer
|
|
|
|
CrowdSec LAPI에서 탐지한 악성 IP를 BunnyCDN 에지에서 선제 차단하는 bouncer.
|
|
|
|
## 아키텍처
|
|
|
|
```
|
|
[CrowdSec LAPI (jp1, 10.253.100.240:8080)]
|
|
↓ 크론잡 (3분 간격)
|
|
↓ /v1/decisions/stream API
|
|
↓
|
|
[bouncer.py]
|
|
→ IP 목록 → Bloom filter 생성
|
|
→ BunnyCDN Compute API로 Edge Script 코드 업데이트
|
|
↓
|
|
[BunnyCDN Edge Script (middleware.ts)]
|
|
→ X-Real-Ip 헤더에서 클라이언트 IP 추출
|
|
→ Bloom filter로 IP 매칭
|
|
→ 매칭되면 Turnstile CAPTCHA 챌린지
|
|
→ 캡차 통과하면 4시간 검증 (verified_ips DB)
|
|
→ Bloom filter에 없으면 오리진으로 통과
|
|
```
|
|
|
|
## 구성 요소
|
|
|
|
### sync/bouncer.py
|
|
|
|
CrowdSec LAPI → BunnyCDN Edge Script 동기화 스크립트.
|
|
|
|
- CrowdSec LAPI stream endpoint에서 ban 결정 fetch
|
|
- IP 목록을 bloom filter (FNV-1a 해시)로 변환
|
|
- BunnyCDN Compute API로 Edge Script 코드 내 `BLOOM_B64` 상수 교체
|
|
- 스크립트 퍼블리시
|
|
|
|
### edge/middleware.ts
|
|
|
|
BunnyCDN Edge Script 미들웨어 (현재 inouter 풀존에 배포됨, script ID: 64811).
|
|
|
|
- `@bunny.net/edgescript-sdk` 사용
|
|
- Bloom filter 기반 IP 차단
|
|
- Cloudflare Turnstile CAPTCHA로 false positive 대응
|
|
- LibSQL DB로 verified IP 4시간 캐싱
|
|
- HMAC 서명 쿠키로 세션 유지
|
|
- Clean IP 캐시 (네거티브 캐시, 최대 50K)
|
|
|
|
## 환경 변수
|
|
|
|
### bouncer.py
|
|
|
|
| 변수 | 설명 | 기본값 |
|
|
|------|------|--------|
|
|
| `CROWDSEC_LAPI_URL` | CrowdSec LAPI URL | `http://10.253.100.240:8080` |
|
|
| `CROWDSEC_BOUNCER_KEY` | Bouncer API key | (required) |
|
|
| `BUNNY_API_KEY` | BunnyCDN account API key | (required) |
|
|
| `BUNNY_SCRIPT_ID` | Edge Script ID | `64811` |
|
|
| `STATE_FILE` | 상태 파일 경로 | `/var/lib/crowdsec-bouncer/state.json` |
|
|
|
|
### Edge Script Variables
|
|
|
|
| 변수 | 설명 |
|
|
|------|------|
|
|
| `TURNSTILE_SITE_KEY` | Cloudflare Turnstile site key |
|
|
| `TURNSTILE_SECRET_KEY` | Cloudflare Turnstile secret key |
|
|
| `CACHE_MODE` | 캐시 모드 (auto) |
|
|
| `BUNNY_DATABASE_URL` | LibSQL database URL |
|
|
| `BUNNY_DATABASE_AUTH_TOKEN` | LibSQL auth token |
|
|
|
|
## 배포
|
|
|
|
### 1. CrowdSec bouncer 등록
|
|
|
|
```bash
|
|
# jp1 crowdsec 컨테이너에서
|
|
cscli bouncers add bunny-cdn-bouncer
|
|
# 출력된 API key를 Vault에 저장
|
|
```
|
|
|
|
### 2. Vault에 시크릿 저장
|
|
|
|
```bash
|
|
vault kv put secret/infra/crowdsec-bunny-bouncer \
|
|
bouncer_key=<CROWDSEC_BOUNCER_KEY>
|
|
```
|
|
|
|
### 3. Incus 컨테이너에 배포
|
|
|
|
```bash
|
|
# jp1 infra-tool 컨테이너
|
|
pip3 install -r sync/requirements.txt
|
|
cp sync/bouncer.py /opt/crowdsec-bouncer/bouncer.py
|
|
```
|
|
|
|
### 4. Cron 설정
|
|
|
|
```bash
|
|
# /etc/cron.d/crowdsec-bunny-bouncer
|
|
*/3 * * * * root CROWDSEC_BOUNCER_KEY="..." BUNNY_API_KEY="..." /usr/bin/python3 /opt/crowdsec-bouncer/bouncer.py >> /var/log/crowdsec-bouncer.log 2>&1
|
|
# 매시 정각: 전체 동기화 (startup mode)
|
|
3 * * * * root CROWDSEC_BOUNCER_KEY="..." BUNNY_API_KEY="..." /usr/bin/python3 /opt/crowdsec-bouncer/bouncer.py --startup >> /var/log/crowdsec-bouncer.log 2>&1
|
|
```
|
|
|
|
## 테스트
|
|
|
|
```bash
|
|
# 테스트 IP 추가
|
|
ssh incus-jp1 "incus exec crowdsec -- cscli decisions add -i 198.51.100.1 -d 10m -R 'test'"
|
|
|
|
# 동기화 실행
|
|
python3 sync/bouncer.py --startup -v
|
|
|
|
# 테스트 IP 제거
|
|
ssh incus-jp1 "incus exec crowdsec -- cscli decisions delete -i 198.51.100.1"
|
|
python3 sync/bouncer.py -v
|
|
```
|
|
|
|
## 풀존 적용 현황
|
|
|
|
| 풀존 | ID | 미들웨어 | 상태 |
|
|
|------|-----|---------|------|
|
|
| inouter | 5316471 | 64811 | 적용됨 |
|
|
| actions | 5330178 | - | 미적용 |
|
|
|
|
## Bloom Filter 세부
|
|
|
|
- 해시 함수: FNV-1a (32-bit) double hashing
|
|
- False positive rate: 0.1%
|
|
- 헤더: 8 bytes (m: uint32 LE, k: uint32 LE)
|
|
- 페이로드: bit array
|
|
- 버전: MD5 해시 앞 16자 (변경 감지용)
|
|
|
|
## Edge Script Pricing
|
|
|
|
| 항목 | 단가 |
|
|
|------|------|
|
|
| 요청 | $0.20 / 1M |
|
|
| CPU 시간 | $0.02 / 1,000초 |
|
|
|
|
처음 25M 요청은 무료. CDN 대역폭 비용은 별도.
|
|
|
|
## License
|
|
|
|
MIT
|