171 lines
6.3 KiB
Markdown
171 lines
6.3 KiB
Markdown
---
|
|
title: CrowdSec 및 SafeLine WAF
|
|
updated: 2026-03-28
|
|
---
|
|
|
|
## CrowdSec LAPI
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| 위치 | jp1 Incus `crowdsec` 컨테이너 |
|
|
| LAPI | `http://10.253.100.240:8080` |
|
|
| 관리 | `ssh incus-jp1 "incus exec crowdsec -- cscli ..."` |
|
|
|
|
## 로그 수집 (Acquisition)
|
|
|
|
### Traefik → CrowdSec (Vector)
|
|
|
|
```
|
|
Traefik DaemonSet (stdout JSON accessLog)
|
|
→ Vector Agent DaemonSet (K3s logging ns, kubernetes_logs source)
|
|
→ VRL transform (access log만 필터, non-JSON abort)
|
|
→ HTTP sink (배치 50건, 5초)
|
|
→ CrowdSec HTTP acquisition (:8086/traefik-logs)
|
|
→ crowdsecurity/traefik-logs 파서
|
|
```
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| Vector | Helm `vector/vector` 0.51.0, Agent DaemonSet (3노드) |
|
|
| Values | `~/k8s/vector/values.yaml` |
|
|
| CrowdSec 포트 | 8086 |
|
|
| 인증 | `Authorization: traefik-crowdsec-log-2024` |
|
|
| 파서 | `crowdsecurity/traefik-logs` (Hub, JSON 모드) |
|
|
|
|
### APISIX → CrowdSec (http-logger)
|
|
|
|
```
|
|
APISIX (global_rules http-logger 플러그인)
|
|
→ 배치 50건, 5초 buffer
|
|
→ CrowdSec HTTP acquisition (:8085/apisix-logs)
|
|
→ custom/apisix-json-logs 파서
|
|
```
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| 설정 | APISIX Admin API global_rules/1 |
|
|
| CrowdSec 포트 | 8085 |
|
|
| 인증 | `Authorization: apisix-crowdsec-log-2024` |
|
|
| 파서 | `custom/apisix-json-logs` (로컬) |
|
|
|
|
### 리얼 IP 처리
|
|
|
|
| 경로 | 설정 | source IP 추출 |
|
|
|------|------|---------------|
|
|
| BunnyCDN → HAProxy → **Traefik** | `forwardedHeaders.trustedIPs: 127/8, 10/8, 172.16/12, 192.168/16` + `externalTrafficPolicy: Local` | `ClientHost` (X-Forwarded-For에서 추출) |
|
|
| BunnyCDN → HAProxy → **APISIX** | `real_ip_header: X-Forwarded-For` + `real_ip_from: 127/8, 10.42/16, 10.43/16, 192.168.9/24, 100.64/10` | `client_ip` (nginx real_ip 모듈) |
|
|
|
|
Traefik values: `~/k8s/traefik/values.yaml`
|
|
|
|
## 시나리오
|
|
|
|
| 유형 | 시나리오 | 출처 |
|
|
|------|---------|------|
|
|
| HTTP | http-probing, http-crawl-non_statics, http-sensitive-files, http-backdoors-attempts, http-sqli-probing, http-xss-probing 등 | Hub |
|
|
| SafeLine | custom/safeline-waf-blocked (trigger), custom/safeline-waf-repeated (3+/5min) | 로컬 |
|
|
| CAPI | http:dos, http:scan, ssh:bruteforce (커뮤니티) | 자동 |
|
|
|
|
## Bouncer
|
|
|
|
### cs-cf-worker-bouncer (Cloudflare Worker)
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| 위치 | jp1 Incus `cs-cf-worker-bouncer` 컨테이너 |
|
|
| 설정 | `/etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml` |
|
|
| 정본 | `gitea.inouter.com/kaffa/k8s` → `configs/crowdsec/crowdsec-cloudflare-worker-bouncer.yaml` |
|
|
| 동기화 | 10초 (decision stream polling) |
|
|
| 방식 | LAPI → bouncer → Cloudflare Worker KV (bloom filter) → Worker에서 차단/captcha |
|
|
| 보호 zone | keepanker.cv, actions.it.com, ironclad.it.com, inouter.com, servidor.it.com |
|
|
| Turnstile | 5개 zone managed 모드, 168시간 secret key 로테이션 |
|
|
|
|
#### Decision 수동 관리
|
|
|
|
bouncer의 `lapi_key`는 **읽기 전용**. decision 추가/삭제는 `crowdsec` 컨테이너에서 `cscli`로:
|
|
|
|
```bash
|
|
# ban 추가
|
|
ssh incus-jp1 "incus exec crowdsec -- cscli decisions add --ip 1.2.3.4 --duration 2m --reason 'manual ban' --type ban"
|
|
|
|
# 확인
|
|
ssh incus-jp1 "incus exec crowdsec -- cscli decisions list --ip 1.2.3.4"
|
|
|
|
# 삭제
|
|
ssh incus-jp1 "incus exec crowdsec -- cscli decisions delete --ip 1.2.3.4"
|
|
```
|
|
|
|
bouncer 컨테이너에서 `curl POST /v1/decisions`로 직접 넣으면 등록 안 됨 — API 키 권한 제한.
|
|
|
|
#### 설정 변경 시 주의
|
|
|
|
**`sed -i` 사용 금지** — Incus 컨테이너 내에서 `sed -i`로 수정 시 이전 내용이 파일 끝에 남아 YAML 깨짐. 반드시 전체 파일 덮어쓰기:
|
|
|
|
```bash
|
|
cat updated-config.yaml | ssh incus-jp1 "incus file push - cs-cf-worker-bouncer/etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml"
|
|
ssh incus-jp1 "incus exec cs-cf-worker-bouncer -- systemctl restart crowdsec-cloudflare-worker-bouncer"
|
|
```
|
|
|
|
### bunny-cdn-bouncer (BunnyCDN Edge Script)
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| 동기화 | jp1 `infra-tool` 컨테이너, `/opt/crowdsec-bouncer/bouncer.py` |
|
|
| 크론 | 3분 delta sync, 매시 full sync |
|
|
| 방식 | LAPI → Bloom filter → BunnyCDN Edge Script 임베딩 → 퍼블리시 |
|
|
| Edge Script | ID 64811 (`crowdsec-bouncer-middleware`), FNV-1a bloom filter |
|
|
| 차단 시 | Cloudflare Turnstile CAPTCHA + LibSQL verified IP 캐싱(4h) |
|
|
| 적용 풀존 | iron-jp (5555247) |
|
|
| API 키 | Vault `secret/infra/crowdsec-bunny-bouncer` |
|
|
| 소스 | `~/crowdsec-bunny-bouncer/` |
|
|
|
|
## 보안 구조
|
|
|
|
```
|
|
클라이언트 → BunnyCDN Edge Script (CrowdSec bouncer, 0차)
|
|
→ BunnyCDN WAF (OWASP CRS, 1차)
|
|
→ Traefik / APISIX + SafeLine WAF (2차)
|
|
→ CrowdSec 로그 분석 (3차) → bouncer 피드백 루프
|
|
```
|
|
|
|
### 0차: BunnyCDN Edge Script (CrowdSec bouncer)
|
|
- Bloom filter로 악성 IP 즉시 차단
|
|
- false positive 시 Turnstile CAPTCHA로 구제
|
|
|
|
### 1차: BunnyCDN WAF (OWASP CRS)
|
|
- CDN 에지에서 오리진 도달 전 차단
|
|
- 차단: SQLi, XSS, CMDi, SSRF, Shellshock, Log4j
|
|
- 비활성화: DATA LEAKAGES SQL (id=911) — NocoDB 오탐 방지
|
|
- 통과: Request Smuggling, NoSQLi, 경로 스캔
|
|
|
|
### 2차: SafeLine WAF (chaitin-waf)
|
|
- APISIX plugin_metadata → detector `10.43.253.244:8000`
|
|
- SafeLine v9.3.2, K3s safeline ns
|
|
- 관리: safeline.inouter.com, API 헤더 `X-SLCE-API-TOKEN`
|
|
- 토큰: Vault `secret/infra/safeline`
|
|
- tengine 미사용, APISIX 직접 연동
|
|
|
|
### 3차: CrowdSec 로그 분석
|
|
- Traefik 로그: Vector DaemonSet → `:8086` → `crowdsecurity/traefik-logs`
|
|
- APISIX 로그: http-logger → `:8085` → `custom/apisix-json-logs`
|
|
- HTTP 시나리오 매칭 → decision → bouncer 피드백
|
|
|
|
## waf-kr BunnyCDN Pull Zone
|
|
|
|
APISIX SafeLine WAF 전용.
|
|
|
|
| 항목 | 값 |
|
|
|------|-----|
|
|
| Zone ID | 5554681 |
|
|
| Name | waf-kr |
|
|
| Origin | https://220.120.65.245:9443 |
|
|
| 경로 | 인터넷 → BunnyCDN(waf-kr) → HAProxy(:9443) → APISIX(31137) → SafeLine → K3s |
|
|
|
|
등록 호스트: `juiceshop.keepanker.cv` (WAF 테스트용)
|
|
|
|
## 참고
|
|
|
|
- BunnyCDN WAF 차단 시 오리진에 로그 안 옴 → CrowdSec에 미수신
|
|
- OpenWrt CrowdSec firewall bouncer는 DNAT 구조라 리얼 IP 매칭 불가
|
|
- chaitin-waf 플러그인은 `plugin_attr`이 아닌 **`plugin_metadata`(etcd)**에서 detector 노드를 읽음
|
|
- Incus 컨테이너에서 `sed -i`로 설정 수정 시 파일 손상 주의 (전체 파일 push 사용)
|